diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58b335ef..9b2ef7a6 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 0b912611..ece21acf 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 91fa72bf..0d15a069 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,20 @@ # 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 +# Lazily expanded (=) so `go list -m` only runs when a tool target below +# 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 +GOLANGCI_LINT_VERSION := v2.12.2 +GOVULNCHECK_VERSION := v1.3.0 + +.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 +46,22 @@ 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. +# 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: + go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --enable-only unused,ineffassign,staticcheck ./... + +deadcode: + go run golang.org/x/tools/cmd/deadcode@$(DEADCODE_VERSION) -test=false ./... + +vulncheck: + go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) ./... + tidy: go mod tidy @@ -41,4 +70,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" diff --git a/README.md b/README.md index 15a881fb..dd383ae1 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: