Skip to content

altdoc-multiversion-docs: redirect retired version directories to their replacements - #303

Merged
d-morrison merged 3 commits into
mainfrom
claude/multiversion-docs-main-url-5jwgnx
Jul 25, 2026
Merged

altdoc-multiversion-docs: redirect retired version directories to their replacements#303
d-morrison merged 3 commits into
mainfrom
claude/multiversion-docs-main-url-5jwgnx

Conversation

@d-morrison

@d-morrison d-morrison commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #301

The problem

altdoc-multiversion-docs.yml publishes the default branch's docs to /dev/.
The scheme it replaces --- insightsengineering/r-pkgdown-multiversion,
which UCD-SERG/serocalculator used before its altdoc migration --- published
to gh-pages/<branch-or-tag>/, so the same docs lived at /main/.

On serocalculator's gh-pages today, main/ holds two leftover font-awesome
CSS files and no index.html, so https://ucd-serg.github.io/serocalculator/main/
404s. Our own tracker has already rotted: UCD-SERG/serocalculator#575's body
links to .../serocalculator/main/reference/index.html.

Keeping /dev/ is the right call --- it is what tidyverse and r-lib sites use,
it agrees with the (dev) label generate_version_dropdown.py already emits,
and it survives a default-branch rename --- so the fix belongs on the redirect
side.

What this adds

A legacy-paths input on altdoc-multiversion-docs.yml, forwarded to
generate-altdoc-landing-page:

legacy-paths: main=dev

That generates a site-root 404.html rewriting any request under /main/ to
the same path under /dev/. Empty (the default) generates nothing, so existing
callers are unaffected.

Why a 404 page, not a main/index.html meta-refresh

  • Deep links. GitHub Pages serves no server-side redirects but does serve a
    site-root 404.html for unresolved paths, so one file covers
    /main/<anything>. A per-directory meta-refresh only catches the directory
    root --- and the links that prompted this are deep ones.
  • It survives deployment. The "Deploy root landing page" step runs with
    clean: true and a clean-exclude list that does not name retired version
    directories, so anything written outside site-root is deleted on the next
    default-branch push.

Why explicit old=new pairs

The landing target alternates between dev and latest-tag depending on which
event ran. Deriving the redirect destination from it would make /main/ resolve
somewhere different after a release build than after a default-branch build.
Explicit pairs keep it stable.

Invalid pairs --- self-redirects, duplicates, malformed entries --- fail the
step rather than publishing a dead or looping redirect.

Test coverage

Three levels, all in the altdoc-docs selftest job:

Level What it proves
tests/test_legacy_redirects.py (pytest, 30 cases) old=new parsing, each fail-fast validation, and the shared _site_output plumbing
A real uses: ./.github/actions/generate-altdoc-landing-page call with legacy-paths set the composite's wiring and github.action_path resolution, the same proof run-review-guard's e2e steps give
tests/run-redirect-js-tests.mjs (node, 10 cases) the generated page's actual redirect script, executed against a table of request paths

The node harness exists because the Python tests can only assert the mapping
reaches the page as text. Whether a given request then lands in the right place
is a separate question, and getting it wrong is silent --- a bad redirect still
renders a plausible not-found page. Its table covers deep links, query/fragment
preservation, and the paths that must not redirect: a genuinely missing page
under /dev/ would otherwise bounce forever, and a directory that merely starts
with a mapped name (/maintenance/) is not a match.

The existing no-legacy-paths assertion now also checks that no 404.html is
produced, so the default stays a true no-op.

Shared plumbing (review round 2)

Both generator scripts now import _site_output.py rather than repeating the
OUTPUT_DIR/DOCS_BASE_URL boilerplate --- the pattern this repo already uses
for extract-total-cost, sum-costs, and build-reviewer-args.

The part of that duplication that could actually bite was the site-root
default, which lived in three hand-maintained places (both scripts and
action.yml's own output-dir default) with nothing checking they agreed. It
is now one constant, and test_output_dir_default_matches_action_yml reads
action.yml and asserts they still match, instead of a comment asking the next
reader to keep them in sync.

Known limitations, documented on the reference page

  • Redirection needs JavaScript; without it the page renders as a plain
    not-found notice linking to the docs root.
  • The generated page replaces the site's 404 page for every unresolved path,
    not just the mapped prefixes.

Docs synced

README.md, website/workflows.qmd, website/reference/altdoc-multiversion-docs.qmd
(inputs table, a new "Retired version directories" section, and a commented
usage line in the example), examples/altdoc-multiversion-docs.yml, the
workflow's own header comment, and CLAUDE.md's Layout and Tests sections.

Rollout note

altdoc-multiversion-docs.yml calls the composite at @v2, so legacy-paths
only takes effect for consumers once the major tag advances past this merge ---
the same bootstrapping gap CLAUDE.md documents for other new capabilities.
The selftest uses the local ./ ref, so CI covers this PR's code.

UCD-SERG/serocalculator picks it up only after migrating off its bespoke
docs.yaml (UCD-SERG/serocalculator#600) and then uncommenting the input
(UCD-SERG/serocalculator#599).

dem-ucdh and others added 2 commits July 25, 2026 01:25
A multiversion docs site publishes each version under its own subdirectory,
so changing the naming scheme breaks every link written against the old one.
The concrete case: a site that published the default branch's docs to
/<branch>/ -- the insightsengineering/r-pkgdown-multiversion layout -- now
publishes them to /dev/, this workflow's layout and pkgdown's own
development-mode convention, leaving every /main/... link dead.

Keeping /dev/ is right: it is what tidyverse and r-lib sites use, it matches
the "(dev)" label the version dropdown already emits, and it survives a
default-branch rename. So the fix belongs on the redirect side.

The new `legacy-paths` input takes `old=new` pairs and generates a site-root
404.html that rewrites requests under old/ to the same path under new/.
GitHub Pages serves no server-side redirects but does serve a site-root
404.html for unresolved paths, which is what makes deep links work -- a
per-directory index.html meta-refresh only catches the directory root, and
the broken links that prompted this are deep ones. Writing it into the same
output directory as the root landing page also means it survives deployment:
the "Deploy root landing page" step runs with clean: true and a clean-exclude
list that does not name retired version directories.

The pairs are explicit rather than derived from whichever version the build
deployed, so /main/ resolves to the same place after a release build as after
a default-branch build. Invalid pairs -- self-redirects, duplicates,
malformed entries -- fail the step rather than publishing a dead or looping
redirect.

Test coverage runs at three levels in the altdoc-docs selftest job: pytest
over the parsing and validation, a real `uses:` call to the composite, and a
node harness that executes the generated page's own redirect script against a
table of request paths. That last one exists because the Python tests can
only assert the mapping reaches the page as text; whether a request then
lands in the right place is a separate question, and getting it wrong is
silent. Its table includes the paths that must not redirect, since a
genuinely missing page under /dev/ would otherwise bounce forever.

Closes #301

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfkoVvS98hgAeW45cBkPm5
Copilot AI review requested due to automatic review settings July 25, 2026 01:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread .github/actions/generate-altdoc-landing-page/generate_legacy_redirects.py Outdated
@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Code review

Reviewed the diff, cross-checked prose claims (GitHub Pages 404.html behavior, pkgdown dev-mode /dev/ convention, and insightsengineering/r-pkgdown-multiversion's per-branch layout -- all confirmed against their respective docs/source), traced the Python parsing/validation logic and the embedded JS redirect script against every selftest case plus edge cases (trailing slashes, deep links, prefix-collisions like /maintenance/ vs. main, query/fragment preservation, outside-base-path), and verified the three required doc-sync sites (README.md, website/workflows.qmd, website/reference/altdoc-multiversion-docs.qmd) plus the changelog fragment naming.

One finding, posted inline: generate_legacy_redirects.py's main() duplicates generate_multiversion_landing_page.py's OUTPUT_DIR/mkdir/DOCS_BASE_URL-resolution boilerplate (including an identical comment) almost verbatim -- a DRY hit under CLAUDE.md's Code review guidelines section 2, consistent with this repo's own precedent of extracting comparably small duplicated snippets (extract-total-cost, sum-costs, build-reviewer-args) into shared helpers.

No bugs found in the new redirect logic, the composite/workflow YAML wiring, or the two new test suites (pytest + the node harness that executes the real generated script). No AI-authorship prose tells, forward references, or fact-check issues found in the new prose beyond the one DRY finding above.

Verdict

Needs minor work -- the DRY duplication is a small, non-blocking cleanup; everything else (correctness, tests, doc sync, changelog) checks out.

@github-actions

Copy link
Copy Markdown
Contributor

Claude finished reviewView run


The top-level summary comment posted successfully: #303 (comment)

Review summary for #303

I reviewed the "redirect retired version directories" PR (adds legacy-paths input to altdoc-multiversion-docs.yml) with four independent agents (two CLAUDE.md compliance, two bug/security scans), then validated every candidate finding before reporting.

Confirmed and posted inline (#303 (comment)):

  • generate_legacy_redirects.py's main() duplicates generate_multiversion_landing_page.py's OUTPUT_DIR/mkdir/DOCS_BASE_URL boilerplate (including an identical comment) nearly verbatim — a DRY violation under CLAUDE.md §2, consistent with this repo's own precedent of extracting even smaller duplicated snippets into shared helpers.

Investigated but not flagged (false positives / non-issues, excluded from the final report):

  • Bold-leading-bullet pair in the new docs section — matches this repo's established, pre-existing documentation convention, not an AI tell.
  • A paragraph line-split in CLAUDE.md's Tests section — cosmetic source-only wrap, renders identically, excluded per the review rules.
  • Mutual-cycle redirect loops, js_literal escaping sufficiency, shell injection in the new selftest steps — all checked and found solid.

No bugs found in the redirect logic, YAML wiring, or the two new test suites (pytest + node harness executing the real generated script). Fact-checked GitHub Pages' 404.html behavior, pkgdown's /dev/ convention, and insightsengineering/r-pkgdown-multiversion's layout — all accurate.

Verdict

Needs minor work — one small, non-blocking DRY cleanup; everything else (correctness, tests, doc sync, changelog) checks out.

@github-actions

Copy link
Copy Markdown
Contributor

💰 Cost: $5.7076 (review) — run

…enerators

generate_legacy_redirects.py's main() repeated
generate_multiversion_landing_page.py's OUTPUT_DIR/mkdir/DOCS_BASE_URL
boilerplate, comment included. Both scripts live in the same composite action
and run back to back, so the shared bits now live in _site_output.py and both
import them -- the pattern this repo already uses for extract-total-cost,
sum-costs, and build-reviewer-args, at a smaller scale.

The part of that duplication that could actually bite was the "site-root"
default, which appeared in both scripts AND in action.yml's own output-dir
default -- three hand-maintained copies that had to agree. It is now one
constant, and a test asserts it still matches action.yml rather than a comment
asking the next reader to keep them in sync.

The pytest module loads the script by file path, which (unlike the action's
own `python3 <dir>/<script>.py` invocation) does not put the action directory
on sys.path, so the test module inserts it explicitly before loading.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfkoVvS98hgAeW45cBkPm5
Copilot AI review requested due to automatic review settings July 25, 2026 02:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.

2026-07-25 03:45 UTC

@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Code review

Re-reviewed after the latest commit (2592935, "Address review: share the output-dir/base-url plumbing between both generators").

Prior finding resolved. The previous review flagged that generate_legacy_redirects.py's main() duplicated generate_multiversion_landing_page.py's OUTPUT_DIR/mkdir/DOCS_BASE_URL boilerplate almost verbatim. This commit extracts that plumbing into a new shared module, _site_output.py, which both generator scripts now import — the same pattern this repo already uses for extract-total-cost, sum-costs, and build-reviewer-args. It also collapses what had been three independent hand-maintained copies of the site-root default (both scripts plus action.yml's own output-dir default) down to one constant, with a new test (test_output_dir_default_matches_action_yml) asserting the two stay in sync instead of relying on a comment. I traced the refactor end to end — both generators still resolve OUTPUT_DIR/DOCS_BASE_URL correctly, the sys.path handling the added pytest module needs to import _site_output when loading the script by file path is correct, and no existing behavior changed.

I re-verified the rest of the diff as well (redirect JS logic in generate_legacy_redirects.py's render(), the action.yml/_selftest.yml/altdoc-multiversion-docs.yml wiring, and the README/website/CLAUDE.md/changelog doc sync) — all still check out, consistent with the prior review's findings on correctness, tests, and doc sync.

One very minor, non-blocking note: the PR description's test-coverage table still says test_legacy_redirects.py has "25 cases," but this commit's new TestSiteOutput class adds 5 more (now 30). Not worth a fix on its own.

Verdict

Ready for merge — the outstanding DRY finding from the prior review is resolved, and no new issues found.

@github-actions

Copy link
Copy Markdown
Contributor

Claude finished reviewView run


Posted the confirming review comment: #303 (comment)

Summary: The latest commit (2592935) resolved the prior review's only finding — the DRY duplication between generate_legacy_redirects.py and generate_multiversion_landing_page.py — by extracting the shared OUTPUT_DIR/DOCS_BASE_URL plumbing into a new _site_output.py module, plus a test asserting the site-root default stays in sync with action.yml. I re-verified the redirect JS logic, workflow/action YAML wiring, and doc sync across README/website/CLAUDE.md/changelog — all correct, no new issues found.

Verdict

Ready for merge

@github-actions

Copy link
Copy Markdown
Contributor

💰 Cost: $1.6821 (review) — run

@d-morrison
d-morrison merged commit df7da09 into main Jul 25, 2026
29 checks passed
@d-morrison
d-morrison deleted the claude/multiversion-docs-main-url-5jwgnx branch July 25, 2026 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

altdoc-multiversion-docs: redirect legacy /&lt;branch&gt;/ docs URLs to /dev/

3 participants