Skip to content

chore(ci): hadolint pre-commit hook floats to :latest, decoupled from rev: pin #1696

Description

@cristim

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 hadolint

No 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions