ci: build, review and deploy the documentation site - #84
Conversation
d217339 to
0b3d7b0
Compare
c0bc0a8 to
8bb7c98
Compare
8bb7c98 to
d1745c0
Compare
a5f8611 to
e93bcfa
Compare
878a21b to
d9aebe5
Compare
2fecdad to
be35906
Compare
soimy
left a comment
There was a problem hiding this comment.
Stack #86 review of current head 70bba8e05ef2049140d4e9786f6e047e8d262c2d.
The Documentation workflow itself is green on this PR: the Node 24 build, Pages configuration, and artifact upload all succeed, while the deploy job is correctly skipped for pull requests. I found one wording/expectation issue below; the workflow implementation otherwise looks sound.
09c993b to
84ee0ef
Compare
|
Auditing the other contributor pages against the repository turned up one stale description this PR should own: Everything else in the audit checked out against the source: the backend trigger lists, the |
|
The legacy-URL section said only private-member anchors are lost. That was never measured against the real thing, so I crawled the published 2.7.4 site (
The section now states that split instead of the private-members-only version (commit: |
431c602 to
3e5a3df
Compare
soimy
left a comment
There was a problem hiding this comment.
Re-review of current head 17ddbf7cfb996b0a29043bdfe133cd37dedc4513.
The previous PR-preview wording issue remains correctly fixed, and both the Node.js CI matrix and Documentation workflow are green.
I found one remaining documentation-consistency issue in the new legacy-link audit:
525db81 to
ab76278
Compare
75b2771 to
83830c9
Compare
Phase D of #81: the site gets a workflow of its own, and the URLs the old TypeDoc site published keep working. - `.github/workflows/docs.yml` builds on every change that can affect the site (docs, src, the manifests, this workflow), runs the type check before the build, uploads `docs/.vitepress/dist` as an artifact so a pull request can be reviewed at a URL, and deploys that same artifact to GitHub Pages from `master`. Deploying needs the repository's Pages source switched to "GitHub Actions"; until that happens `doc:publish` still pushes to `gh-pages`, and the documentation page says the two must not run together. - The build writes a redirect page for every URL the old site published — `classes/*.html`, `interfaces/*.html`, `enums/PACKING_LOGIC.html`, `modules.html`, `hierarchy.html` — and fails when a target is missing from the build instead of shipping a redirect into a 404. Legacy anchors that used underscores are rewritten in the browser (`#max_area` -> `#max-area`); the ones that belonged to private members are documented as gone. - `docs/site.json` holds the base path, read by both the VitePress config and the redirect builder, so the published prefix cannot drift between them. Verified locally by serving the built tree: the home page, the API index, the user and contributor routes, releases and all five legacy class/interface/enum paths return 200, and the assets are referenced through the base prefix.
The Pages deployment template keeps `cancel-in-progress: false` for a reason: a run cancelled halfway through a deployment leaves the published site in a state nobody chose. The group is per ref, so a pull-request build still queues behind another build of the same branch and production deploys serialise.
`srcExclude` keeps `docs/spec/` and `docs/plans/` out of the page tree, and the config comment said so — a comment is not a check. The build now ends with `scripts/verify-docs-output.mjs`, which asserts both directions: - no `dist/spec/**` or `dist/plans/**` page tree exists, and since VitePress only indexes the pages it builds, that is also what keeps the records out of search; - every handwritten page (home, user, contributor, releases) has a built page, so the same option that hides the records cannot silently swallow a guide; - a local search index was built and carries site text, or "excluded from search" would be vacuously true. Measured both ways: dropping `srcExclude` makes it fail naming `dist/spec` and `dist/plans`; widening it to `user/**` fails earlier, in VitePress's own dead-link check, which the positive control then covers for the linkless case. The first version scanned every built page for the records' file names and failed on `docs/contributor/documentation.html`, which legitimately links to them — the page tree is the signal, not the names.
VitePress fails the build on a dead page link but not on a missing anchor, so a renamed section rots silently — and the anchors TypeDoc writes into its own cross-references are the bulk of them. The output check now walks the published markdown (user, contributor, releases, home and the generated API pages), resolves each relative or root-absolute link against the built HTML, and fails when the target page or the `#anchor` in it does not exist. Measured: 188 internal links resolve on the current tree; renaming one anchor in `docs/user/troubleshooting.md` fails the build naming the file, the link and the missing id.
…it is Two follow-ups to the previous commits on this branch: - `oxlint` rejects a `$`-anchored regex where `String#endsWith` says the same thing (`unicorn/prefer-string-starts-ends-with`), which made `npm run lint` exit 1 while every other gate passed. The asset guard now uses `endsWith` for all three cases, and the no-op `.html$` replace next to it is gone. - `docs/contributor/documentation.md` called the workflow "one job" and listed its triggers incompletely: there are two jobs (`build` and `deploy`), and the `paths` filters also cover `scripts/**` — which really is part of the build — plus the root markdown files.
The output check verified that every handwritten page is built, which is only half of reachable: VitePress fails on a link that points nowhere, never on a page that nothing links to, so a new page can be built, indexed and still invisible from the navigation. The config's `link:` entries are now checked both ways — each one has to resolve to a built page, and every published page has to be reached by one. Measured: 18 links, all resolving, no orphans; deleting the `Repacking` sidebar entry fails the build naming `user/repacking.html`.
The other two checks print what they verified, so the nav one does too — a gate whose only output is silence on success is easy to believe is not running.
VitePress rewrites the links it generates and the ones written in markdown, but a raw-html href is left exactly as written — and such a URL works while serving locally at the root and 404s on the project page. The output check now reads the base from `docs/site.json` and fails on any absolute `href`/`src` in the built html, js and css that does not start with it. Measured: 638 absolute URLs, all under `/maxrects-packer/` today, and the legacy redirect pages carry the base in both their canonical link and their refresh target. Appending `<a href="/user/options">` to a guide page fails the build, naming the page and the asset chunk that carries it.
The description had been written one PR ahead of the script it talks about — the same statement-ahead-of-its-branch shape the review flagged on AGENTS.md. It now sits on the branch that introduces `scripts/verify-docs-output.mjs` and covers what the check actually does today rather than the three assertions it started with: page tree, per-page reachability through the nav, links and anchors, the search index, and the base prefix on every absolute URL — each measured against a broken state before it was trusted.
`typedoc.json` names `tsconfig: "tsconfig.json"` explicitly, so a compiler option there can change the generated API pages — but neither filter list mentioned it, so a PR that touched only the tsconfig would leave the deployed site describing the previous options until something else triggered a rebuild. Both lists now carry it (12 filters each, verified by parsing the workflow), and the trigger sentence in `docs/contributor/documentation.md` spells the inputs out instead of saying "the TypeDoc and package manifests".
`upload-pages-artifact` uploads a Pages artifact; it does not publish one. `deploy-pages` is gated on a push to `master`, so a pull request gets a downloadable artifact and no URL at all. The deployment section and the workflow header now say what each job does.
`documentation.md` lists all four steps; the quick reference in `development.md` still stopped after the site build, which is where the redirects and the output check were added.
The page said only private-member anchors are lost. Crawling the published 2.7.4 site gives the real split: 105 of the 170 member anchors on the 11 redirected pages resolve, and the other 65 are private members (22), the old theme's own signature anchors (28) or sections of the two legacy index pages (15), which have no per-entry anchor on `api/index.html`.
The build writes the legacy-URL redirects and runs the output check from here on, so the commands table names all four steps instead of the two the content layer has.
The spike report's Phase A counts contradicted the numbers in this section. It now marks them superseded and repeats the re-measurement, so the sentence that sends readers there says so.
The check verified that absolute URLs sit under the base, never that they resolve, so a page still pointing at a deleted file stayed green — which is the shape of the theme retirement, where the CSS and JS a page referenced stopped existing. Every root-absolute `href`/`src` is now resolved inside the build (file, `.html`, or a directory's `index.html`). Measured against a broken state: with `vp-icons.css` removed the check exits 1 and names 29 references; restored it reports 638 absolute URLs under the base that resolve.
83830c9 to
6500382
Compare
|
Rebased on the current #83 head and out of draft. Gates on this head ( Merge order is #83 → #84 → #85: this layer's base is |
|
Phase D of #81, stacked on the content migration. Review order: #82 → content migration → this. CI runs here once the stack is on
master; the branch is rebased at each step.The workflow
.github/workflows/docs.ymlbuilds on every change that can affect the site (docs/**,src/**, the manifests, the workflow itself), runsnpm run typecheckbefore generating the API reference, uploadsdocs/.vitepress/distas a Pages artifact, and deploys that same artifact to GitHub Pages frommaster— one build job, Node 24,npm ci --include=dev. Thedeployjob is gated on a push tomaster, so a pull request stops after the upload: it gets a downloadable build attached to the run, not a published site and not a preview URL.One maintainer step is required before the first deployment: switch Settings → Pages → Build and deployment to GitHub Actions. Until that happens
npm run doc:publishstill pushes the built site to thegh-pagesbranch the old way, anddocs/contributor/documentation.mdsays the two paths must not be used together.Legacy URLs keep working
The old site served TypeDoc's HTML from the Pages root, so
classes/*.html,interfaces/*.html,enums/PACKING_LOGIC.html,modules.htmlandhierarchy.htmlwere published addresses. The build now writes a redirect page at each of them, pointing at the page that replaced it, and fails when a target is missing from the build rather than shipping a redirect into a 404 (measured: pointing theBinredirect at a page that does not exist fails the build and names it).Legacy anchors that used underscores are rewritten in the browser (
#max_area→#max-area). Anchors that belonged to private members cannot be preserved —excludePrivate: trueis what keeps implementation members out of the site — and are documented as landing on the page itself.docs/site.jsonholds the base path for both the VitePress config and the redirect builder, so the published prefix cannot drift between them.Verification
Served the built tree locally and fetched every route: the home page,
/api/, the user and contributor sections, releases, and all five legacy class/interface/enum paths return 200, with assets referenced through/maxrects-packer/.lint,format:check,typecheck,verify:docs,docs:build,cover(100%) andverify:packageall green.The build ends with an output check
srcExcludeis a config option, and a config option is not a check:scripts/verify-docs-output.mjsnow inspects what was actually built and asserts both directions — no page tree forspec/orplans/(which is also what keeps them out of the search index, since VitePress only indexes pages it built), a built page for every handwritten page, and a search index that carries site text rather than nothing.Measured both ways: dropping
srcExcludefails namingdist/specanddist/plans; widening it touser/**fails earlier, in VitePress's own dead-link check. The check's first version scanned built pages for the records' file names and failed ondocs/contributor/documentation.html, which legitimately links to them — the page tree is the signal, not the names.The workflow's action inputs were checked against the actions' own
action.yml(upload-pages-artifacttakespath,setup-nodetakesnode-version/cache,deploy-pagespublishespage_url), since none of this can be exercised locally.Two more checks the build now ends with
build: resolve every internal link, anchors included): VitePress fails on a dead page link but says nothing about a missing#anchor, and TypeDoc's own cross-references are full of them. Every relative and root-absolute link in the published markdown is resolved against the built HTML — 188 today — and a renamed section fails the build naming the file, the link and the missing id.build: fail when a page is built but nothing links to it+build: report the nav coverage when it passes): the check verified each page was built, which is only half of reachable. The config'slink:entries are now checked both ways, so a new page that nothing links to fails instead of shipping invisibly outside search. Deleting theRepackingsidebar entry reproduces it.Clean-checkout verification
Every gate above was also run from a fresh
git cloneof the chain head into an empty directory — nonode_modules, nodist/, nodocs/api/, no ignored files — because the worktree the checks were written in is full of build products:npm ci --include=dev→lint·format:check·typecheck·verify:docs·docs:build·test(126 passed, 2 skipped) ·cover(100% on all four metrics) ·verify:package, all exit 0;npm pack --dry-run= 28 files / 66,871 B.The built tree was then served at the real base path,
/maxrects-packer/, and all 16 routes returned 200: home, both guide sections, releases, the API root and two generated pages, the five legacy paths (classes/,interfaces/,enums/,modules.html,hierarchy.html), a referenced asset, and404.html. Theclasses/MaxRectsPacker.htmlredirect carriesurl=/maxrects-packer/api/classes/MaxRectsPacker.html, so the base survives the redirect.Hashes of this stack's commits are deliberately not quoted: they move on every rebase, so commits are named by subject instead.
Trigger list audited against what the build reads
typedoc.jsonnamestsconfig: "tsconfig.json", so a compiler option there can change the generated API pages — and neitherpathslist mentioned it. A PR touching only the tsconfig would therefore have left the deployed site describing the previous options until something else triggered a rebuild. Both lists carry it now (12 filters each, checked by parsing the workflow), and the trigger sentence indocs/contributor/documentation.mdnames the inputs instead of saying "the TypeDoc and package manifests". The rest of the build's inputs were checked the same way and are covered:docs/**,src/**,scripts/**, the two package manifests, the root markdown files and the workflow itself; the site references nothing underassets/, so that directory is deliberately absent.Review round
The deployment sentence promised a preview:
upload-pages-artifactuploads a Pages artifact, it does not publish one, anddeploy-pagesruns only onmaster. A pull request therefore gets a downloadable artifact and no URL at all.docs/contributor/documentation.mdand the workflow's header comment now say exactly that, and this description was corrected with them.Consistency pass on the audit numbers
The legacy-anchor section and the spike report it links to disagreed — 105/170 here against a Phase A column adding up to 129/170 there. Nothing reproduces 129 from the published site (exact
id102, after the underscore rewrite 105, on any new page 106, substring of a new id 149, unique names 40 of 98), so the report marks its counts superseded, states the re-measured 105/170 with the three buckets, and this page's pointer says so.