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
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
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"
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():
ap = argparse.ArgumentParser()
ap.add_argument("--man-dir", default="man")
@@ -130,6 +178,7 @@ def main():
out = os.path.join(args.out_dir, page + ".md")
open(out, "w").write(build_page(args.man_dir, sources, page, extras_dir))
print(f" wrote {page}.md ({', '.join(sources)})")
sync_sidebar(args.out_dir, mapping)
return 0