Skip to content

feat(cli): cloudhal build --published and the static not-published page (RFC-0043 PR 2) - #198

Merged
Madraka merged 2 commits into
mainfrom
feat/build-published-static-status
Nov 10, 2026
Merged

Madraka merged 2 commits into
mainfrom
feat/build-published-static-status

Conversation

@Madraka

@Madraka Madraka commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

RFC-0043 rollout PR 2 (§1.10, static builds). Part of the self-host work behind #150. The rebuild-on-publish hook comes in a separate PR after this one.

What changes

  • cloudhal build --published builds the published snapshot only (resolvePublishedBuild in snapshot.ts).
    • With nothing published, it writes the not-published status page as the whole output (commands/build-status.ts), without an Astro run:
      • index.html and 404.html, from statusPageHtml (@cloudhal/astro-renderer/status-page) in the site's default locale, with the site's name and no logo or theme tokens (§1.6);
      • robots.txt with Disallow: /;
      • _headers with no-store, noindex and the status CSP;
      • a .cloudhal-status marker that holds the state.
    • The build prints the state it wrote.
    • A published pointer to a missing snapshot throws. It is never treated as "not published".
    • --published --snapshot is refused.
    • @cloudhal/cli now depends on @cloudhal/astro-renderer directly. It was already in the tree through @cloudhal/cloudflare.
  • Plain cloudhal build is unchanged: published, then latest, then a fresh snapshot. This is documented in --help (the CLI reference) and in the guide. A typed overload keeps build()'s return type for existing callers.
  • deploy/docker/bin/site-build passes --published and says when it published the status page.
  • deploy/docker/Caddyfile: an @status matcher (file /.cloudhal-status, not /robots.txt) rewrites to /index.html and serves it with file_server { status 503 }. It sends Retry-After: 3600, Cache-Control: no-store, X-Robots-Tag: noindex, X-Cloudhal-State, the CSP, nosniff and no-referrer. The existing caddy validate step covers it.
  • docs/guides/self-host.md: the latest-snapshot caveat is gone. A new "Before the first publish" paragraph has the header table, a recipe for other web servers, and a note that the theme's statusPage hook does not run in a static build. The upgrade step mentions the new Caddyfile.

Tests

  • packages/cli/test/build-published.test.ts:
    • the status output: files, the tr locale, the English fallback, and a symlinked dist that is removed rather than followed;
    • a spawned build --published before a publish: status page, no snapshot created, no drafts;
    • a spawned build after a publish: the site, no marker;
    • --snapshot refused.
  • scripts/docker-smoke.ts:
    • site-build before the first publish, which also covers the 0.6.3 legacy move;
    • the compose Caddyfile run in caddy:2.11 on the container's volume: / and deep paths answer 503 with Retry-After: 3600, no-store and noindex, and robots.txt answers 200;
    • after the publish, 200 with the home page and no marker;
    • a third build checks the swap and that the previous build is kept.
  • Local results:
    • CLI suite: 65 files and 453 tests, all passing;
    • apps/docs repo-docs test passes;
    • biome, size check and typecheck are clean;
    • the Caddyfile was validated and served with local Caddy 2.11.9: 503 on pages, 200 on robots.txt, 200 once current points at a real build.
  • Not run locally: the image build and the docker smoke test. The Docker workflow runs both on this PR.

Patch changeset for @cloudhal/cli.

🤖 Generated with Claude Code

Madraka and others added 2 commits November 10, 2026 17:13
…ge (RFC-0043 PR 2)

`cloudhal build --published` builds the published snapshot only. A site
that was never published gets the not-published status page as its whole
output, written without an Astro run: index.html and 404.html from
@cloudhal/astro-renderer/status-page in the site's default locale, a
robots.txt that disallows everything, a _headers file (no-store, noindex,
the page's CSP) and a .cloudhal-status marker. A published pointer to a
missing snapshot is an error, never "not published". Plain `cloudhal
build` is unchanged (published, then latest, then a fresh snapshot).

The Docker image's site-build passes --published, so a fresh self-host
no longer serves drafts before its first publish. The Caddyfile answers
every path except robots.txt with the page, 503, Retry-After: 3600 and
no-store while the marker exists. The self-host guide documents the
headers and drops the latest-snapshot caveat.

Tests: build-published.test.ts covers the status output (locale, a link
at dist removed rather than followed, the English fallback), a spawned
build before and after a publish, and --snapshot being refused. The
docker smoke test now builds before the first publish and runs the
compose Caddyfile in a caddy:2.11 container on the same volume: 503 with
Retry-After before the publish, 200 after it, then the swap and the
kept-previous checks on a third build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
…FC-0043 PR 2 review)

- build-published.test.ts proves "never the latest":
  - an unpublished snapshot before the first publish still gives the
    status page, which does not name that snapshot;
  - a draft rename after the publish stays out of the build;
  - a rollback from s2 to s1 builds s1.
- site-build passes --published only when `cloudhal build --help`
  lists it. A CLI from 0.7.1 or older, in an image built from this
  checkout with CLOUDHAL_VERSION, gets one warning and the old call.
- entrypoint runs one site-build in the background once Studio answers
  and /data/cloudhal.json exists. tini reaps it, not `cloudhal studio`.
  A never-published volume upgraded from 0.6.4–0.7.1 switches at once
  to the 503 status page, a new volume gets it on its first start, and
  a failed build only logs.
- docker-smoke waits for the start-up build and takes a draft snapshot
  before the explicit first build, which must still be the 503. Where
  the CLI has no --published, it skips the status-page checks. The
  0.6.3 folder is simulated after the start-up build.
- self-host guide (upgrades, building the image), README, docker.yml
  backfill note and the changeset follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
@Madraka
Madraka merged commit b7e0a1b into main Nov 10, 2026
16 of 17 checks passed
@Madraka
Madraka deleted the feat/build-published-static-status branch November 10, 2026 15:07
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.

1 participant