diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index f44795f..366813d 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -101,6 +101,19 @@ jobs: build-web: name: Build prerendered web + # The prerender fetches VITE_API_BASE at build time, so this job bakes + # whatever the api is serving when it runs. Without these dependencies it + # raced the api/worker deploys and the crawler snapshot silently disagreed + # with the live api for a whole day, until the nightly refresh: it won the + # race once (a visibility fix reached both together) and lost it once (an + # api field the snapshot then lacked). deploy-worker is in the list too, + # not just deploy-api — the worker owns migrations, so a schema change + # isn't applied until it has restarted. + # + # The cost is a serial deploy: binaries -> api/worker -> web. Worth it; + # the alternative is a snapshot whose correctness depends on which runner + # finished first. + needs: [deploy-api, deploy-worker] runs-on: fedora-44 steps: - uses: actions/checkout@v4 diff --git a/CLAUDE.md b/CLAUDE.md index 81c4c2e..b81f296 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -119,6 +119,13 @@ CI-driven via **Gitea Actions** (`.gitea/workflows/`), the source of infra truth prerendered web bundle, then deploy each component over SSH as the `gitea_ci` user with scoped sudo (`asset/sudoers.d/`). Services run under systemd with hardened units; the api/worker reach postgres over mTLS using the host cert. + The job graph is deliberately serial — binaries → api/worker → web — because + the prerender fetches `VITE_API_BASE` at build time and so bakes whatever the + api is serving when `build-web` runs. `build-web` therefore `needs:` both + deploy jobs (the worker owns migrations, so a schema change isn't live until + it restarts). When it ran in parallel instead, whether the crawler snapshot + matched the api came down to which runner finished first, and a lost race + meant a stale snapshot until the nightly refresh. - `refresh.yml` — daily `schedule:` (+ manual): rebuilds and redeploys only the web tier, re-baking the prerendered crawler snapshot from the current gist (CV) and activity API without bouncing the api/worker.