docs(16-06): complete image hygiene CI assertions plan

This commit is contained in:
Lucas Berger
2026-06-13 08:28:37 -04:00
parent 27046dbf92
commit 9a108a3618
3 changed files with 116 additions and 11 deletions
@@ -0,0 +1,105 @@
---
phase: 16-ci-dependency-audit-and-security-checks
plan: "06"
subsystem: ci
tags: [ci, docker, image-hygiene, boot-smoke, security, publish]
dependency_graph:
requires: ["16-01", "16-04"]
provides: ["IMG-03"]
affects: [".gitea/workflows/publish.yml"]
tech_stack:
added: []
patterns:
- "Static assertion step: grep-based structural checks in shell before docker push"
- "Boot-smoke: docker run with forbidden env combo + timeout + exit-code semantics"
key_files:
created: []
modified:
- .gitea/workflows/publish.yml
decisions:
- "D-10 (16-06): Static assertions grep for 7 forbidden .dockerignore patterns + --target production pin; grep is substring-safe (apps/api/scripts matches apps/api/scripts/seed-credential.mjs)"
- "D-10 (16-06): Boot-smoke treats EXIT==0 and EXIT==124 as failures; any other non-zero is a PASS — covers the D-08 guard's process.exit(1) path"
- "D-10 (16-06): Both hygiene steps ordered strictly after build and before push; set -euo pipefail ensures any failure stops the job before push runs"
metrics:
duration: "1 minute"
completed: "2026-06-13"
tasks_completed: 2
files_modified: 1
requirements: [IMG-03]
---
# Phase 16 Plan 06: Image Hygiene CI Assertions Summary
**One-liner:** Publish-time CI assertions that block docker push when .dockerignore is incomplete, --target production is dropped, or the production image tolerates DEV_AUTH_BYPASS=true (D-10 / IMG-03).
## What Was Built
Two CI assertion steps added to `.gitea/workflows/publish.yml`, inserted strictly between the `Build production image` step and the `Push image` step:
**Step 1 — "Image hygiene — static assertions"** (`set -euo pipefail`):
- Fails if `.dockerignore` is absent
- Loops over 7 forbidden patterns (`.env`, `node_modules`, `apps/api/scripts`, `.git`, `.planning`, `apps/api/tests`, `apps/pwa/e2e`) and fails if any is missing from `.dockerignore`
- Fails if `--target production` is no longer grep-able in `publish.yml` itself
- Catches config drift that would ship secrets or dev files (T-16-19)
**Step 2 — "Image hygiene — boot-smoke (must refuse dev-bypass in production)"** (`set -euo pipefail`):
- Runs the freshly-built `sha_tag` image with `NODE_ENV=production DEV_AUTH_BYPASS=true`
- `timeout 15` caps the run (T-16-21: prevents the job hanging if the guard does not fire)
- EXIT==0 → image started → guard NOT working → FAIL
- EXIT==124 → timeout → guard not firing → FAIL
- Any other non-zero exit → image refused boot → PASS
- Proves `assertNotDevBypassInProduction()` (16-01 / D-08) fires in the actual shipped image (T-16-18)
The step order in the publish job is now:
1. Checkout
2. Compute image tags
3. Docker login
4. Build production image
5. Image hygiene — static assertions ← new
6. Image hygiene — boot-smoke ← new
7. Push image
8. Docker logout (if: always())
## Task Commits
| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | Split publish build and push into separate steps | bc00f3e | .gitea/workflows/publish.yml |
| 2 | Add static image-hygiene assertion + boot-smoke before push | 27046db | .gitea/workflows/publish.yml |
## Deviations from Plan
None — plan executed exactly as written.
## Verification
- YAML parses cleanly (python3 yaml.safe_load confirmed)
- Step ordering verified: assertions at indices 4-5, push at index 6 (between Build at 3 and Push at 6)
- All 7 forbidden .dockerignore patterns confirmed present in .dockerignore
- `--target production` grep confirmed to match within publish.yml
- Dockerfile `ENV NODE_ENV=production` confirmed in production stage (line 45) — precondition for the smoke to be meaningful
- `bootGuards.ts` `process.exit(1)` confirmed fires when `NODE_ENV=production AND DEV_AUTH_BYPASS=true` — that is what the smoke exercises
## Threat Coverage
| Threat ID | Description | Mitigated By |
|-----------|-------------|--------------|
| T-16-18 | Regressed image that tolerates DEV_AUTH_BYPASS=true published | Boot-smoke step: fails push if image starts under forbidden combo |
| T-16-19 | Future change removes .dockerignore or drops --target production | Static assertions step: fails push on any missing pattern or pin |
| T-16-20 | Assertions run after push (bad image already published) | Ordering: both steps are before Push image step |
| T-16-21 | Boot-smoke hangs if guard does not fire | `timeout 15`; EXIT==124 treated as failure |
## Known Stubs
None.
## Self-Check
Files modified:
- `.gitea/workflows/publish.yml` — modified (confirmed by git log)
Commits:
- `bc00f3e` — ci(16-06): split publish build and push into separate steps
- `27046db` — ci(16-06): add static image-hygiene assertion + boot-smoke before push
## Self-Check: PASSED