Skip to content

ci: migrate the docs canister to dfinity's certified-assets canister - #708

Merged
Kamirus merged 3 commits into
v3from
claude/docs-canister-asset-migration-sdz47w
Aug 11, 2026
Merged

ci: migrate the docs canister to dfinity's certified-assets canister#708
Kamirus merged 3 commits into
v3from
claude/docs-canister-asset-migration-sdz47w

Conversation

@Kamirus

@Kamirus Kamirus commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

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 the cli-v3.0.0-beta.1 release 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 docs to @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:

  • Unknown paths return a real 404, using the 404.html Docusaurus already builds. The legacy canister answers them with the root index.html at HTTP 200 — the soft-404 that forces every deploy assertion in release.yml and .github/actions/deploy-canister/ to grep response bodies instead of statuses, and that served /.ic-assets.json as the intro page.
  • .well-known/ uploads 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 — it was load-bearing for docs.mops.one and easy to delete by accident.
  • ETags on every response and certified 206 range responses (legacy sends neither).

trailingSlash: false is required, not incidental

The 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. false makes 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:

step result
deploy with the legacy recipe (mainnet's state today) 230 assets, 14s
switch manifest, icp deploy -m reinstall 230 assets, 21s
normal deploy on the populated canister 5s — change detection, no re-upload
content change, normal deploy 6s — new file served
one more repeat deploy 5s

Path parity against the legacy recipe over the same 230-asset build: /, /quick-start, /next, /mops.toml, /sitemap.xml, /img/logo.svg and /.well-known/ic-domains serve identically; /does-not-exist returns the Docusaurus 404 page instead of a soft 200; /.ic-assets.json no longer leaks.

The .well-known/ic-domains assertion in .github/actions/deploy-canister/ passes with no .ic-assets.json present. Neither canister sets Cache-Control, so the migration is header-neutral; a _headers file 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 default auto mode 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):

icp canister stop docs -e ic --identity mops
icp canister snapshot create docs -e ic --identity mops
icp canister start docs -e ic --identity mops
icp deploy docs -e ic --identity mops --no-create -m reinstall

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. v3 owns the docs canister: its build is a superset of main's — the same docs/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 into v3.

Not migrated

assets, blog and play-frontend are unchanged here. cli cannot move yet — static-site takes a single dir and that canister serves two (cli-releases/ and cli-releases/frontend/dist/). Note static-site is 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

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.
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Cursor AI review

👀 HUMAN REVIEW REQUESTED — significant intended changes detected

Category Assessment Details
Summary Migrates the production docs canister from @dfinity/asset-canister to @dfinity/static-site@v0.3.3, sets Docusaurus trailingSlash: false, and drops the docs-only .ic-assets.json opt-in for .well-known/.
Code Quality Small, focused change: recipe pin + matching Docusaurus constraint, with AGENTS.md documenting the mid-migration split and why cli cannot move yet.
Consistency Trailing-slash URL spelling updated in lockstep across release.yml, cli/RELEASE.md, AGENTS.md, and .agents/skills/mops-cli/SKILL.md; other asset canisters stay on the legacy recipe.
Security Recipe remains version+sha256 pinned in icp.yaml; no new secrets, no trigger/permission widening in release.yml; .well-known/ic-domains still present under docs/static/ and still asserted by deploy-canister/action.yml.
Tests No new automated coverage (this deploy path has none); post-deploy check still greps https://$DOMAIN/.well-known/ic-domains body in .github/actions/deploy-canister/action.yml — appropriate for both legacy soft-404 and real 404 canisters.
Maintainability AGENTS.md records load-bearing differences (404 vs soft-404, .ic-assets.json, trailingSlash: false, reinstall cutover); comment in deploy-canister/action.yml updated for the shared action’s remaining legacy callers.

Significant Changes Requiring Human Review

  • S1: Production docs canister wasm cutover (reinstall required)
    • References: icp.yaml (docs recipe ~L33–44), .github/actions/deploy-canister/action.yml (L30–41), docs/docusaurus.config.js (L20–24), docs/static/.ic-assets.json (deleted), AGENTS.md (asset-canister mid-migration note)
    • Base behavior: docs deployed via @dfinity/asset-canister@v2.3.0 / assetstorage 0.29.1; unknown paths soft-404 as root index.html at HTTP 200; .well-known/ upload depended on docs/static/.ic-assets.json; Docusaurus had no explicit trailingSlash.
    • Diff proof: Head switches the docs recipe to @dfinity/static-site@v0.3.3 (incompatible wasm), deletes the .ic-assets.json opt-in, and requires trailingSlash: false so clean URLs stay extension-less without 307; release.yml / deploy-canister still run plain icp deploy … --no-create --yes (no -m reinstall), so cutover is an intentional one-off ops step outside CI.
    • Impact: Confirm the production reinstall plan (icp deploy docs -e ic --identity mops --no-create -m reinstall), expected docs.mops.one downtime, .well-known/custom-domain survival, URL/404 behavior change, and ordering vs removing any remaining mainline dfx deploy docs path before the next release hits this manifest.
    • Confidence: High

Verdict

Decision: REQUEST_HUMAN_REVIEW
Risk: Medium-High
Reason: This is an intentional production docs-canister wasm migration with a mandatory reinstall cutover and release-pipeline timing risk; a human should sign off on the ops plan even though the diff itself looks coherent and pinned.


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>
@Kamirus
Kamirus merged commit 78705e7 into v3 Aug 11, 2026
25 checks passed
@Kamirus
Kamirus deleted the claude/docs-canister-asset-migration-sdz47w branch August 11, 2026 10:47
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