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)
4.2 KiB
4.2 KiB
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
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
# 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 failed2— 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§Phase E - ADR-0001:
0001-docker-build-lockfile-policy.md - Sibling script:
learning_ai_common_plat/scripts/gitea/doctor.sh