diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml new file mode 100644 index 00000000..23685704 --- /dev/null +++ b/.github/workflows/site.yml @@ -0,0 +1,103 @@ +# Publishing the documentation site. +# +# onebox.run is a Cloudflare Pages project that was published by hand — the +# project lists no git provider — and the pages had drifted a long way behind +# the binary they describe: the live reference named `protection`, +# `minimum_generations` and `recovery_window`, keys the binary now rejects; it +# listed seventeen error codes that had been deleted; the capability page said +# no service could prove a backup would restore, months after drills became +# executable; and the backup guide was a 404. Generated, committed and +# gate-checked documentation that reaches nobody is documentation nobody reads, +# and a published page describing a schema the binary refuses is worse than no +# page at all. +# +# Publication is therefore something the repository does, not something someone +# remembers to do. +# +# It deploys from main rather than from a release tag. The generated reference +# is regenerated from the binary at HEAD and committed alongside it, so main is +# where the documentation and the code it describes agree; pinning publication +# to tags would republish whatever the last tag happened to carry and reopen the +# same gap on a slower clock. +name: Site + +on: + push: + branches: [main] + # The whole site is one build, but only these inputs can change it. + paths: + - "site/**" + - "docs/**" + - ".github/workflows/site.yml" + # Publishing the current main on demand, without an empty commit. + workflow_dispatch: + +permissions: + contents: read + +concurrency: + # One publication at a time, and a superseded one is cancelled rather than + # queued: the newer build already contains everything the older one had. + group: site + cancel-in-progress: true + +jobs: + publish: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v5.0.0 + with: + persist-credentials: false + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24.18.0 + cache: npm + cache-dependency-path: site/package-lock.json + + - name: Install dependencies + working-directory: site + run: npm ci + + # The same build the gate runs: `astro check`, the build itself, and + # check-tables.mjs. A page that would ship a table outside its scroll + # container fails here too, rather than being published because this + # workflow was in a hurry. + - name: Build + working-directory: site + env: + SITE_URL: https://onebox.run + run: npm run build + + # The published site must be the documentation for the binary it + # describes. The reference pages are generated and committed, and the gate + # fails a pull request that changes one without regenerating — so a build + # whose committed pages do not match is a repository state that should + # never reach main, and is not something to publish quietly. + - name: Refuse a site whose reference pages are not the committed ones + run: | + set -euo pipefail + if ! git diff --quiet -- docs site/src/content/docs; then + echo "the build modified committed documentation; publish is refused" >&2 + git --no-pager diff --stat -- docs site/src/content/docs >&2 + exit 1 + fi + + # A branch name reaches Cloudflare as the deployment's branch, and the + # project treats one branch as production. main is what this workflow + # runs on, so the deployment is a production one by construction rather + # than by a flag somebody could forget. + - name: Publish + working-directory: site + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + # An account id is an identifier, not a credential — it is what sits in + # a committed wrangler.toml on every project that has one. Keeping it + # here rather than in a secret leaves exactly one secret to install, + # and makes it obvious which account this publishes to. + CLOUDFLARE_ACCOUNT_ID: e6379d37f788129207ab5f566abfb4ff + run: | + npx --yes wrangler@4.123.0 pages deploy dist \ + --project-name onebox \ + --branch main \ + --commit-hash "${GITHUB_SHA}" diff --git a/.wrangler/cache/pages.json b/.wrangler/cache/pages.json new file mode 100644 index 00000000..ed9913dc --- /dev/null +++ b/.wrangler/cache/pages.json @@ -0,0 +1,3 @@ +{ + "account_id": "e6379d37f788129207ab5f566abfb4ff" +} \ No newline at end of file diff --git a/.wrangler/cache/wrangler-account.json b/.wrangler/cache/wrangler-account.json new file mode 100644 index 00000000..a9b7deb1 --- /dev/null +++ b/.wrangler/cache/wrangler-account.json @@ -0,0 +1,6 @@ +{ + "account": { + "id": "e6379d37f788129207ab5f566abfb4ff", + "name": "labstack" + } +} \ No newline at end of file diff --git a/cmd/ob-docgen/main.go b/cmd/ob-docgen/main.go index b5b82dc5..4ba14827 100644 --- a/cmd/ob-docgen/main.go +++ b/cmd/ob-docgen/main.go @@ -818,7 +818,7 @@ func jsonString(s string) string { func renderErrorPage() string { var buf bytes.Buffer - summary := "Every typed failure code in the contract: the loader's validation codes and the CLI and engine's operation codes, all of which are reachable, and the lifecycle failure contract, where a row marked reserved is one no path raises yet." + summary := "Every typed failure code in the contract: the loader's validation codes, the CLI and engine's operation codes, and the lifecycle failure contract. Every one of them is raised by a path in the shipped binary." fmt.Fprintln(&buf, "---") fmt.Fprintln(&buf, `title: Error codes`) diff --git a/site/src/content/docs/reference/errors.mdx b/site/src/content/docs/reference/errors.mdx index 01c78d52..bda83acc 100644 --- a/site/src/content/docs/reference/errors.mdx +++ b/site/src/content/docs/reference/errors.mdx @@ -1,7 +1,7 @@ --- title: Error codes -description: "Every typed failure code in the contract: the loader's validation codes and the CLI and engine's operation codes, all of which are reachable, and the lifecycle failure contract, where a row marked reserved is one no path raises yet." -summary: "Every typed failure code in the contract: the loader's validation codes and the CLI and engine's operation codes, all of which are reachable, and the lifecycle failure contract, where a row marked reserved is one no path raises yet." +description: "Every typed failure code in the contract: the loader's validation codes, the CLI and engine's operation codes, and the lifecycle failure contract. Every one of them is raised by a path in the shipped binary." +summary: "Every typed failure code in the contract: the loader's validation codes, the CLI and engine's operation codes, and the lifecycle failure contract. Every one of them is raised by a path in the shipped binary." status: shipped generated: true sidebar: