From aab69fab1491fcf54827281d77f5d03e3aad23f3 Mon Sep 17 00:00:00 2001 From: PierrunoYT Date: Tue, 14 Jul 2026 22:38:09 +0200 Subject: [PATCH 1/3] fix pinned Go quality toolchain --- .github/workflows/ci.yml | 15 +++------------ AGENTS.md | 4 ++-- Makefile | 27 +++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58b335ef6..9b2ef7a65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,27 +100,18 @@ jobs: go-version-file: go.mod cache: true - # govulncheck, deadcode, and golangci-lint each resolve a toolchain from - # their own modules, which can drop below the version go.mod requires and - # then fail to load our packages. Pin GOTOOLCHAIN to the go.mod toolchain - # so all three run under it. - - name: Pin toolchain from go.mod - run: | - toolchain="$(awk '/^toolchain /{print $2}' go.mod)" - echo "GOTOOLCHAIN=${toolchain:-auto}" >> "$GITHUB_ENV" - # Hard gate: fails the build when code reaches a known vulnerability. A stdlib # CVE is cleared by a toolchain bump (see go.mod). May also flag a newly # published advisory on an unrelated PR — intentional: do not ship known vulns. - name: govulncheck - run: go run golang.org/x/vuln/cmd/govulncheck@v1.3.0 ./... + run: make vulncheck # Advisory: reports functions unreachable from any cmd/* main so dormant # code is visible in CI. Non-blocking while the dormant subsystems are # still being wired or removed. - name: deadcode (advisory) continue-on-error: true - run: go run golang.org/x/tools/cmd/deadcode@v0.46.0 -test=false ./... + run: make deadcode # Advisory: catches what deadcode's whole-program reachability analysis # doesn't, unused private functions/assignments reachable within a @@ -130,4 +121,4 @@ jobs: # the repo are cleaned up incrementally (see #527). - name: golangci-lint (advisory) continue-on-error: true - run: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 run --enable-only unused,ineffassign,staticcheck ./... + run: make lint-static diff --git a/AGENTS.md b/AGENTS.md index 0b912611a..ece21acfd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ This file outlines the project conventions and repository guidelines for coding If you are an AI coding agent executing tasks in this repository, you **MUST** run all Go code quality and security checks before committing code or completing your task: 1. **Format & Vet**: Run `go fmt ./...` and `go vet ./...`. -2. **Lint**: Run `go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 run --enable-only unused,ineffassign,staticcheck ./...`. -3. **Security**: Run `go run golang.org/x/vuln/cmd/govulncheck@v1.3.0 ./...`. +2. **Lint**: Run `make lint-static`. +3. **Security**: Run `make vulncheck`. If any check fails or cannot be run, do not ignore it. Prompt the user for instructions or setup assistance before proceeding. diff --git a/Makefile b/Makefile index 91fa72bf6..1ff380a63 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,19 @@ # Zero build/test/lint targets. AGENTS.md says "Build with `make`" and "Run `make # lint` before opening a PR" — these targets back those instructions. .DEFAULT_GOAL := build -.PHONY: build build-all test test-race vet fmt fmt-check lint tidy clean help +GO_VERSION := $(shell go list -m -f "{{.GoVersion}}") +GO_TOOLCHAIN := go$(GO_VERSION) +DEADCODE_VERSION := v0.46.0 +GOLANGCI_LINT_VERSION := v2.12.2 +GOVULNCHECK_VERSION := v1.3.0 + +ifeq ($(OS),Windows_NT) +TOOLCHAIN_ENV := set "GOTOOLCHAIN=$(GO_TOOLCHAIN)" && +else +TOOLCHAIN_ENV := GOTOOLCHAIN=$(GO_TOOLCHAIN) +endif + +.PHONY: build build-all test test-race vet fmt fmt-check lint lint-static deadcode vulncheck tidy clean help # Build the main CLI binary into ./zero. build: @@ -33,6 +45,17 @@ fmt-check: # Lint = formatting check + vet (no extra tooling required). lint: fmt-check vet +# Versioned tools select the toolchain from their own modules when invoked with +# package@version. Pin them to this module's Go version so they can load it. +lint-static: + $(TOOLCHAIN_ENV) go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... + +deadcode: + $(TOOLCHAIN_ENV) go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... + +vulncheck: + $(TOOLCHAIN_ENV) go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... + tidy: go mod tidy @@ -41,4 +64,4 @@ clean: go clean ./... help: - @echo "Targets: build (default), build-all, test, test-quick, vet, fmt, fmt-check, lint, tidy, clean" + @echo "Targets: build (default), build-all, test, test-quick, vet, fmt, fmt-check, lint, lint-static, deadcode, vulncheck, tidy, clean" From 6ebd0bbf2a062207e45c194749bb4dfcd17a6b83 Mon Sep 17 00:00:00 2001 From: PierrunoYT Date: Tue, 14 Jul 2026 23:17:56 +0200 Subject: [PATCH 2/3] fix(make): use a POSIX GOTOOLCHAIN prefix for pinned tool targets The Windows_NT branch used cmd.exe syntax (set "..." &&), but GNU Make runs recipes through sh even on Windows (MSYS/Git Bash), where that form fails and breaks make lint-static/deadcode/vulncheck. A plain GOTOOLCHAIN=... prefix works everywhere, so the OS check is gone. Also switch GO_VERSION/GO_TOOLCHAIN to recursive expansion so 'go list -m' only runs when a tool target actually needs it, instead of at parse time for every target. Addresses PR review from CodeRabbit and Copilot. Co-Authored-By: Claude Fable 5 --- Makefile | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 1ff380a63..e03a8b86a 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,16 @@ # Zero build/test/lint targets. AGENTS.md says "Build with `make`" and "Run `make # lint` before opening a PR" — these targets back those instructions. .DEFAULT_GOAL := build -GO_VERSION := $(shell go list -m -f "{{.GoVersion}}") -GO_TOOLCHAIN := go$(GO_VERSION) +# Lazily expanded (=) so `go list -m` only runs when a tool target below +# actually uses the toolchain, not at parse time for every target. Recipes use +# a plain POSIX `GOTOOLCHAIN=...` prefix: GNU Make runs recipes through sh even +# on Windows (MSYS/Git Bash), so no cmd.exe-specific form is needed. +GO_VERSION = $(shell go list -m -f "{{.GoVersion}}") +GO_TOOLCHAIN = go$(GO_VERSION) DEADCODE_VERSION := v0.46.0 GOLANGCI_LINT_VERSION := v2.12.2 GOVULNCHECK_VERSION := v1.3.0 -ifeq ($(OS),Windows_NT) -TOOLCHAIN_ENV := set "GOTOOLCHAIN=$(GO_TOOLCHAIN)" && -else -TOOLCHAIN_ENV := GOTOOLCHAIN=$(GO_TOOLCHAIN) -endif - .PHONY: build build-all test test-race vet fmt fmt-check lint lint-static deadcode vulncheck tidy clean help # Build the main CLI binary into ./zero. @@ -48,13 +46,13 @@ lint: fmt-check vet # Versioned tools select the toolchain from their own modules when invoked with # package@version. Pin them to this module's Go version so they can load it. lint-static: - $(TOOLCHAIN_ENV) go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... + GOTOOLCHAIN=$(GO_TOOLCHAIN) go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... deadcode: - $(TOOLCHAIN_ENV) go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... + GOTOOLCHAIN=$(GO_TOOLCHAIN) go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... vulncheck: - $(TOOLCHAIN_ENV) go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... + GOTOOLCHAIN=$(GO_TOOLCHAIN) go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... tidy: go mod tidy From 2c84a8eb636289dbce2cb045693adee8d6417c2d Mon Sep 17 00:00:00 2001 From: PierrunoYT Date: Wed, 15 Jul 2026 04:44:58 +0200 Subject: [PATCH 3/3] fix(build): make pinned-tool targets shell-neutral, fix README drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address code review on PR #689: - lint-static/deadcode/vulncheck previously set GOTOOLCHAIN via a POSIX `VAR=value cmd` recipe prefix. GNU Make on Windows runs recipes through whatever SHELL is configured, and under a native cmd.exe setup that syntax is invalid — cmd.exe treats the whole "GOTOOLCHAIN=go1.26.5" token as the program to run and fails before go ever executes (reproduced locally: cmd.exe errors with "'MYVAR' is not recognized..." for the equivalent pattern). Switched to a target-specific `export`, which sets the variable in the child process environment via Make itself, before is invoked — works under sh, cmd.exe, and PowerShell alike (verified against all three targets with SHELL := cmd.exe). - README.md's Code Quality and Security Checks section still told contributors to run the raw `go run ...@version` commands this PR identifies as selecting the wrong (older) toolchain and failing to load this Go 1.26.5 module — only CI and AGENTS.md had been updated to the new make targets. Points steps 3-4 at `make lint-static` / `make vulncheck` instead, matching AGENTS.md. Verified end-to-end: `make vulncheck`, `make deadcode`, and `make lint-static` all run successfully and reach their tools (deadcode's advisory report and lint-static's 35 pre-existing findings match the PR's documented baseline). Co-Authored-By: Claude Sonnet 5 --- Makefile | 20 ++++++++++++++------ README.md | 10 +++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index e03a8b86a..0d15a0692 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,12 @@ # lint` before opening a PR" — these targets back those instructions. .DEFAULT_GOAL := build # Lazily expanded (=) so `go list -m` only runs when a tool target below -# actually uses the toolchain, not at parse time for every target. Recipes use -# a plain POSIX `GOTOOLCHAIN=...` prefix: GNU Make runs recipes through sh even -# on Windows (MSYS/Git Bash), so no cmd.exe-specific form is needed. +# actually uses the toolchain, not at parse time for every target. GOTOOLCHAIN +# is set via Make's own `export` (below, scoped to the pinned-tool targets) +# rather than a POSIX `GOTOOLCHAIN=... cmd` recipe prefix: Make sets exported +# variables in the child process environment itself before invoking $(SHELL), +# so this works under any configured SHELL (sh, cmd.exe, PowerShell) instead +# of only shells that understand inline env-var-assignment syntax. GO_VERSION = $(shell go list -m -f "{{.GoVersion}}") GO_TOOLCHAIN = go$(GO_VERSION) DEADCODE_VERSION := v0.46.0 @@ -45,14 +48,19 @@ lint: fmt-check vet # Versioned tools select the toolchain from their own modules when invoked with # package@version. Pin them to this module's Go version so they can load it. +# The target-specific `export` sets GOTOOLCHAIN in these three targets' recipe +# environment only (not build/test/vet/etc.), via Make itself rather than +# shell syntax — see the GO_TOOLCHAIN comment above. +lint-static deadcode vulncheck: export GOTOOLCHAIN = $(GO_TOOLCHAIN) + lint-static: - GOTOOLCHAIN=$(GO_TOOLCHAIN) go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... + go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... deadcode: - GOTOOLCHAIN=$(GO_TOOLCHAIN) go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... + go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... vulncheck: - GOTOOLCHAIN=$(GO_TOOLCHAIN) go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... + go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... tidy: go mod tidy diff --git a/README.md b/README.md index 15a881fbb..dd383ae19 100644 --- a/README.md +++ b/README.md @@ -351,12 +351,12 @@ go run ./cmd/zero-perf-bench ### Code Quality and Security Checks -Before committing any changes, ensure all Go code quality and security checks pass. Pinned `go run` commands matching CI constraints can be used directly without prior installation: +Before committing any changes, ensure all Go code quality and security checks pass. The `make` targets below pin each tool to this module's Go version, so they load correctly even when your default `go` toolchain is older — running the plain `go run ...@version` form yourself can select the tool module's own (older) toolchain and fail to load this module instead. -1. **Formatting**: Run `go fmt ./...` (or `make fmt`). -2. **Vetting**: Run `go vet ./...` (or `make vet`). -3. **Linting**: Run `go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 run --enable-only unused,ineffassign,staticcheck ./...`. -4. **Vulnerability Scan**: Run `go run golang.org/x/vuln/cmd/govulncheck@v1.3.0 ./...`. +1. **Formatting**: Run `make fmt` (or `go fmt ./...`). +2. **Vetting**: Run `make vet` (or `go vet ./...`). +3. **Linting**: Run `make lint-static`. +4. **Vulnerability Scan**: Run `make vulncheck`. If you prefer to install these tools globally on your path, you can run: