Stamp the README counts from the document, because typed ones go stale

Seven SDK READMEs opened by telling a developer the client covers "2,479
operations over 1,814 paths, grouped into 192 services". The document serves
2,259 over 1,620 in 117 — every number wrong, in the first paragraph of the page
each language's users land on. Nobody mistyped them; they were true once and the
document moved, which is what a hand-written count does.

So the numbers are no longer written. A `<!--counts-->` span in the README is
rewritten from the spec the client was actually generated from, on every
generate — the sentence around it stays the repo's own voice, and a README with
no markers is left untouched, so adopting it is per-repo rather than a surprise
rewrite.

Measured by PARSING the document, not by matching it. The first version counted
`^      operationId:` and `^  /`, which is true of cloud's YAML and of nothing
else — this driver hands the generator a JSON copy, so every count came out zero
and stamped "0 operations over 0 paths" into the README it was added to fix. It
also disagreed with itself about services: the regex said 148, the parse says
117, and only one of those is the number of distinct tags on an operation.
This commit is contained in:
2026-08-23 10:39:53 -07:00
parent 3cd4ba7648
commit ab911f5a38
+55
View File
@@ -66,6 +66,7 @@ import concurrent.futures as futures
import hashlib
import json
import os
import re
import shutil
import subprocess
import sys
@@ -449,11 +450,65 @@ def sdk(name, cfg, spec, version, drops, repo, check):
if not check:
with open(os.path.join(repo, MANIFEST), "w") as f:
f.write("".join(f"{p}\n" for p in sorted(now)))
stamp(repo, spec)
if check and ok:
print(f"[{name}] clean")
return ok
METHODS = {"get", "put", "post", "delete", "patch", "head", "options", "trace"}
def scale(spec):
"""(operations, paths, services) of the document a client was cut from.
PARSED, not pattern-matched. The first version counted `^ operationId:`
and `^ /`, which is true of cloud's YAML and of nothing else — this driver
hands the generator a JSON copy, so every count came out zero and stamped
"0 operations over 0 paths" into the README it was added to fix.
"""
doc = as_json(spec) if str(spec).endswith((".yaml", ".yml")) else spec
with open(doc, encoding="utf-8") as f:
d = json.load(f)
paths = d.get("paths") or {}
ops = [op for item in paths.values() for m, op in (item or {}).items()
if m in METHODS and isinstance(op, dict)]
tags = {t for op in ops for t in (op.get("tags") or [])}
return len(ops), len(paths), len(tags)
# The counts a README quotes, stamped rather than typed.
#
# Seven READMEs said "2479 operations over 1814 paths, grouped into 192
# services". The document served 2259 over 1620 in 148 — every number wrong, on
# the first paragraph of the page a developer lands on. Nobody mistyped them:
# they were true once and the document moved, which is what a hand-written count
# does. cloud's own notes call this out ("a count quoted in prose is stale the
# next week") and the fix there is the same as here — say how to re-measure it,
# or measure it for them.
#
# The span is delimited so the sentence around it stays the client's own voice;
# only the numbers are ours. A README with no markers is left alone, so adopting
# this is per-repo and never a surprise rewrite.
COUNTS = re.compile(r"(<!--counts-->)(.*?)(<!--/counts-->)", re.S)
def stamp(repo, spec):
"""Rewrite the marked counts in README.md from the spec actually used."""
path = os.path.join(repo, "README.md")
if not os.path.isfile(path):
return
text = open(path, encoding="utf-8").read()
if not COUNTS.search(text):
return
ops, paths, svcs = scale(spec)
said = f"{ops:,} operations over {paths:,} paths, grouped into {svcs} services"
out = COUNTS.sub(lambda m: m.group(1) + said + m.group(3), text)
if out != text:
open(path, "w", encoding="utf-8").write(out)
print(f"[readme] {os.path.basename(repo)}: {said}")
def sweep(repo, removed):
"""Drop the directories a removal emptied, never one that holds anything."""
for p in sorted({os.path.dirname(p) for p in removed}, key=len, reverse=True):