docs: correct stale claims in CLAUDE.md #96
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish docs | |
| on: | |
| push: | |
| branches: [master] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: docs-publish | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| # Pinned so the cache key and the download URL stay in sync. See the | |
| # 'Fetch Doxygen' step for why the upstream binary is used. | |
| env: | |
| DOXYGEN_VERSION: 1.16.1 | |
| steps: | |
| - name: Checkout source | |
| uses: actions/checkout@v7 | |
| - name: Checkout gh-pages | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: gh-pages | |
| path: gh-pages-out | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y meson ninja-build groff | |
| # ubuntu-latest's apt doxygen lags upstream by several years and emits | |
| # a different HTML structure (no #doc-content wrapper, plain <h1>s), | |
| # which silently breaks the layout selectors in doxygen/doxygen-custom.css. | |
| # Pin to the official upstream binary so live rendering matches local. | |
| - name: Cache Doxygen | |
| id: doxygen-cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ~/.cache/doxygen | |
| key: doxygen-${{ env.DOXYGEN_VERSION }}-${{ runner.os }}-${{ runner.arch }} | |
| - name: Fetch Doxygen (cache miss) | |
| if: steps.doxygen-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p ~/.cache/doxygen | |
| # --retry covers transient doxygen.nl flakes (we hit a 403 once | |
| # despite the same URL being served fine seconds before). | |
| curl --retry 2 --retry-delay 5 --retry-all-errors -fsSL \ | |
| -o /tmp/doxygen.tar.gz \ | |
| "https://www.doxygen.nl/files/doxygen-${DOXYGEN_VERSION}.linux.bin.tar.gz" | |
| tar xf /tmp/doxygen.tar.gz -C ~/.cache/doxygen --strip-components=1 | |
| - name: Install Doxygen | |
| run: | | |
| sudo install -m 0755 ~/.cache/doxygen/bin/doxygen /usr/local/bin/ | |
| doxygen --version | |
| - name: Generate docs | |
| run: | | |
| meson setup builddir -Dgen-docs=true | |
| ninja -C builddir docs | |
| - name: Read project version | |
| id: ver | |
| run: | | |
| v=$(meson introspect builddir --projectinfo \ | |
| | python3 -c 'import json,sys; print(json.load(sys.stdin)["version"])') | |
| echo "version=$v" >> "$GITHUB_OUTPUT" | |
| - name: Refresh versioned directory on gh-pages | |
| env: | |
| VERSION: ${{ steps.ver.outputs.version }} | |
| run: | | |
| set -e | |
| # API/ABI is stable within a minor version, so docs publish to | |
| # vMAJOR.MINOR.x/ (e.g. v0.2.x), not vMAJOR.MINOR.PATCH/. | |
| MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2) | |
| dest="gh-pages-out/v${MAJOR_MINOR}.x" | |
| rm -rf "$dest" | |
| mkdir -p "$dest" | |
| cp -a builddir/html/. "$dest/" | |
| # Clean up obsolete patch-level dirs from before this convention | |
| # landed (e.g. v0.2.0, v0.2.1 superseded by v0.2.x). nullglob so | |
| # the loop exits 0 once those dirs are gone (otherwise an empty | |
| # match yields the literal pattern and the `[ -d ]` short-circuit | |
| # returns 1, failing the step under `bash -e`). | |
| shopt -s nullglob | |
| for old in gh-pages-out/v"${MAJOR_MINOR}".[0-9]*; do | |
| rm -rf "$old" | |
| done | |
| - name: Rebuild landing-page version list | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, re | |
| root = pathlib.Path("gh-pages-out") | |
| index = root / "index.html" | |
| if not index.exists(): | |
| print("no index.html on gh-pages; skipping landing-page update") | |
| raise SystemExit(0) | |
| # Accept either vN.N.N (legacy frozen historical dirs like v0.1.0) | |
| # or vN.N.x (current rolling-pointer per minor series). | |
| pattern = re.compile(r"v\d+\.\d+\.(?:\d+|x)") | |
| def sort_key(v): | |
| # "v0.2.x" sorts above "v0.2.5" because we want the rolling | |
| # pointer for a minor series ranked above any frozen patch. | |
| return tuple( | |
| 10**9 if p == "x" else int(p) | |
| for p in v[1:].split(".") | |
| ) | |
| versions = sorted( | |
| (p.name for p in root.iterdir() | |
| if p.is_dir() and pattern.fullmatch(p.name)), | |
| key=sort_key, | |
| reverse=True, | |
| ) | |
| items = "\n".join( | |
| f' <li><a href="{v}/index.html">{v}</a></li>' for v in versions | |
| ) | |
| html = index.read_text() | |
| new_html, n = re.subn( | |
| r"(<ul>)(.*?)(</ul>)", | |
| lambda m: f"{m.group(1)}\n{items}\n {m.group(3)}", | |
| html, count=1, flags=re.DOTALL, | |
| ) | |
| if n == 0: | |
| print("no <ul>...</ul> block found in index.html; leaving it alone") | |
| elif new_html != html: | |
| index.write_text(new_html) | |
| print(f"updated landing page with {len(versions)} versions") | |
| else: | |
| print("landing page already up to date") | |
| PY | |
| - name: Commit and push if changed | |
| env: | |
| VERSION: ${{ steps.ver.outputs.version }} | |
| working-directory: gh-pages-out | |
| run: | | |
| set -e | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| if [ -z "$(git status --porcelain)" ]; then | |
| echo "Docs unchanged — nothing to publish." | |
| exit 0 | |
| fi | |
| git add -A | |
| git commit -m "docs: publish v${VERSION} (from ${GITHUB_SHA::7})" | |
| git push origin gh-pages |