ci(docs): docs.beardrive.ai deploys itself again (#152)

docs.beardrive.ai was serving a build from around 2026-07-19 — three weeks
and 38 docs commits stale. /manual/hooks/ 404s while /manual/skills-and-hooks/,
deleted in #85, still serves; the sitemap has no <lastmod> and robots.txt is
Cloudflare's managed content-signals file with no Sitemap: line, so #140
plainly never shipped.

The cause: the Pages project (beardrive-docs, docs.beardrive.ai) is a
direct-upload project with no Git provider. Someone ran `wrangler pages
deploy` by hand, then stopped, and nothing anywhere noticed — every check
this repo has runs during a deploy that was no longer happening.

docs.yml builds web/docs on PRs that touch it and deploys to Pages on pushes
to main. Not the Pages Git integration, deliberately: it clones shallow, and
astro.config.mjs reads each page's <lastmod> from the commit date behind it,
so a depth-1 checkout drops all 27 of them — hence fetch-depth: 0. It also
would rebuild the docs for every commit in a repo that is mostly Go.

The path filter includes internal/webapp/frontend/src/tw.css: the palette is
generated from that file, so it is a docs input even though it lives outside
web/docs (which is also why the checkout can't be sparse).

The post-deploy check:sitemap run is continue-on-error. Half of what it
checks — a Cloudflare-managed robots.txt shadowing ours, cache propagation
right after upload — isn't this repo's call, and a good deploy shouldn't go
red over it.

Needs CLOUDFLARE_API_TOKEN (Pages: Edit) and CLOUDFLARE_ACCOUNT_ID.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Snow Lee (Sungwon)
2026-08-11 18:28:32 -07:00
committed by GitHub
co-authored by Claude Opus 5
parent 37bd466bb7
commit 3d2acf658c
3 changed files with 86 additions and 1 deletions
+72
View File
@@ -0,0 +1,72 @@
# Builds web/docs on every PR that touches it, and deploys to Cloudflare Pages
# (project `beardrive-docs`, custom domain docs.beardrive.ai) on push to main.
#
# Deliberately NOT the Pages Git integration: that clones shallow, and the
# sitemap's <lastmod> comes from each page's commit date, so a depth-1 checkout
# silently drops every lastmod. Hence `fetch-depth: 0` below.
#
# Needs two secrets: CLOUDFLARE_API_TOKEN (an account API token with
# "Cloudflare Pages: Edit") and CLOUDFLARE_ACCOUNT_ID.
name: docs
on:
push:
branches: [main]
paths:
- "web/docs/**"
# The palette is generated from the hub frontend's tokens, so a change
# there changes the built docs too.
- "internal/webapp/frontend/src/tw.css"
- ".github/workflows/docs.yml"
pull_request:
paths:
- "web/docs/**"
- "internal/webapp/frontend/src/tw.css"
- ".github/workflows/docs.yml"
workflow_dispatch:
concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # sitemap <lastmod> comes from commit dates
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: web/docs/package-lock.json
# The build reads ../../internal/webapp/frontend/src/tw.css, so the whole
# repo has to be checked out — not just this subdirectory.
- run: npm ci
working-directory: web/docs
- run: npm run build
working-directory: web/docs
- name: Deploy to Cloudflare Pages
if: github.event_name != 'pull_request'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: >
npx --yes wrangler@4 pages deploy dist
--project-name=beardrive-docs
--branch=main
--commit-dirty=true
working-directory: web/docs
# Same checks Search Console runs: robots.txt -> index -> every URL 200s.
# Reports, never blocks: half of what it checks (a managed robots.txt
# shadowing ours, cache propagation) is Cloudflare's call, not this
# repo's, and a good deploy must not go red over it. Read the step.
- name: Verify what production actually serves
if: github.event_name != 'pull_request'
continue-on-error: true
run: npm run check:sitemap https://docs.beardrive.ai
working-directory: web/docs
+1 -1
View File
@@ -23,7 +23,7 @@ npm run e2e # Playwright suite against the seeded e2e hub (starts itself
./check-dist.sh # verify committed static/ matches frontend/src (run before releases)
```
There is no Makefile, linter config, or CI config in-repo. Releases run `goreleaser release` on a tagged commit (see `.goreleaser.yaml`); the version is injected via `-ldflags "-X main.version=..."` into `cmd/bdrive/main.go`. The frontend's built assets are **committed** (go:embed needs them in the module), so `go build` and `go install` never require Node — but a release tag must not ship a stale `internal/webapp/static`: run `frontend/check-dist.sh` first.
There is no Makefile or linter config in-repo. CI is three GitHub Actions workflows: `ci.yml` (build/vet/test on Linux and macOS), `bump-cloud.yml` (pins the new OSS commit in the private cloud repo), and `docs.yml` (builds `web/docs` on PRs, deploys it to the `beardrive-docs` Cloudflare Pages project — docs.beardrive.ai — on pushes to main; see `web/docs/README.md`). Releases run `goreleaser release` on a tagged commit (see `.goreleaser.yaml`); the version is injected via `-ldflags "-X main.version=..."` into `cmd/bdrive/main.go`. The frontend's built assets are **committed** (go:embed needs them in the module), so `go build` and `go install` never require Node — but a release tag must not ship a stale `internal/webapp/static`: run `frontend/check-dist.sh` first.
When testing the CLI manually, set `BDRIVE_HOME=/some/tmp/dir` to relocate all beardrive state (device identity, mount registry, volume stores) away from the real `~/.bdrive`.
+13
View File
@@ -117,6 +117,19 @@ to type.
Static output in `dist/`. Any static host works; build command `npm run build`,
output directory `dist`, project root `web/docs`.
Today that host is **Cloudflare Pages**, project `beardrive-docs`
(`docs.beardrive.ai`), deployed by `.github/workflows/docs.yml` on every push to
`main` that touches `web/docs/**` or the token source. It needs two repository
secrets: `CLOUDFLARE_API_TOKEN` (an account token with *Cloudflare Pages: Edit*)
and `CLOUDFLARE_ACCOUNT_ID`.
Not the Pages **Git integration**, deliberately: it clones shallow, which costs
every `<lastmod>` (see "Checkout depth"), and it would build the docs on commits
that cannot change them. The Pages project is a direct-upload project — nothing
deploys it but this workflow. That is also the failure mode to watch for: when
deploys were manual, they simply stopped, and the site sat three weeks stale
while every check that only runs *during* a deploy stayed green.
### Redirects
The docs were reorganized around the agent-first path, so three old URLs moved: