Skip to content

feat: publish versioned docs at /docs/vX.Y.Z and /docs/next - #1216

Open
claude[bot] wants to merge 7 commits into
mainfrom
versioned-docs
Open

claude[bot] wants to merge 7 commits into
mainfrom
versioned-docs

Conversation

@claude

@claude claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Requested by Samuel Attard · Slack thread

Before: electronjs.org only serves the docs of the latest stable release at /docs/latest; older releases, prereleases and main have no rendered docs.

After: Every stable release since v38 and every prerelease of the unreleased major gets a self-contained snapshot at /docs/vX.Y.Z/, /docs/dev/ serves electron/electron@main, and /docs/next is a Worker alias to the newest prerelease; /docs/latest is unchanged.

  • yarn pre-build --version vX.Y.Z[-alpha.N]|dev downloads that tag (or main) into docs/<version>/; next and nightlies are rejected.
  • ELECTRON_DOCS_VERSION=<version> yarn build:version produces a self-contained Docusaurus build under /docs/<version>/ (English only, noindex, canonical to /docs/latest, own assets).
  • Versioned pages show a banner linking back to latest; every docs page gets a navbar version dropdown driven by /docs/versions.json.
  • publish-versioned-docs.yml: workflow_dispatch, repository_dispatch, and a daily dev rebuild plus gap-fill of v38+ stable releases and the unreleased major's prereleases (10 per run), uploading via OIDC to the docs storage account.
  • /docs/versions.json contract: { latest, next, versions, prereleases, dev: { sha, updated } }.
  • sidebars.js now comes from a shared template; its output is unchanged.

Tested: yarn lint clean, yarn test 48/48, and offline v44.3.0, v45.0.0-alpha.6 and dev builds checked for banner, canonical, noindex, sidebar links and asset paths.

Depends on electron/infra#354 applying first (Worker routing for /docs/v*, /docs/dev*, /docs/next*, /docs/versions.json); the optional review nits are queued for the next push after maintainer review.

Checklist

🤖 Generated with Claude Code

https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG


Generated by Claude Code

… or main

`yarn pre-build` keeps its behaviour for `docs/latest`; `yarn pre-build:version
vX.Y.Z` (or `next`) downloads the docs of that tag (or of `main`) into
`docs/vX.Y.Z` (or `docs/next`) and runs the same fixers. The start-page
slug, fiddle path rewrite and the tarball folder prefix (GitHub drops the
leading `v` of tag names) are no longer hardcoded to `latest`. The `.sha`
of versioned folders stores the commit the tag/branch resolves to.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
The GitHub source links and "Open in Fiddle" buttons now derive the
Electron ref from the docs folder a file lives in (`docs/<version>/`)
instead of always using the latest stable, and the API history table links
to the breaking changes of the version being viewed.

Adds a banner above every doc page of the per-version builds pointing back
to `/docs/latest`, a canonical link override for those builds, and a navbar
dropdown (in every build) listing the published docs versions from
`/docs/versions.json`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
`docusaurus.versioned.config.ts` derives from the main config to build one
docs version (`ELECTRON_DOCS_VERSION=vX.Y.Z|next yarn build:version`) as a
self-contained site under `/docs/<version>/`: English only, no blog/pages
or network-fetching plugins, `noindex`, broken links downgraded to
warnings, and website links turned into absolute links.

`sidebars.js` is now generated from `sidebars-template.js` (byte-identical
output), which the versioned build reuses while dropping entries whose
pages do not exist in that version.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
Builds and uploads `/docs/vX.Y.Z/` snapshots and `/docs/next/` to the docs
storage account via OIDC, on demand (`workflow_dispatch`,
`repository_dispatch`) or daily to backfill stable releases since v38.0.0
(10 per run), then regenerates `/docs/versions.json` from the container's
actual contents.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
Adds `yarn test` (run on pull requests) with specs for the sidebar template
and missing-doc filter, the docs version path helpers, the version dropdown
list, the gap-fill selection and the versions.json builder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 11, 2026

Copy link
Copy Markdown

Deploying electron-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1fd863e
Status: ✅  Deploy successful!
Preview URL: https://c5df5974.electron-website.pages.dev
Branch Preview URL: https://versioned-docs.electron-website.pages.dev

View logs

