diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58b335ef6..b63b364fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,36 @@ jobs: go-version-file: go.mod cache: true + - name: Install Make on Windows + if: matrix.os == 'windows-latest' + shell: pwsh + run: choco install make --no-progress -y + + - name: Validate quality targets with stock macOS Make + if: matrix.os == 'macos-latest' + env: + GIT_CONFIG_COUNT: 1 + GIT_CONFIG_KEY_0: grep.patternType + GIT_CONFIG_VALUE_0: fixed + run: make -n vulncheck + + - name: Check vulnerabilities through native Windows Make + if: matrix.os == 'windows-latest' + shell: cmd + run: make vulncheck + + - name: Check dead code through native Windows Make + if: matrix.os == 'windows-latest' + continue-on-error: true + shell: cmd + run: make deadcode + + - name: Run static lint through native Windows Make + if: matrix.os == 'windows-latest' + continue-on-error: true + shell: cmd + run: make lint-static + - name: Check formatting if: matrix.os == 'ubuntu-latest' shell: bash @@ -100,27 +130,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 +151,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 cdf9fe4f6..b8d974730 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,10 +51,8 @@ request, or completing an implementation task: or run focused tests with `-race`, when concurrency is affected. 4. **Build**: `go run ./cmd/zero-release build`. 5. **Smoke test**: `go run ./cmd/zero-release smoke`. -6. **Advisory lint**: - `go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 run --enable-only unused,ineffassign,staticcheck ./...`. -7. **Security**: - `go run golang.org/x/vuln/cmd/govulncheck@v1.3.0 ./...`. +6. **Advisory lint**: `make lint-static`. +7. **Security**: `make vulncheck`. 8. **Diff hygiene**: `git diff HEAD --check` (covers staged and unstaged tracked changes). diff --git a/Makefile b/Makefile index 4e9734450..6ef9cc560 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,13 @@ -# Zero build/test/lint targets. AGENTS.md says "Build with `make`" and "Run `make -# lint` before opening a PR" — these targets back those instructions. +# Zero build/test/lint targets. AGENTS.md says to build and run quality checks +# with `make` — these targets back those instructions. .DEFAULT_GOAL := build -.PHONY: build build-all test test-race vet fmt fmt-check lint tidy clean baseline help +GO_VERSION = $(word 2,$(shell git grep -G -h "^go[[:space:]]" -- go.mod)) +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 baseline help # Build the main CLI binary into ./zero. build: @@ -33,6 +39,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. Read this module's go directive directly, without invoking +# the possibly stale Go toolchain or consulting a multi-module GOWORK. git grep +# works with both POSIX shells and cmd.exe, including GNU Make 3.81 on macOS. +# The target-specific export is shell-independent. +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 @@ -55,4 +77,4 @@ baseline: build --output internal/perfbench/reports/baseline.json help: - @echo "Targets: build (default), build-all, test, test-quick, vet, fmt, fmt-check, lint, tidy, clean, baseline" + @echo "Targets: build (default), build-all, test, test-quick, vet, fmt, fmt-check, lint, lint-static, deadcode, vulncheck, tidy, clean, baseline" diff --git a/README.md b/README.md index 3188bac74..e23500d54 100644 --- a/README.md +++ b/README.md @@ -366,22 +366,16 @@ variable unset and set. ### 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: - -```bash -# Install golangci-lint -go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 - -# Install govulncheck -go install golang.org/x/vuln/cmd/govulncheck@v1.3.0 -``` +Use the repository-managed targets rather than globally installed binaries: +the targets apply the module's required Go toolchain as well as the reviewed, +pinned tool versions. ### Cross-Compile Examples