Skip to content

ADR-070 — Split docs deploy from app deploy (CI-gated docs, e2e-gated app)

Status · Accepted (gate workflow renamed from e2e.yml to docker-e2e.yml by ADR-071) Date · 2026-05-28 Closes into · .github/workflows/deploy-docs.yml + preview.yml Related ADRs · ADR-014 (CI / pipeline chain), ADR-016 (asset freshness cron), ADR-071 (e2e/docker-e2e consolidation — the e2e workflow this ADR refers to has been folded into docker-e2e) Related fix · 82fb49b4d (VitePress {‌{ }} Vue interpolation escape)

Context

Until today, the orrery GH Pages publish chain was a single linear pipeline:

push to main → ci.yml → e2e.yml → preview.yml → gh-pages

preview.yml built BOTH the app bundle (npm run build) AND the docs site (npm run docs:build), then published the entire build/ (with build/docs/ overlaid) to the gh-pages branch. The workflow_run trigger required the upstream e2e workflow to complete with conclusion: success before this final deploy fired.

The coupling was deliberate — don't ship broken app code to the public preview. But the same gate also blocks docs from shipping when e2e fails for reasons unrelated to docs correctness.

The failure mode that triggered this ADR (2026-05-27 → 28):

  1. A VitePress build error (docs/adr/ADR-064.md's inline ${‌{ secrets.* }} was parsed by Vue's compiler-sfc as template interpolation) failed Deploy preview.
  2. The fix landed in commit 82fb49b4d ~30 min later.
  3. But Deploy preview never re-fired automatically on the fix commit because the mobile e2e suite was independently red on a parallel-agent push (mars-tier3-panorama exit-button click flake on mobile-chromium). The fix sat on main, validated by npm run docs:build locally, but not deployed.
  4. Resolved temporarily by manual workflow_dispatch — but the underlying coupling remains.

This is the third time in recent memory the e2e gate has blocked an unrelated deploy:

  • v0.6.0 → v0.6.1: four-round CI ping-pong from mobile-only failures blocked the tag deploy
  • 2026-05-25: visual:69 + science-tabs baseline mismatch held up the deploy of mobile fixes
  • 2026-05-27: VitePress fix held up by parallel agent's mobile-test work (this incident)

In all three cases, the failure didn't actually mean the deploy target was broken — it meant a sibling test was flaky.

Decision

Split the deploy chain into two workflows, gated independently:

  • deploy-docs.yml — fires on CI success on main. Builds + publishes only build/docs/ to gh-pages, with keep_files: true so the previously-published app bundle stays intact.
  • preview.yml — stays as is in scope, but now fires only the app bundle + docs together deploy on e2e success. Continues to publish the full build/ (overwrites existing docs subdir with whatever it builds, which is the same docs content from the same npm run docs:build).

Shared concurrency group gh-pages-publish (with cancel-in-progress: false) serializes both workflows on the gh-pages branch so a docs-deploy and app-deploy in flight don't race.

What this changes operationally

  • Docs fixes ship as soon as CI passes, regardless of e2e state. Typical wall-clock: 5-10 min push-to-live.
  • App bundle still gated on full e2e (desktop + mobile-chromium). No change in the safety guarantee for the runtime app.
  • GH Pages sees two distinct commits per main push: one for docs (CI-gated) and one for app+docs (e2e-gated, lands a few minutes later). The docs subdir is touched twice — first by deploy-docs.yml (via destination_dir: docs + keep_files: true), then by preview.yml (full publish_dir: ./build).

Why keep_files: true for the docs deploy

The peaceiris/actions-gh-pages action's default behaviour is to wipe the publish_dir's contents on the target branch and replace with the new artefact. For a docs-only deploy that would wipe the app bundle — exactly the regression this ADR is trying to prevent. keep_files: true makes the action overlay-style — only files within destination_dir: docs get touched.

Why a shared concurrency group instead of separate ones

cancel-in-progress: false means each workflow's run waits for any other run in the group to finish before starting. Without that, a docs-deploy and an app-deploy could land at exactly the same time and one of them would fail with Updates were rejected because the remote contains work that you do not have locally. Single-group serialization avoids that without losing parallelism on workflows that don't touch gh-pages.

Consequences

Positive

  • Docs fixes ship independent of e2e flakiness. The dominant motivating case: VitePress / sidebar / typography fixes are docs-correctness changes that don't touch app code, but used to be held hostage by mobile-only e2e flakes.
  • Faster docs feedback loop. 5-10 min CI vs 25-30 min full pipeline. Useful when iterating on architecture docs / ADR layouts.
  • Same app-deploy safety net. preview.yml's e2e gate is preserved. No regression in "don't ship broken app to public preview".
  • No new dependency. Both workflows use the same peaceiris/actions-gh-pages@v4 action with different config; no new actions or services introduced.

Negative

  • Two GH Pages commits per push instead of one — slightly noisier git history on the gh-pages branch. Not user-visible; gh-pages content is the deploy artefact, not a reviewable surface.
  • A docs change that depends on a coordinated app change (rare — e.g. linking docs to a new app route) can ship first via deploy-docs, leaving a brief window of dead-link-to-not-yet-deployed-route. Mitigation: rely on npm run preflight's link validation locally for that specific case, or stage the coordinated change so docs land after app.
  • The shared concurrency group introduces some serialization. A burst of pushes will see deploys queue up. Practical impact: minimal (deploys are rare events; serialization adds at most a few minutes to the second deploy in a burst).

Neutral

  • Both workflows continue to support workflow_dispatch for manual re-runs (useful for the recovery case the May 2026 incident used).
  • The Monday-06:00-UTC weekly cron stays on preview.yml (full app+docs rebuild for asset freshness per ADR-016).

Future work

If we add a public app domain in production (separate from the preview gh-pages site — RFC-024 / GH #260 phase 2), the docs split may not be needed there; the docs site could be its own gh-pages subdomain entirely. But for the preview chain, the split lands today.

Trail

  • Original incident: 2026-05-27 23:00 UTC — VitePress build error on docs/adr/ADR-064.md blocked Deploy preview.
  • Fix landed: 82fb49b4d (the VitePress {‌{ }} ZWNJ escape pattern across 9 docs files).
  • Manual recovery: gh workflow run "Deploy preview" --ref main at 2026-05-28 03:50 UTC fired the chain regardless of e2e state, validated the fix end-to-end, and updated gh-pages.
  • This ADR + split workflows: 2026-05-28.

Orrery — architecture documentation · MIT · No tracking