ci: migrate the docs canister to dfinity's certified-assets canister - #708
Merged
Conversation
The docs canister moved from dfx to icp-cli deploys but kept the legacy dfx-era asset recipe (`@dfinity/asset-canister`, which installs dfinity/sdk's `assetstorage` 0.29.1). Move it to `@dfinity/static-site`, which pins dfinity's current certified-assets canister and its sync plugin as a matched pair. What this buys: - Unknown paths return a real 404 with the `404.html` Docusaurus already builds. The legacy canister answers them with the root `index.html` at HTTP 200, which is why the deploy assertions grep bodies instead of statuses — and why `/.ic-assets.json` was being served at 200 too. - `.well-known/` is uploaded despite the leading dot, so the custom domain resolves with no opt-in file. `docs/static/.ic-assets.json` existed only to force that upload and is deleted. - ETags on every response, and one version pin instead of a recipe version plus a separate `assetstorage` version. `trailingSlash: false` is required, not incidental: the new canister canonicalises clean URLs, so with Docusaurus's default output every deep link would 307 to a trailing-slash URL that disagrees with the `<link rel="canonical">` Docusaurus writes. With it, Docusaurus emits `<route>.html` and the served URLs are byte-identical to today's at 200. Verified against a local replica by deploying this manifest and, for comparison, the legacy recipe over the same 230-asset build: the full URL surface serves 200, `/.well-known/ic-domains` passes the assertion in `.github/actions/deploy-canister/`, unknown paths 404 with the Docusaurus page, and `/.ic-assets.json` is no longer served. Cutover is a one-off `-m reinstall`; the canister ID and its custom-domain registration survive. Documented in AGENTS.md along with the remaining legacy/new differences.
Contributor
Cursor AI review👀 HUMAN REVIEW REQUESTED — significant intended changes detected
Significant Changes Requiring Human Review
VerdictDecision: REQUEST_HUMAN_REVIEW Generated for commit eab666e |
The docs canister canonicalises to the extension-less form, so `/next/` 307s to `/next`. Nothing links to the slashed form any more; these were the last prose mentions of it.
Kamirus
added a commit
that referenced
this pull request
Aug 11, 2026
Companion to #708, which migrates the docs canister on `v3` to dfinity's certified-assets canister. Merge this one first, or alongside. ## Why `v3` owns the docs canister from now on, and that covers the **2.x docs too**. v3's Docusaurus build is a superset of this branch's: the same `docs/versioned_docs/version-2.x/` at the site root, plus the in-development line under `/next`. So a 2.x release publishing docs from here adds nothing, and would drop `/next` until v3 redeployed. After #708 it would also **fail**. That PR moves the canister to `@dfinity/static-site`, and `dfx deploy` cannot drive it — different wasm, different Candid interface. The step would error and abort the release *before* the artifacts PR, i.e. a half-finished release. ## What changed Removes `Deploy docs canister` and the `Install docs dependencies` step that only fed it, leaving the reasoning and the manual publish command in a comment where they were. The `cli` canister deploy is deliberately untouched — that canister still runs the legacy asset canister and `dfx deploy` still drives it correctly. Also refreshes a now-stale comment in `docs/docusaurus.config.js`: it said the two branches must agree because the canister is shared and "whichever branch deployed most recently decides the layout." Only v3 deploys now, so it says that instead. The config still shapes local previews on this branch, so the versions block itself is unchanged. ## Consequence worth knowing Docs edits made on `main` no longer reach docs.mops.one on a 2.x release. They reach it once merged into `v3` and v3 deploys — which per the plan happens after any main deploy. To publish out of band, from a v3 checkout: ``` icp deploy docs -e ic --identity mops --no-create --yes ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SRHrmmy3iWFZwPhQtXVfQQ</body> --- _Generated by [Claude Code](https://claude.ai/code/session_01SRHrmmy3iWFZwPhQtXVfQQ)_ Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The legacy asset recipe cannot redeploy a canister that already holds assets: its sync plugin either exhausts its sandbox memory after minutes of silence or emits a create-everything batch that traps with
'asset already exists'(dfinity/icp-cli#703). That is what broke the docs deploy in thecli-v3.0.0-beta.1release run — 37 silent minutes, then the job timeout. Only the empty-canister path works, and a reinstall buys exactly one deploy before the next one fails again.This moves
docsto@dfinity/static-site@v0.3.3— dfinity's current certified-assets canister, which Raymond pointed at (skill). The recipe tag pins the canister wasm and its sync plugin as a matched pair, and redeploys work incrementally, change detection included.Also gained in the move:
404.htmlDocusaurus already builds. The legacy canister answers them with the rootindex.htmlat HTTP 200 — the soft-404 that forces every deploy assertion inrelease.ymland.github/actions/deploy-canister/to grep response bodies instead of statuses, and that served/.ic-assets.jsonas the intro page..well-known/uploads despite the leading dot, so the custom domain resolves with no opt-in file.docs/static/.ic-assets.jsonexisted only to force that upload and is deleted — it was load-bearing fordocs.mops.oneand easy to delete by accident.trailingSlash: falseis required, not incidentalThe new canister canonicalises clean URLs. With Docusaurus's default output (
quick-start/index.html) the canonical form becomes/quick-start/, so every deep link would 307 to a URL that disagrees with the<link rel="canonical">Docusaurus itself writes.falsemakes Docusaurus emit<route>.html, which the canister serves at the extension-less URL — byte-identical to today's URLs, at 200, with no redirect hop. Both variants were built and deployed to confirm this.Verification
Full cutover simulation on a local replica, in production order — and step 3 is the exact case the legacy recipe fails at:
icp deploy -m reinstallPath parity against the legacy recipe over the same 230-asset build:
/,/quick-start,/next,/mops.toml,/sitemap.xml,/img/logo.svgand/.well-known/ic-domainsserve identically;/does-not-existreturns the Docusaurus 404 page instead of a soft 200;/.ic-assets.jsonno longer leaks.The
.well-known/ic-domainsassertion in.github/actions/deploy-canister/passes with no.ic-assets.jsonpresent. Neither canister setsCache-Control, so the migration is header-neutral; a_headersfile for content-hashed/assets/*is a follow-up, deliberately not bundled here.Cutover
Not a plain deploy — the wasms have incompatible state, so
icp deploy's defaultautomode picks an upgrade that cannot work. One-off, with a snapshot as the rollback handle (snapshots require the canister stopped; a stopped asset canister keeps serving queries, so this is not an outage):The canister ID — and with it the custom-domain registration — survives; the cost is downtime for the length of the sync (~21s locally). Rollback is
snapshot restore, which brings back the old wasm and assets together. Subsequent releases are ordinary deploys.Interaction with
main#709 (merged) already removed
main's dfx docs deploy, which could not drive the new wasm.v3owns the docs canister: its build is a superset ofmain's — the samedocs/versioned_docs/version-2.x/at the site root plus the in-development line under/next— so 2.x doc edits reach production once merged intov3.Not migrated
assets,blogandplay-frontendare unchanged here.clicannot move yet —static-sitetakes a singledirand that canister serves two (cli-releases/andcli-releases/frontend/dist/). Notestatic-siteis 0.x, where a minor bump is breaking by its own policy (reinstall + full re-upload) — read release notes before moving the pin. All of this is written down in AGENTS.md.🤖 Generated with Claude Code