name: CI # Enforces the contribution rules documented in CLAUDE.md. # Formatting and the convention checker are scoped to the change's diff so that # grandfathered legacy code is not punished; build/test run module-wide, and # go vet runs module-wide but advisory-only (it never fails CI on legacy code). on: push: branches: ["**"] pull_request: permissions: contents: read jobs: verify: runs-on: ubuntu-latest defaults: run: working-directory: src steps: - name: Checkout (full history for diffing) uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version: "1.24" cache-dependency-path: src/go.sum - name: Resolve diff base id: base working-directory: ${{ github.workspace }} run: | if [ "${{ github.event_name }}" = "pull_request" ]; then base="${{ github.event.pull_request.base.sha }}" else base="${{ github.event.before }}" fi # Use the base only if it is a real commit present in this checkout. # Push events can report a github.event.before that was force-pushed # away or never fetched (git then errors "fatal: bad object"); fall # back to HEAD's parent, and finally to empty so the diff-scoped steps # below skip gracefully instead of crashing the job under `bash -e`. if ! git rev-parse --verify --quiet "${base}^{commit}" >/dev/null 2>&1; then base="$(git rev-parse --verify --quiet 'HEAD~1^{commit}' 2>/dev/null || true)" fi echo "sha=$base" >> "$GITHUB_OUTPUT" echo "Diff base: ${base:-}" - name: Convention checker (rules 1-5, diff only) if: steps.base.outputs.sha != '' working-directory: ${{ github.workspace }} run: sh scripts/check-conventions.sh --diff "${{ steps.base.outputs.sha }}" - name: gofmt (changed Go files) if: steps.base.outputs.sha != '' working-directory: ${{ github.workspace }} run: | files=$(git diff --name-only --diff-filter=ACM "${{ steps.base.outputs.sha }}" -- '*.go' || true) [ -z "$files" ] && { echo "No Go files changed."; exit 0; } unformatted=$(gofmt -l $files) if [ -n "$unformatted" ]; then echo "These files are not gofmt-clean:"; echo "$unformatted" echo "Run: gofmt -w "; exit 1 fi - name: go vet (advisory — never blocks CI) # Module-wide vet surfaces pre-existing issues in grandfathered legacy # code that this project intentionally does not modify, so it is run for # visibility only and never fails the build. Enforcement of the # contribution rules on NEW code is handled by the diff-scoped # convention checker above. continue-on-error: true run: go vet ./... - name: go build (all packages, native) run: go build ./... - name: Cross-compile smoke test (portability, rule 5) # Mirrors the release targets in the Makefile: the shipped binary must # build for other OSes from a single, dependency-free codebase. run: | GOOS=windows GOARCH=amd64 go build -o /dev/null . GOOS=darwin GOARCH=arm64 go build -o /dev/null . GOOS=linux GOARCH=arm GOARM=6 go build -o /dev/null . - name: go test run: go test -count=1 ./...