Static linter for Dockerfile + docker-compose + .npmrc.docker drift. Sibling to gitea-doctor. Codifies all 15 invariants from Phase A of the docker-build-optimization-roadmap so regressions are caught at PR time, not at build time. Verified against both pilots: - learning_ai_clock: PASS (1 expected warning) - learning_ai_peakpulse: PASS (1 expected warning, pnpm-lock per ADR-0001) - learning_ai_notes (un-migrated control): FAIL with 6 specific findings Refs: docker-build-optimization-roadmap.md \xc2\xa7Phase E (E1, E5)
70 lines
4.2 KiB
Markdown
70 lines
4.2 KiB
Markdown
# docker-doctor — Static linter for Docker build hygiene
|
|
|
|
Sibling to `gitea-doctor` (`scripts/gitea/doctor.sh`). Detects Dockerfile, compose, and
|
|
`.npmrc.docker` drift from the invariants established by
|
|
[`docker-build-optimization-roadmap.md`](../../../learning_ai_devops_tools/docs/docker-build-optimization-roadmap.md)
|
|
Phase A.
|
|
|
|
## When to run
|
|
|
|
- **Before every Docker build** (alongside `gitea-doctor`)
|
|
- **In CI** on PRs touching `Dockerfile`, `docker-compose*.yml`, `.dockerignore`, `.npmrc.docker`
|
|
- **In pre-commit hook** (warning-only at first, error after stabilization)
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# Canonical (run from common-plat)
|
|
bash scripts/docker-doctor.sh --repo /path/to/repo
|
|
|
|
# Per-repo wrapper (preferred)
|
|
bash scripts/docker-doctor.sh
|
|
|
|
# CI / scripting
|
|
bash scripts/docker-doctor.sh --quiet # only print failures
|
|
bash scripts/docker-doctor.sh --warn-only # always exit 0
|
|
```
|
|
|
|
## Checks performed
|
|
|
|
| # | Check | Severity | Roadmap ref |
|
|
| --- | --------------------------------------------------------------------------------------------- | -------- | ----------------- |
|
|
| 1 | `.npmrc.docker` uses `${GITEA_NPM_HOST}` placeholder | Error | F4 |
|
|
| 2 | `.npmrc.docker` uses `${GITEA_NPM_OWNER}` placeholder | Error | F14 |
|
|
| 3 | `.npmrc.docker` uses `${GITEA_NPM_TOKEN}` for `_authToken` | Error | F4 |
|
|
| 4 | `.gitignore` covers `.docker-deps/*` | Error | B3 |
|
|
| 5 | `.gitignore` covers `*.bak` | Warn | B3 |
|
|
| 6 | Dockerfile has `# syntax=docker/dockerfile:1.7` directive | Warn | A2 |
|
|
| 7 | Dockerfile base image is approved (`node:22-alpine`/`-slim` or `${BASE_IMAGE}`) | Error | canonical |
|
|
| 8 | Dockerfile uses corepack (no `npm install -g pnpm`) | Error | A1 |
|
|
| 9 | If Dockerfile COPYs `.npmrc.docker`, it declares `ARG GITEA_NPM_OWNER` + `ARG GITEA_NPM_HOST` | Error | F14 |
|
|
| 10 | `.docker-deps/` COPY uses wildcard `COPY .docker-deps* ...` | Error | A5-2, B3 |
|
|
| 11 | Web Dockerfile uses glob `COPY web/*.{json,ts,...}` (not enumerated configs) | Error | F11, F13 |
|
|
| 12 | Compose healthcheck uses `127.0.0.1`, not `localhost` | Error | F12 |
|
|
| 13 | Compose healthcheck has `start_period` | Warn | A9-3 |
|
|
| 14 | Compose passes `GITEA_NPM_OWNER` build arg | Warn | F14 |
|
|
| 15 | `.dockerignore` does NOT exclude `pnpm-lock.yaml` | Warn | F1 (per ADR-0001) |
|
|
|
|
## Exit codes
|
|
|
|
- `0` — all checks pass (warnings allowed)
|
|
- `1` — one or more error-level checks failed
|
|
- `2` — bad invocation / repo not found
|
|
|
|
## Sibling: gitea-doctor
|
|
|
|
`docker-doctor` and `gitea-doctor` are intentionally separate:
|
|
|
|
| Tool | Scope | When to run |
|
|
| --------------- | -------------------------------------------- | -------------------------------- |
|
|
| `gitea-doctor` | runtime env, token, registry HTTP 200 | Before every build / deploy |
|
|
| `docker-doctor` | static analysis of Dockerfile + compose YAML | On every PR touching those files |
|
|
|
|
Run both via `make doctor` in repos that have it wired up.
|
|
|
|
## Related
|
|
|
|
- Roadmap: [`docker-build-optimization-roadmap.md`](../../../learning_ai_devops_tools/docs/docker-build-optimization-roadmap.md) §Phase E
|
|
- ADR-0001: [`0001-docker-build-lockfile-policy.md`](../../../learning_ai_devops_tools/docs/adr/0001-docker-build-lockfile-policy.md)
|
|
- Sibling script: `learning_ai_common_plat/scripts/gitea/doctor.sh`
|