08 · Publishing & Deploy

← Docs home · Task: publish a section into docs/, bust caches, and ship it to Cloudflare Pages.

Source of truth: scripts/workflows/publish.py, bump-cache-version, wrangler.toml. The README’s “GitHub Pages” note is stale — the live site is Cloudflare Pages.


Step 1 — publish to docs/

publish.py is pipeline step 13, but also runs standalone:

python scripts/workflows/publish.py --project "Calculus Volume 1" --section 3.1
python scripts/workflows/publish.py --project "Calculus Volume 1"            # all sections

What it does, in order:

  1. Verify HTML (_verify_html) — broken links, unclosed tags, malformed LaTeX → writes a .verify_ok stamp on pass.
  2. Copy projects/<Book>/html/<sec>.htmldocs/<slug>/chapter-N-*/<sec>.html and referenced images → docs/images/.
  3. Apply figure swaps (the figure-animator survival guard) — <img><figure-anim-wrap> / <figure-static-wrap> from figure_animations.yaml.
  4. Group consecutive figures into .figure-row grids.
  5. Sync the navbar (sync_docs_script_dropdowns) and regenerate the collection index in docs/index.html.

Useful flags:

Flag Effect
--dry-run Report only; write nothing.
--sections 2.1 2.3 Publish only those sections.
--auto-render Invoke figure_animate render --execute after copy (canonical full-pipeline flow; needs Docker).
--slides Also build the Slidev deck (see 06).
--sweep-only GC orphaned static PNGs (run after figure renders settle).

⚠️ Path-depth gotcha (memory project_publish_path_depth): the asset-rewrite depth in publish.py must be sub.count('/') + 1. A past commit bumped it to +2 and silently broke CSS on chapter-subdir pages. If chapter pages render unstyled after a publish change, check this first.


Step 2 — bump cache version

Pages send Cache-Control: no-cache on .html (see docs/_headers), but versioned assets (?v=YYYYMMDD) own their own cache lifetime. After any CSS/JS or figure re-render, bump the token.

Say “bump cache version” → the bump-cache-version skill sweeps three surfaces and verifies the old token is gone:

  1. The skeleton template.
  2. projects/*/html/*.html.
  3. docs/*/*.html (+ chapter subdirs).

Token format: YYYYMMDD (today), or YYYYMMDD<letter> for same-day re-bumps. Per-figure MP4 tokens in figure_animations.yaml (cache_buster:) and the HTML ?v= query must be bumped together.


Step 3 — deploy to Cloudflare Pages

git push does NOT deploy. (Memory project_pages_deploy.) The bookshelf Pages project has no Git integration — it deploys by direct upload.

⚠️ The production branch is desktop, not preview. Deploy to desktop to update the live public site (oerbookshelf.app + getstarted.oerbookshelf.app). --branch preview only updates the throwaway preview.bookshelf-9l4.pages.dev alias and will leave the real site stale. Verify the production branch with wrangler pages deployment list --project-name bookshelf --environment production.

# PRODUCTION (updates oerbookshelf.app):
wrangler pages deploy docs --project-name bookshelf --branch desktop --commit-dirty=true

# staging only (preview.bookshelf-9l4.pages.dev):
# wrangler pages deploy docs --project-name bookshelf --branch preview

Backend prerequisites (one-time)


Docs site

These docs are published as their own rendered site at docs.oerbookshelf.app.

Edit .mdnpm run docs → deploy. Forgetting the rebuild ships stale HTML.


Deploy ordering rule

If a section uses raSHio callouts/walkthroughs that link the live tool site, deploy raSHio first, then ship the book page. Same for any externally-linked companion — the link target must be live before the page that points at it.


Quick checklist

[ ] preflight green               (see 03 · Content Pipeline)
[ ] publish.py (--auto-render if figures)   → docs/ updated, .verify_ok written
[ ] figures/CSS changed?          → bump-cache-version
[ ] external links (raSHio) live?
[ ] wrangler pages deploy docs --project-name bookshelf --branch desktop --commit-dirty=true
[ ] spot-check oerbookshelf.app  (--branch desktop = production)
[ ] commit  (only if asked — see 09 · Maintenance)