@@ -50,26 +50,44 @@ This is intentional and load-bearing. Several things depend on it:
5050
5151### Rollout workflow
5252
53- ** Merge to ` main ` → redeploy dev only:**
53+ Application code is ** baked into the image** (` COPY . /app ` in the ` Dockerfile ` ); pods no
54+ longer ` git clone ` at startup. ` docker.yml ` builds on every push to ` main ` and on ` vX.Y.Z `
55+ release tags. The image is the unit of release.
56+
57+ ** Tags CI produces:**
58+ - ` :main ` — moving; rebuilt on every push to ` main ` and by the weekly cron. ** dev** tracks this.
59+ - ` :<git-sha> ` — immutable; one per commit.
60+ - ` :vX.Y.Z ` — immutable; built on release tags. ** prod** pins this (by digest, below).
61+
62+ ** Merge to ` main ` → redeploy dev:**
5463```
5564kubectl apply -f k8s/dev-deployment.yaml
5665kubectl rollout restart deployment/dev-duckdb-mcp -n biodiversity
5766```
58-
59- ** Tag a release → redeploy prod:**
67+ dev pins ` :main ` with ` imagePullPolicy: Always ` , so the restart pulls the freshest build.
68+ ** Wait for the ` docker.yml ` run on your merge to go green first** — rolling before the
69+ image is pushed gives ` ImagePullBackOff ` .
70+
71+ ** Tag a release → redeploy prod (promote by digest):**
72+ 1 . ` git tag vX.Y.Z && git push origin vX.Y.Z ` , then wait for ` docker.yml ` to build ` :vX.Y.Z ` .
73+ 2 . Read the digest from the build run's job summary, or:
74+ ` docker buildx imagetools inspect ghcr.io/boettiger-lab/mcp-data-server:vX.Y.Z --format '{{.Manifest.Digest}}' `
75+ 3 . Set ` image: ghcr.io/boettiger-lab/mcp-data-server:vX.Y.Z@sha256:<digest> ` in
76+ ` k8s/deployment.yaml ` (separate commit).
77+ 4 . ` kubectl apply -f k8s/deployment.yaml `
78+ 5 . ` kubectl rollout restart deployment/duckdb-mcp -n biodiversity `
79+
80+ prod pins an immutable ` :vX.Y.Z@sha256:… ` (tag for humans, digest enforced — if they ever
81+ disagree, the digest wins). ** Never apply prod while the manifest points at an image CI
82+ hasn't built yet** — the rollout stalls on ` ImagePullBackOff ` . ` kubectl apply ` must precede
83+ ` rollout restart ` ; a git push alone does not update the cluster.
84+
85+ Verify all prod replicas converge on a single digest after rollout:
6086```
61- kubectl apply -f k8s/deployment.yaml
62- kubectl rollout restart deployment/duckdb-mcp -n biodiversity
87+ kubectl -n biodiversity get pods -l app=duckdb-mcp \
88+ -o custom-columns='NAME:.metadata.name,IMAGE:.status.containerStatuses[0].imageID'
6389```
6490
65- Prod clones a pinned tag, so a release is two steps: ** first** bump the ` --branch vX.Y.Z `
66- pin in ` k8s/deployment.yaml ` to the new tag (separate commit, as in #151 ), ** then** apply.
67- Never ` kubectl apply -f k8s/deployment.yaml ` to prod while the pin still points at the
68- previous tag — ` deployment.yaml ` changes (e.g. new ` /healthz ` probes) can reference code
69- the pinned tag doesn't yet have, and the rollout will stall on failing probes.
70-
71- ` kubectl apply ` must precede ` rollout restart ` — a git push alone does not update the cluster.
72-
7391---
7492
7593This project uses two distinct, asynchronous AI agent processes. Do not confuse them.
0 commit comments