ci(wiki): auto-insert new module pages into the sidebar

generate.py now keeps _Sidebar.md in sync: any Module:-* page from the
mapping that is not yet linked is inserted alphabetically into the
Modules list. Existing entries (custom labels, nested sub-entries,
hand-written non-module links) are left untouched, so adding a module no
longer requires editing the sidebar by hand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex
2026-07-04 03:07:53 +02:00
co-authored by Claude Opus 4.8
parent c76bfc018b
commit 4e76d7339f
2 changed files with 55 additions and 0 deletions
+6
View File
@@ -18,6 +18,12 @@ touches `man/**` or this tooling.
- `extras/<Page>.md` — optional hand-maintained appendix (screenshots, showcase - `extras/<Page>.md` — optional hand-maintained appendix (screenshots, showcase
snippets that have no man equivalent), appended verbatim after the generated body. snippets that have no man equivalent), appended verbatim after the generated body.
The generator also keeps `_Sidebar.md` in sync: any `Module:-*` page in the mapping
that is not yet linked in the sidebar is inserted alphabetically into the `Modules:`
list. Existing sidebar entries (custom labels, nested sub-entries, hand-written
non-module links) are never modified — so a new module auto-appears in navigation
without disturbing the curated structure.
## What it touches ## What it touches
Only the pages listed in `mapping.json` are (re)written. Every other wiki page Only the pages listed in `mapping.json` are (re)written. Every other wiki page
+49
View File
@@ -90,6 +90,54 @@ def build_page(man_dir, sources, page, extras_dir):
return "\n".join(parts).rstrip() + "\n" return "\n".join(parts).rstrip() + "\n"
ENTRY_RE = re.compile(r"^ - \[([^\]]+)\]\(\./Module:-")
LINK_RE = re.compile(r"\]\(\./(Module:-[^)]+)\)")
def sync_sidebar(out_dir, mapping):
"""Non-destructively add any mapped Module page missing from _Sidebar.md.
Only inserts links for pages absent from the sidebar; existing entries
(custom labels, nested sub-entries, hand-written non-module links) are
never modified. Insertion is alphabetical within the top-level module list.
"""
path = os.path.join(out_dir, "_Sidebar.md")
if not os.path.exists(path):
print(" no _Sidebar.md; skipping sidebar sync")
return
lines = open(path).read().splitlines()
linked = set(LINK_RE.findall("\n".join(lines)))
missing = sorted((p for p in mapping
if p.startswith("Module:-") and p not in linked),
key=str.lower)
if not missing:
print(" sidebar up to date")
return
def entries():
return [i for i, l in enumerate(lines) if ENTRY_RE.match(l)]
if not entries():
print(" could not locate Modules section; skipping sidebar sync")
return
for page in missing:
label = page[len("Module:-"):].replace("-", " ")
new_line = f" - [{label}](./{page})"
pos = None
for i in entries():
if ENTRY_RE.match(lines[i]).group(1).lower() > label.lower():
pos = i
break
if pos is None: # after the last module entry and its sub-entries
j = entries()[-1] + 1
while j < len(lines) and lines[j].startswith(" "):
j += 1
pos = j
lines.insert(pos, new_line)
print(f" sidebar += {label}")
open(path, "w").write("\n".join(lines) + "\n")
def main(): def main():
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
ap.add_argument("--man-dir", default="man") ap.add_argument("--man-dir", default="man")
@@ -130,6 +178,7 @@ def main():
out = os.path.join(args.out_dir, page + ".md") out = os.path.join(args.out_dir, page + ".md")
open(out, "w").write(build_page(args.man_dir, sources, page, extras_dir)) open(out, "w").write(build_page(args.man_dir, sources, page, extras_dir))
print(f" wrote {page}.md ({', '.join(sources)})") print(f" wrote {page}.md ({', '.join(sources)})")
sync_sidebar(args.out_dir, mapping)
return 0 return 0