Check the golangci-lint version in bootstrap, not just presence (closes #24) #34

Merged
clawbot merged 2 commits from bootstrap-version-check into main 2026-08-09 08:38:36 +02:00
2 changed files with 72 additions and 5 deletions
Showing only changes of commit 9e924721e6 - Show all commits

23
TODO.md
View File

@@ -29,6 +29,29 @@
# Completed Steps # Completed Steps
- check the installed golangci-lint version in `script/bootstrap`
instead of only its presence (2026-08-09, branch
`bootstrap-version-check`, closes #24): `missing golangci-lint` meant
any linter already on `PATH` satisfied the check, so the pin was never
consulted and the v2.12.2 bump from #3 was inert on every host that
already had one — this host ran v2.10.1 against a v2.12.2 pin,
`make check` went green, and `make docker` then rejected the same
commit with findings the local gate never saw. The version now lives
in one place, `GOLANGCI_LINT_VERSION`, with the `go install` module
ref derived from it so a bump cannot half-apply; a
`golangci_lint_version` helper parses `golangci-lint --version`
(taking the field after the word `version` and tolerating an optional
leading `v`, which the module ref carries and the binary's output does
not), and any version that is not the pin — older, newer, absent or
unparseable — is reinstalled. `git`, `make` and `go` keep their
presence-only checks and now say why in a comment: they are host
package-manager tools the repo deliberately does not pin, with
`go.mod` governing the language version and the digest-pinned images
covering reproducible builds. Verified on this host by bootstrapping
from v2.10.1 to v2.12.2 and running it again to a no-op, plus stub
runs under `dash` covering the absent, older, newer, image-style and
leading-`v` cases; `make check` and `make lint` are clean at v2.12.2,
so v2.10.1 was not hiding any findings on `main`
- unwind the hash worker pool on the error path (2026-08-09, branch - unwind the hash worker pool on the error path (2026-08-09, branch
`hash-pool-cleanup`, closes #6): `hashPhase` used to return the `hash-pool-cleanup`, closes #6): `hashPhase` used to return the
moment `recordRun` failed and abandon the pool — the feeder parked moment `recordRun` failed and abandon the pool — the feeder parked

View File

@@ -4,14 +4,20 @@
# installed tools are skipped. Base tooling comes from nix, apt, brew, # installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present. # or apk (detected in that order); assumes nothing is present.
# golangci-lint is installed via `go install` pinned to the same version # golangci-lint is installed via `go install` pinned to the same version
# the Dockerfile lint stage uses (never "latest"). # the Dockerfile lint stage uses (never "latest"), and is reinstalled
# whenever the installed version differs from that pin.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Pinned versions, 2026-08-07 (same version as the Dockerfile lint stage). # Pinned versions, 2026-08-07 (same version as the Dockerfile lint stage).
# golangci-lint v2.12.2 # This is the single source of truth for the linter version: the module
GOLANGCI_LINT_REF="github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2" # ref below and the version comparison in main() are both derived from
# it, so a bump here cannot half-apply. Written without a leading "v",
# the way `golangci-lint --version` reports it.
GOLANGCI_LINT_VERSION="2.12.2"
GOLANGCI_LINT_MODULE="github.com/golangci/golangci-lint/v2/cmd/golangci-lint"
GOLANGCI_LINT_REF="$GOLANGCI_LINT_MODULE@v$GOLANGCI_LINT_VERSION"
PKGMGR="" PKGMGR=""
SUDO="" SUDO=""
@@ -60,16 +66,54 @@ missing() {
! command -v "$1" >/dev/null 2>&1 ! command -v "$1" >/dev/null 2>&1
} }
# Echo the installed golangci-lint version, or nothing when the tool is
# absent. The binary reports e.g.
# golangci-lint has version 2.12.2 built with go1.26.5 from abc1234 ...
# so the version is the field after the literal word "version", and it
# carries no leading "v" (the module ref does). Some builds do print a
# leading "v", so strip one if present and compare bare versions.
golangci_lint_version() {
command -v golangci-lint >/dev/null 2>&1 || return 0
golangci-lint --version 2>/dev/null | awk '
{
for (i = 1; i < NF; i++) {
if ($i == "version") {
v = $(i + 1)
sub(/^v/, "", v)
print v
exit
}
}
}
'
}
main() { main() {
cd "$ROOT" cd "$ROOT"
# System tooling, deliberately unpinned: these come from the host
# package manager and whatever version it ships is what the host
# gets, so a presence check is the right check. The repo pins no
# system toolchain versions — the Go language version is governed by
# go.mod, and builds that must be reproducible run in the Docker
# image, whose base images are pinned by digest.
if missing git; then pkg_install git git git git; fi if missing git; then pkg_install git git git git; fi
if missing make; then pkg_install gnumake make make make; fi if missing make; then pkg_install gnumake make make make; fi
if missing go; then pkg_install go golang go go; fi if missing go; then pkg_install go golang go go; fi
# Lint tooling, pinned via go install (installs into # Lint tooling, pinned via go install (installs into
# "$(go env GOPATH)/bin"; ensure that is on your PATH). # "$(go env GOPATH)/bin"; ensure that is on your PATH). Unlike the
if missing golangci-lint; then go install "$GOLANGCI_LINT_REF"; fi # system tools above this is version-checked, not presence-checked:
# the Dockerfile lint stage runs a digest-pinned linter, so a host
# running any other version lints against different rules and
# `make check` can go green on a commit CI then rejects. Any version
# that is not the pin — older or newer — is reinstalled.
installed="$(golangci_lint_version)"
if [ "$installed" != "$GOLANGCI_LINT_VERSION" ]; then
echo "bootstrap: golangci-lint ${installed:-absent or unparseable}," \
"want $GOLANGCI_LINT_VERSION; installing"
go install "$GOLANGCI_LINT_REF"
fi
go mod download go mod download