Repository navigation
chore(ci): hadolint pre-commit hook floats to :latest, decoupled from rev: pin #1696
Description
Problem
The hadolint-docker pre-commit hook was reddening main and every open PR (via the pre-commit --all-files CI job, which lints the whole merge ref, not just a PR's diff), including PRs that touch zero Docker files (e.g. #1658).
Root cause: version drift, not new Dockerfile debt
.pre-commit-config.yaml pins rev: v2.14.0 on the hadolint/hadolint repo, but that pin only fixes which commit of hadolint's hook definition (.pre-commit-hooks.yaml) is used. That file declares:
- id: hadolint-docker
entry: ghcr.io/hadolint/hadolint hadolintNo image tag. Docker resolves an untagged reference to :latest, so the actual linter binary floats independently of our rev: pin. Confirmed:
$ docker run --rm ghcr.io/hadolint/hadolint:v2.14.0 hadolint --version
Haskell Dockerfile Linter 2.14.0
$ docker run --rm ghcr.io/hadolint/hadolint:latest hadolint --version
Haskell Dockerfile Linter 2.15.1
Running both versions against the unchanged Dockerfiles (with the repo's .hadolint.yaml ignore list applied, matching what pre-commit actually does):
v2.14.0 (pinned rev): exit 0, no findings
latest / 2.15.1: exit 1
Dockerfile:149 DL3066 Non-numeric user-id may not be resolvable by host system
Dockerfile:165 DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments
Dockerfile.dev:62 DL3066 Non-numeric user-id may not be resolvable by host system
Dockerfile.test:21 DL3066 Non-numeric user-id may not be resolvable by host system
hadolint 2.15.x applies DL3066 to any USER <name> directive (all four are pre-existing, intentional USER cudly/devuser/e2e non-root switches after adduser/addgroup) and now also applies DL3025 to HEALTHCHECK's CMD sub-clause (Dockerfile:165 is the HEALTHCHECK line, not the container's ENTRYPOINT/CMD, which are already in exec/JSON form). Neither Dockerfile line changed; the git history on all three Dockerfiles predates today, and .pre-commit-config.yaml's hadolint rev: hasn't moved since 2026-02-20. Only the untagged upstream image moved.
Fix
Replace the external hadolint-docker hook with a local hook definition pinned to the immutable digest of the same v2.14.0 image (ghcr.io/hadolint/hadolint:v2.14.0@sha256:27086352fd5e1907ea2b934eb1023f217c5ae087992eb59fde121dce9c9ff21e), so the linter version can only change via an explicit bump in this repo, not silently via an upstream :latest retag. No Dockerfile changes were needed or made.
Blast radius
Blocked every open PR's Run pre-commit hooks CI job (whole-merge-ref lint), regardless of whether the PR touched Docker files.
Activity
fix(ci): pin hadolint image by digest to stop drift off the rev pin (#…
fix(ci): pin hadolint image by digest to stop drift off the rev pin (#…