docs: document the analysis page and the CI deploy flow

The RUNBOOK still said the workflow did not deploy to the cluster. It does
now - it builds, pushes, applies the manifests, rolls the pods and verifies
the app from inside the pod. The manual deploy path is documented alongside
it, along with the dry-run step that catches the schema errors kubectl apply
silently swallows.
This commit is contained in:
2026-11-09 13:12:51 +10:00
parent e1a9a7384f
commit 4d1e070d74
2 changed files with 63 additions and 4 deletions
+22 -1
View File
@@ -42,6 +42,7 @@ The tests use `mongomock`, so they need no database and no network.
| `/brews` | The brew process: record a reading, review every run | | `/brews` | The brew process: record a reading, review every run |
| `/active` | What is in the fermenter right now — tap, gravity, projected strength | | `/active` | What is in the fermenter right now — tap, gravity, projected strength |
| `/history` | How every brew has gone, with charts and the standard-vs-legacy ABV comparison | | `/history` | How every brew has gone, with charts and the standard-vs-legacy ABV comparison |
| `/analysis` | How consistently the brews ferment — attenuation, fermentation curves, brew timings |
### `/active` — the active beers page ### `/active` — the active beers page
@@ -59,6 +60,27 @@ A run with only one reading and no recorded original gravity has no reliable
estimate, so the alcohol figure shows a dash rather than a misleading number. estimate, so the alcohol figure shows a dash rather than a misleading number.
Record the OG on the run (or take a second reading) and it appears. Record the OG on the run (or take a second reading) and it appears.
### `/analysis` — how the brews ferment
The page that turns the history into something you can brew better from:
- **Attenuation by brew** — what fraction of the original gravity was
fermented away, for every finished brew.
- **Fermentation curves** — the typical shape of a fermentation, drawn as a
median curve with the min–max envelope behind it. Plotting every brew
individually gives sixteen near-identical lines and an unreadable legend.
- **Every finished brew** — OG, FG, ABV, attenuation, days in the fermenter,
days to reach 1.010, and the finish date.
- **Readings that need a second look** — readings outside the plausible
hydrometer range.
- Headline figures: average attenuation and its spread, average brew time,
average time to 1.010.
A reading that is physically impossible — a final gravity below the original
gravity by more than the wort itself could have — is excluded from the
averages rather than silently distorting them, and the run is listed on the
page so it can be corrected.
### Run metadata ### Run metadata
Some details cannot be derived from the readings, so they are stored per run in Some details cannot be derived from the readings, so they are stored per run in
@@ -94,7 +116,6 @@ src/flask/
views/ blueprints: auth, brews, analysis views/ blueprints: auth, brews, analysis
mongo/repository.py Mongo access for the beer_data and beer_runs collections mongo/repository.py Mongo access for the beer_data and beer_runs collections
templates/ Jinja templates templates/ Jinja templates
static/ CSS, JS, fonts, logo (all served locally — no CDN)
static/ CSS, JS, fonts, logo (all served locally - no CDN) static/ CSS, JS, fonts, logo (all served locally - no CDN)
tests/ pytest suite tests/ pytest suite
scripts/ operational scripts (the run-metadata backfill) scripts/ operational scripts (the run-metadata backfill)
+41 -3
View File
@@ -69,15 +69,53 @@ env:
## Deploying ## Deploying
Pushing to `master` triggers `.gitea/workflows/build_push.yml`, which runs the Pushing to `master` triggers `.gitea/workflows/build_push.yml`, which builds
test suite and then builds and pushes the image. **The workflow does not the image, pushes it, **deploys it to the cluster and verifies it**. There is
currently deploy to the cluster** — apply the manifests yourself: no manual step.
The workflow is a single job, in this order:
1. **Checkout**
2. **Create Kubeconfig** — writes `secrets.KUBEC_CONFIG_BUILDX_NEW_2` to
`$HOME/.kube/config`. This is FIRST because the buildx `kubernetes` driver
needs it to create its builder pod in the cluster.
3. **Set up Docker Buildx** — `driver: kubernetes`, `namespace=gitea-runner`.
4. **Login to the registry** — `secrets.REG_PASSWORD`.
5. **Build and Push** — `linux/amd64` only. A multi-arch build runs qemu,
which crashes the buildkit pod on this NUC (exit 255, `SandboxChanged`)
and the build then hangs for many minutes.
6. **Deploy** — installs kubectl, applies `kube/*.yaml`, creates the
image-pull secret idempotently, then `rollout restart` + `rollout status`.
The rollout restart is required: the image tag in the manifest is
`:latest`, so `kubectl apply` alone does not roll the pods.
7. **Verify the deployed app** — exercises `/healthz` and `/readyz` from
inside the pod, using the pod's own python (there is no `curl` in the
slim image).
> The deploy step does **not** delete the namespace. That would destroy the
> hand-provisioned `beer-db` and `beer-app` Secrets.
### Deploying by hand
If you need to deploy without going through CI — for example to test a
manifest change — do the same thing the workflow does:
```bash ```bash
kubectl apply -f kube/beer_namespace.yaml
kubectl apply -f kube/beer-deployment.yaml
kubectl apply -f kube/beer-service.yaml
kubectl -n beer rollout restart deployment/beer-deployment kubectl -n beer rollout restart deployment/beer-deployment
kubectl -n beer rollout status deployment/beer-deployment --timeout=180s kubectl -n beer rollout status deployment/beer-deployment --timeout=180s
``` ```
Validate the manifests before applying them. `kubectl apply` **silently
rejects** a manifest with a schema error, so a broken manifest leaves the
old Deployment in place while the job still reports success:
```bash
kubectl apply --dry-run=client -f kube/
```
### Verify the deploy actually took ### Verify the deploy actually took
CI green and pods `Ready` do **not** prove the new code is running. Check the CI green and pods `Ready` do **not** prove the new code is running. Check the