golangci-lint cache can report findings against paths that do not exist #36
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A third way a gate in this repo can tell you something other than the truth about your working tree — and the first one that produces false findings rather than a false green.
Observed by the independent reviewer of #34. Their first
make checkreported ten Go findings against paths under../sfdupes-rework-34/— a worktree that had already been deleted and did not exist on the machine. Setting a freshGOLANGCI_LINT_CACHEand re-running gave0 issues.The mechanism: golangci-lint caches analysis results keyed on file content. Agents and reviewers here work in throwaway worktrees, so the same file content appears at many different paths over time. A cache entry created under one worktree path gets served for byte-identical content under another, and the stale path travels with it into the report.
Why it matters beyond the confusing paths. This repo now has three independent ways for a gate to lie, and reviewers have hit all three:
make dockercan be a total layer-cache hit that executes nothing and exits 0. Confirmed live three separate times.The failure mode here is the least dangerous of the three, because it produces noise rather than false confidence. But it costs review time, and a reviewer who cannot reproduce a finding may reasonably conclude the finding is spurious when the next one is real.
Definition of done
script/linthandles the cache. Options: pointGOLANGCI_LINT_CACHEat a repo-local, gitignored directory so it cannot be polluted by other checkouts; or leave the shared cache and document the failure mode plus the one-line remedy. A repo-local cache is the cleaner answer and costs only a cold first run..dockerignore, andmake cleanremoves it.make checkbeyond the 20-second budget on a warm cache.make checkandmake dockergreen, the latter demonstrably not a cache hit (#32).Also fix, from the same review of #34, three small robustness gaps in the
script/bootstrapverification that landed in #24 — none of them able to produce a false pass, all of them worth closing:GOPATHyields a malformed directory in the diagnostic (/a:/b/bin). Cosmetic; it still fails loudly. Take the first element.golangci-lint --versionexit status is ignored, so a binary that prints the pinned version and then exits non-zero is treated as a match and skipped. Check the status.timeout(1)— stock macOS, andbrewis in the package-manager detection list, so macOS is a supported host class — a wedged linter hangs bootstrap unbounded. Worst case with the timeout present is 60 seconds across the two bounded reads, which is acceptable; unbounded is not.Dissolved by #46, merged as
d43c1d3. Closing — every subject of this issue is gone from the treerather than fixed.
The contamination mechanism was a golangci-lint cache shared across
host checkouts. The linter now runs only inside
Dockerfile.lint,where
WORKDIRis always/src, no cache directory is mounted in, andCHECK_EPOCHinvalidates the gate layer on every run — so each runstarts with an empty analysis cache and no path from any other
checkout can be served into it. Corroborated by the measured cost: the
lint step takes 27-45s on every run, which is a cold analysis, not a
warm-cache hit.
DoD items 1-4 (repo-local cache directory,
.gitignore/.dockerignoreentries,
make clean, the 20-second warm-cache budget) have no subjectnow that no cache is reused, and the owner has waived lint caching.
The three
script/bootstraprobustness gaps appended here — multi-elementGOPATH, unchecked--versionexit status, unbounded read withouttimeout(1)— are also moot: that whole verification block, includingthe version parser and the pin, was deleted rather than hardened.