Dockerfile lint-stage pin comment is misleading and inconsistently formatted #25

Open
opened 2026-08-09 03:58:25 +02:00 by clawbot · 2 comments
Collaborator

Two cosmetic-but-real defects on the lint stage, raised as non-blocking findings during the review of PR #2.

1. The comment says something untrue. Dockerfile:2 reads:

# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-07

REPO_POLICIES requires the form # image:vX.Y.Z, YYYY-MM-DD above every hash-pinned reference. The (Debian-based) parenthetical breaks that form, and worse, it implies the v2.12.2 bump changed the base distribution. It did not — the reviewer ran both the old and new digests and confirmed v2.12.1 was Debian trixie too. A comment that explains a change that never happened is worse than no comment.

2. The pin form is inconsistent with its siblings. Dockerfile:3 uses FROM golangci/golangci-lint:v2.12.2@sha256:..., while the builder and runtime stages both use the bare FROM image@sha256:... form, which is also what the policy template shows. Security-neutral — the digest governs either way — but three FROM lines in one file should not use two conventions.

Definition of done

  1. Dockerfile:2 reads exactly # golangci/golangci-lint:v2.12.2, 2026-08-07.
  2. Dockerfile:3 uses the bare FROM golangci/golangci-lint@sha256:... form, matching the other two stages and the policy template. The digest is unchanged: sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240.
  3. The version/date comments above the golang and alpine pins are checked against the same required format while in there.
  4. make check green and make docker succeeds, still building the lint stage from the same digest.
Two cosmetic-but-real defects on the lint stage, raised as non-blocking findings during the review of PR #2. **1. The comment says something untrue.** `Dockerfile:2` reads: # golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-07 REPO_POLICIES requires the form `# image:vX.Y.Z, YYYY-MM-DD` above every hash-pinned reference. The `(Debian-based)` parenthetical breaks that form, and worse, it implies the v2.12.2 bump changed the base distribution. It did not — the reviewer ran both the old and new digests and confirmed v2.12.1 was Debian trixie too. A comment that explains a change that never happened is worse than no comment. **2. The pin form is inconsistent with its siblings.** `Dockerfile:3` uses `FROM golangci/golangci-lint:v2.12.2@sha256:...`, while the builder and runtime stages both use the bare `FROM image@sha256:...` form, which is also what the policy template shows. Security-neutral — the digest governs either way — but three `FROM` lines in one file should not use two conventions. ## Definition of done 1. `Dockerfile:2` reads exactly `# golangci/golangci-lint:v2.12.2, 2026-08-07`. 2. `Dockerfile:3` uses the bare `FROM golangci/golangci-lint@sha256:...` form, matching the other two stages and the policy template. The digest is unchanged: `sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`. 3. The version/date comments above the `golang` and `alpine` pins are checked against the same required format while in there. 4. `make check` green and `make docker` succeeds, still building the lint stage from the same digest.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:58:25 +02:00
Author
Collaborator

Still live after #46 (d43c1d3),
and now doubled: Dockerfile.lint carries the same
(Debian-based) parenthetical and the same tag-plus-digest FROM
form. Both files are in scope.

Implementation requirements:

  • Dockerfile:2 and the corresponding comment in Dockerfile.lint
    read exactly # golangci/golangci-lint:v2.12.2, 2026-08-07.
  • Both FROM lines use the bare FROM golangci/golangci-lint@sha256:...
    form. Digest unchanged:
    sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240.
  • Check the golang and alpine pin comments against the same format
    while in there.

The trap: script/verify-lint-image-pin exists to keep those two
FROM lines identical and parses them to do it. Dropping the tag
changes what it parses. Read it first, keep it working, and prove it
still fails on a tag-only and a digest-only disagreement after the
change — a guard that silently stops comparing is worse than the
inconsistency being fixed.

make check green and make docker still building both images from
the same digest.

Still live after https://git.eeqj.de/sneak/sfdupes/issues/46 (`d43c1d3`), and now doubled: `Dockerfile.lint` carries the same `(Debian-based)` parenthetical and the same tag-plus-digest `FROM` form. Both files are in scope. Implementation requirements: - `Dockerfile:2` and the corresponding comment in `Dockerfile.lint` read exactly `# golangci/golangci-lint:v2.12.2, 2026-08-07`. - Both `FROM` lines use the bare `FROM golangci/golangci-lint@sha256:...` form. Digest unchanged: `sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`. - Check the `golang` and `alpine` pin comments against the same format while in there. The trap: `script/verify-lint-image-pin` exists to keep those two `FROM` lines identical and parses them to do it. Dropping the tag changes what it parses. Read it first, keep it working, and prove it still fails on a tag-only and a digest-only disagreement after the change — a guard that silently stops comparing is worse than the inconsistency being fixed. `make check` green and `make docker` still building both images from the same digest.
Author
Collaborator

Done on next as 337b319.

Both files now read # golangci/golangci-lint:v2.12.2, 2026-08-07 over
FROM golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240.
Digest unchanged. The golang and alpine pin comments already matched
the required form; untouched.

Guard (script/verify-lint-image-pin): its awk matches
golangci/golangci-lint[:@], so the tagless reference still parses — no
logic change was needed. Evidence, all after the change:

  • agreeing pins: exits 0 and prints the shared reference; both builds
    ran the gate live, not cached (make check, Dockerfile.lint:
    gate lint-image-pin ... agree on ...@sha256:5cce...; make docker,
    lint stage [7/9], same output).
  • digest-only disagreement: exits 1 with both refs printed, and fails
    the build — perturbing Dockerfile to a zero digest aborted
    docker build -f Dockerfile.lint . at [6/8]
    gate lint-image-pin with exit code: 1. Perturbation reverted.
  • tag-only disagreement: this case no longer exists, since neither
    reference carries a tag. Tag drift is still caught: a tag
    reintroduced on one side only is a plain string mismatch and fails
    (verified against fixtures), as do two differing tags if tags ever
    return.
  • no vacuous pass: an unparseable reference is a hard failure, not an
    empty-vs-empty compare. Mangling the image name gives
    expected exactly one golangci/golangci-lint FROM reference, found 0
    and exit 1, whether one file or both are mangled.

One deviation, disclosed rather than folded in: the guard's advice line
said "bump both FROM lines together, tag and digest", which is now
inaccurate; it reads "bump both FROM lines together so ...". Message
text only, no logic.

Verification: make check green with the linter demonstrably executing
(gate lint 36.2s, 0 issues., not CACHED; tests
ok sneak.berlin/go/sfdupes 2.427s). make docker green, both images
built from that same digest, lint gates 35.7s, builder gate test 32.3s.
Not run: any behaviour, ordering or gate change; nothing outside the two
Dockerfiles, that one message line, and the TODO.md entry.

Done on `next` as `337b319`. Both files now read `# golangci/golangci-lint:v2.12.2, 2026-08-07` over `FROM golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`. Digest unchanged. The `golang` and `alpine` pin comments already matched the required form; untouched. Guard (`script/verify-lint-image-pin`): its awk matches `golangci/golangci-lint[:@]`, so the tagless reference still parses — no logic change was needed. Evidence, all after the change: - agreeing pins: exits 0 and prints the shared reference; both builds ran the gate live, not cached (`make check`, `Dockerfile.lint`: `gate lint-image-pin` ... `agree on ...@sha256:5cce...`; `make docker`, lint stage `[7/9]`, same output). - digest-only disagreement: exits 1 with both refs printed, and fails the build — perturbing `Dockerfile` to a zero digest aborted `docker build -f Dockerfile.lint .` at `[6/8]` `gate lint-image-pin` with `exit code: 1`. Perturbation reverted. - tag-only disagreement: this case no longer exists, since neither reference carries a tag. Tag drift is still caught: a tag reintroduced on one side only is a plain string mismatch and fails (verified against fixtures), as do two differing tags if tags ever return. - no vacuous pass: an unparseable reference is a hard failure, not an empty-vs-empty compare. Mangling the image name gives `expected exactly one golangci/golangci-lint FROM reference, found 0` and exit 1, whether one file or both are mangled. One deviation, disclosed rather than folded in: the guard's advice line said "bump both FROM lines together, tag and digest", which is now inaccurate; it reads "bump both FROM lines together so ...". Message text only, no logic. Verification: `make check` green with the linter demonstrably executing (`gate lint` 36.2s, `0 issues.`, not `CACHED`; tests `ok sneak.berlin/go/sfdupes 2.427s`). `make docker` green, both images built from that same digest, lint gates 35.7s, builder `gate test` 32.3s. Not run: any behaviour, ordering or gate change; nothing outside the two Dockerfiles, that one message line, and the `TODO.md` entry.
Sign in to join this conversation.