@claude
claude Bot marked this pull request as ready for review September 11, 2026 22:10
@claude
claude Bot requested a review from a team as a code owner September 11, 2026 22:10

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline findings already reported, I checked whether DocsVersionDropdown's use of useLocation().pathname breaks on versioned builds where baseUrl is /docs/vX.Y.Z/ (i.e., whether React Router strips the basename before parseDocsVersionPath sees it) — Docusaurus bakes baseUrl into each route's path rather than passing it as a router basename, so pathname still includes the version segment and the parse succeeds; this is not an additional bug beyond what's already flagged inline.

Extended reasoning...

The bug hunt already produced multiple confirmed findings (non-atomic azcopy copy for republished versions, silent-drop of next from versions.json on transient blob-download failures, unbounded trust of fetchPublishedVersions on network errors in the gap-fill script, non-deterministic shared 404.html across concurrent matrix jobs, and an unwired reject in downloadFromGitHub) which will be posted as inline comments, and the hunt exited on max_bugs rather than running dry, so per policy this should never be approved. I independently examined one additional candidate beyond the reported list — whether DocsVersionDropdown's reliance on useLocation().pathname would silently fail to render the dropdown on versioned-doc pages because React Router might strip the site's baseUrl (which is /docs/vX.Y.Z/ for versioned builds) as a router basename before parseDocsVersionPath runs. Docusaurus routes are registered with baseUrl already prefixed into the route path (rather than being applied via a React Router basename), so pathname from useLocation() retains the version segment and parseDocsVersionPath resolves as expected; this concern does not hold up as a separate bug.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟣 scripts/tasks/download-docs.ts — downloadFromGitHub's new Promise((resolve) => ...) never wires up reject, so a non-OK tarball fetch (throw new Error(...) at line 79) becomes an unhandled promise rejection instead of a caught error; the pre-build step crashes with an opaque uncaught-exception instead of a clean 'invalid version' message. Fix: reject(error) in a .catch on the fetch chain so callers get a normal rejected promise. This is a pre-existing gap, but the diff makes it fire far more often: it's now exercised for every scheduled gap-fill run (up to 10 historical tags plus next nightly) and every manual/repository_dispatch version, versus only latest before.

    Extended reasoning...

    In downloadFromGitHub, the executor only destructures resolve; when response.ok is false it throws inside .then(), which rejects the inner promise but nothing calls the outer reject, so download()'s returned promise never settles (or, depending on Node's unhandledRejection mode, the process aborts with a generic stack trace). Previously this path was only reachable when building docs/latest (rare, well-tested target). Now publish-versioned-docs.yml's scheduled job calls pre-build.ts --version for up to 10 back-filled release tags plus next every night, and any bad/renamed/rate-limited tag (electron/electron history has some renamed or yanked tags) triggers this path routinely, hanging or crashing that matrix job with no actionable error message.

    Verification: pre-existing. The defect is real: in downloadFromGitHub the executor at line 76 (return new Promise((resolve) => {) never captures reject, and the throw new Error(...) at lines 79-81 fires inside a fetch(...).then(...) chain with no .catch. On a non-OK response the outer promise never settles and the thrown error becomes an unhandled rejection (crash in Node's default throw mode).…

Comment thread .github/workflows/publish-versioned-docs.yml Outdated
Comment thread .github/workflows/publish-versioned-docs.yml Outdated
Comment thread scripts/versioned-docs/find-missing.ts
Comment thread .github/workflows/publish-versioned-docs.yml
`next` is now an alias for the newest alpha/beta prerelease tag, resolved
by the CDN from versions.json, so prerelease tags are published as immutable
bundles like stable ones and the mutable tree built from main is renamed to
`dev`. versions.json gains `next`, `prereleases` and `dev`, the gap-fill also
backfills the prereleases of the upcoming major, and the banner, dropdown,
pre-build and workflow inputs follow the new labels.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline findings, I also checked whether DocsVersionBanner's "back to latest" link breaks under the versioned build's baseUrl: /docs/vX.Y.Z/ — i.e., whether Docusaurus treats that as a router basename and strips the version prefix from useLocation().pathname before parseDocsVersionPath/replaceDocsVersionInPath run. It doesn't: Docusaurus keeps the full baseUrl in the pathname (no basename-stripping router), so the version-prefix parsing in docs-version.ts still sees /docs/vX.Y.Z/... and resolves correctly.

Extended reasoning...

This run reported two new CONFIRMED findings (the retry_on: error vs. timeout gap in publish-versioned-docs.yml, and the unconditional useDocsVersions fetch running before the docs-only guard in DocsVersionDropdown.tsx) that will be posted as inline comments, so a human look is already signaled by those. My review body is limited to the narrow exception: recording one additional candidate issue that was investigated and ruled out this run beyond the inline findings — whether the versioned build's baseUrl acts as a react-router basename and strips the docs-version segment from useLocation().pathname, which would break DocsVersionBanner's link back to /docs/latest. Docusaurus does not use basename-based routing for baseUrl; the full baseUrl remains part of the pathname exposed via useLocation(), so parseDocsVersionPath's docs/<version>/... regex still matches on versioned builds and the banner link resolves correctly. This is offered purely as additional information about what was checked, not as a guarantee that the rest of the versioned-build routing is bug-free, and does not withdraw or supersede any previously reported findings on this PR.

Comment thread .github/workflows/publish-versioned-docs.yml
Comment thread src/components/DocsVersionDropdown.tsx

@dsanders11 dsanders11 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's a lot to chew on here, I've been working through it and exploring alternative solutions. For now requesting changes just to indicate I'm still actively working on this.

@dsanders11 dsanders11 self-assigned this Sep 18, 2026
Resolve sidebars.js conflict by keeping the template call and port
the boilerplates-and-clis removal from #1222 into sidebars-template.js.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXtnHVnLbGQyY34y9CKffG
Comment thread src/util/docs-version.ts
Comment on lines +157 to +167
export function replaceDocsVersionInPath(
pathname: string,
targetVersion: string,
): string {
const parsed = parseDocsVersionPath(pathname);

if (!parsed) {
return pathname;
}

return `/docs/${targetVersion}${parsed.rest ? `/${parsed.rest}` : '/'}`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 Visitors on translated docs pages (e.g. /de/docs/latest/...) get bounced to the English page whenever they use the new version dropdown, even when picking the version they're already on. replaceDocsVersionInPath (docs-version.ts:157-167) rebuilds the URL as /docs/${targetVersion}${rest} with no locale segment, and DocsVersionDropdown's VersionLink (DocsVersionDropdown.tsx:97) feeds it useLocation().pathname, which includes the locale prefix on any non-English page. Fix: preserve the current locale segment (or resolve the link through Docusaurus's locale-aware URL builder) so every dropdown entry, across all 7 non-English locales, keeps the visitor on their language instead of only the English copy.

Extended reasoning...

docusaurus.config.ts keeps locales de/es/fr/ja/pt/ru/zh, each with its own baseUrl prefix (e.g. /de/), and registers the new custom-electronDocsVersionDropdown navbar item globally (docusaurus.config.ts:155-158), so it renders on every localized doc page too. On such a page useLocation().pathname is like /de/docs/latest/tutorial/introduction. DocsVersionDropdown builds every entry's href via docsVersionUrl(pathname, entry.version) (DocsVersionDropdown.tsx:97). docsVersionUrl calls replaceDocsVersionInPath, which uses parseDocsVersionPath to grab only the docs// part and returns /docs/${targetVersion}${rest}, discarding the /de prefix. The resulting absolute link is https://www.electronjs.org/docs/latest/tutorial/introduction — the English page — for every entry, including clicking 'latest' while already on latest. No such dropdown existed before this change, so this is a new regression, not pre-existing.

Verification: normal — The locale prefix is silently dropped when the version dropdown builds its links, so non-English docs readers are bounced to English. Chain: docusaurus.config.ts:65-95 keeps locales de/es/fr/ja/pt/ru/zh each with its own baseUrl (/de/ ...), and docusaurus.config.ts:155-158 registers custom-electronDocsVersionDropdown globally, so the dropdown renders on localized docs pages. On… | nit.…

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed, with a narrower scope: only docs/latest is translated (the versioned builds run with --locale en and i18n.locales: ['en']), so the latest entry was the one that could keep the reader's locale and didn't. Fix prepared: replaceDocsVersionInPath/docsVersionUrl take an optional locale prefix that is kept for latest and dropped for every other version, with the prefix resolved from useDocusaurusContext().i18n in the dropdown and banner, plus tests. Holding the push until the pending maintainer review lands, since the branch is under review.


Generated by Claude Code

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.

2 participants