diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8fc5686..a283c63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.23' + go-version-file: go.mod - name: Download dependencies run: go mod download @@ -44,9 +44,10 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.23' + go-version-file: go.mod - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: latest + version: v1.64.8 + args: --config .golangci.yml ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..0ec5bd8 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,14 @@ +run: + timeout: 5m + go: "1.24" + modules-download-mode: readonly + +linters: + disable-all: true + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - unused diff --git a/Makefile b/Makefile index 088be01..1ad8841 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,90 @@ -.PHONY: build test test-verbose test-race coverage coverage-html lint clean deps check install calibrate-providers install-hooks help +.PHONY: build fmt vet test test-verbose test-race coverage coverage-html lint lint-install clean deps check install calibrate-providers install-hooks help # Binary name BINARY=nightshift PKG=./cmd/nightshift +GO=go +GOLANGCI_LINT=golangci-lint +GOLANGCI_LINT_CONFIG=.golangci.yml +GOLANGCI_LINT_VERSION=v1.64.8 +GO_ROOT_BIN=$(shell $(GO) env GOROOT)/bin +GOFMT=$(GO_ROOT_BIN)/gofmt +GO_BIN_OVERRIDE=$(shell $(GO) env GOBIN) +GO_BIN_DIR=$(if $(GO_BIN_OVERRIDE),$(GO_BIN_OVERRIDE),$(shell $(GO) env GOPATH)/bin) +GOLANGCI_LINT_BIN=$(GO_BIN_DIR)/$(GOLANGCI_LINT) # Build the binary build: - go build -o $(BINARY) $(PKG) + $(GO) build -o $(BINARY) $(PKG) # Install the binary to your Go bin directory install: - go install $(PKG) - @echo "Installed $(BINARY) to $$(if [ -n "$$(go env GOBIN)" ]; then go env GOBIN; else echo "$$(go env GOPATH)/bin"; fi)" + $(GO) install $(PKG) + @echo "Installed $(BINARY) to $$(if [ -n "$$($(GO) env GOBIN)" ]; then $(GO) env GOBIN; else echo "$$($(GO) env GOPATH)/bin"; fi)" # Run provider calibration comparison tool calibrate-providers: - go run ./cmd/provider-calibration --repo "$$(pwd)" --codex-originator codex_cli_rs --min-user-turns 2 + $(GO) run ./cmd/provider-calibration --repo "$$(pwd)" --codex-originator codex_cli_rs --min-user-turns 2 + +# Check formatting without rewriting files +fmt: + @GO_FILES="$$(git ls-files -- '*.go')"; \ + if [ -z "$$GO_FILES" ]; then \ + echo "gofmt: no Go files"; \ + else \ + UNFORMATTED="$$( $(GOFMT) -l $$GO_FILES )" || exit 1; \ + if [ -z "$$UNFORMATTED" ]; then \ + echo "gofmt: ok"; \ + else \ + echo "gofmt: run 'gofmt -w' on:"; \ + echo "$$UNFORMATTED"; \ + exit 1; \ + fi; \ + fi + +# Run go vet across the module +vet: + $(GO) vet ./... # Run all tests test: - go test ./... + $(GO) test ./... # Run tests with verbose output test-verbose: - go test -v ./... + $(GO) test -v ./... # Run tests with race detection test-race: - go test -race ./... + $(GO) test -race ./... # Run tests with coverage report coverage: - go test -coverprofile=coverage.out ./... - go tool cover -func=coverage.out + $(GO) test -coverprofile=coverage.out ./... + $(GO) tool cover -func=coverage.out @echo "" @echo "To view HTML coverage report, run: go tool cover -html=coverage.out" # Generate HTML coverage report coverage-html: coverage - go tool cover -html=coverage.out -o coverage.html + $(GO) tool cover -html=coverage.out -o coverage.html @echo "Coverage report generated: coverage.html" -# Run golangci-lint (if installed) +# Run golangci-lint with the checked-in config lint: - @which golangci-lint > /dev/null || (echo "golangci-lint not installed. Run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest" && exit 1) - golangci-lint run + @if command -v $(GOLANGCI_LINT) > /dev/null; then \ + LINT_BIN="$$(command -v $(GOLANGCI_LINT))"; \ + elif [ -x "$(GOLANGCI_LINT_BIN)" ]; then \ + LINT_BIN="$(GOLANGCI_LINT_BIN)"; \ + else \ + echo "golangci-lint not installed. Run: make lint-install"; \ + exit 1; \ + fi; \ + PATH="$(GO_ROOT_BIN):$$PATH" "$$LINT_BIN" run --config $(GOLANGCI_LINT_CONFIG) ./... + +# Install the repo's pinned golangci-lint version +lint-install: + $(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) # Clean build artifacts clean: @@ -54,25 +94,28 @@ clean: # Install development dependencies deps: - go mod download - go mod tidy + $(GO) mod download + $(GO) mod tidy -# Run all checks (test + lint) -check: test lint +# Run the full local verification suite +check: fmt vet test lint # Show help help: @echo "Available targets:" @echo " build - Build the binary" + @echo " fmt - Check gofmt output" + @echo " vet - Run go vet" @echo " test - Run all tests" @echo " test-verbose - Run tests with verbose output" @echo " test-race - Run tests with race detection" @echo " coverage - Run tests with coverage report" @echo " coverage-html - Generate HTML coverage report" @echo " lint - Run golangci-lint" + @echo " lint-install - Install pinned golangci-lint ($(GOLANGCI_LINT_VERSION))" @echo " clean - Clean build artifacts" @echo " deps - Download and tidy dependencies" - @echo " check - Run tests and lint" + @echo " check - Run fmt, vet, tests, and lint" @echo " install - Build and install to Go bin directory" @echo " calibrate-providers - Compare local Claude/Codex session usage for calibration" @echo " install-hooks - Install git pre-commit hook" diff --git a/README.md b/README.md index 84f92cd..4127be3 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,7 @@ Each task has a default cooldown interval to prevent the same task from running ### Pre-commit hooks -Install the git pre-commit hook to catch formatting and vet issues before pushing: +Install the git pre-commit hook to catch the fast local checks before pushing: ```bash make install-hooks @@ -271,6 +271,15 @@ This symlinks `scripts/pre-commit.sh` into `.git/hooks/pre-commit`. The hook run - **go vet** — catches common correctness issues - **go build** — ensures the project compiles +Full linting is intentionally separate so the hook stays quick: + +```bash +make lint-install +make check +``` + +`make lint-install` installs the repo's pinned `golangci-lint` into your Go bin directory, and `make lint`/`make check` will use that copy even if the directory is not on your `PATH`. + To bypass in a pinch: `git commit --no-verify` ## Uninstalling diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index c597d65..5c2c1ec 100755 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -28,9 +28,9 @@ else fi # --- go vet --- +# Keep the hook fast: full golangci-lint still runs via `make lint` and CI. printf " %-20s" "go vet" -VET_OUT=$(go vet ./... 2>&1) -if [[ $? -eq 0 ]]; then +if VET_OUT=$(go vet ./... 2>&1); then echo "✓" PASS=$((PASS+1)) else @@ -41,8 +41,7 @@ fi # --- go build --- printf " %-20s" "go build" -BUILD_OUT=$(go build ./... 2>&1) -if [[ $? -eq 0 ]]; then +if BUILD_OUT=$(go build ./... 2>&1); then echo "✓" PASS=$((PASS+1)) else