name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: # Fast check to catch accidental .beads/issues.jsonl changes from contributors check-no-beads-changes: name: Check for .beads changes runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Check for .beads/issues.jsonl changes run: | if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -q "^\.beads/issues\.jsonl$"; then echo "This PR includes changes to .beads/issues.jsonl" echo "" echo "This file is the project's issue database and should not be modified in PRs." echo "" echo "To fix, run:" echo " git checkout origin/main -- .beads/issues.jsonl" echo " git commit --amend" echo " git push --force" echo "" exit 1 fi echo "No .beads/issues.jsonl changes detected" # Verify committed formulas allow build without go:generate # This catches issues where go install @latest would fail check-embedded-formulas: name: Check embedded formulas runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' - name: Build without go:generate run: | # This must succeed with committed formulas only # If this fails, run: go generate ./... && git add -A && git commit go build -v ./cmd/gt - name: Verify formulas are in sync run: | # Regenerate and check for differences go generate ./internal/formula/... if ! git diff --exit-code internal/formula/formulas/; then echo "" echo "ERROR: Committed formulas are out of sync with .beads/formulas/" echo "Run: go generate ./... && git add -A && git commit" exit 1 fi test: name: Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' - name: Configure Git run: | git config --global user.name "CI Bot" git config --global user.email "ci@gastown.test" - name: Build run: go build -v ./cmd/gt - name: Test with Coverage run: | go test -race -short -coverprofile=coverage.out ./... 2>&1 | tee test-output.txt - name: Upload Coverage Data if: github.event_name == 'pull_request' uses: actions/upload-artifact@v4 with: name: coverage-data path: | coverage.out test-output.txt # Separate job to process coverage after ALL tests complete coverage: name: Coverage Report runs-on: ubuntu-latest needs: [test, integration] if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' - name: Download Coverage Data uses: actions/download-artifact@v4 with: name: coverage-data - name: Generate Coverage Report run: | # Parse per-package coverage from test output echo "## Code Coverage Report" > coverage-report.md echo "" >> coverage-report.md # Get overall coverage TOTAL=$(go tool cover -func=coverage.out | grep total | awk '{print $3}') echo "**Overall Coverage: ${TOTAL}**" >> coverage-report.md echo "" >> coverage-report.md # Create per-package table echo "| Package | Coverage |" >> coverage-report.md echo "|---------|----------|" >> coverage-report.md # Extract package coverage from all test output lines grep -E "github.com/steveyegge/gastown.*coverage:" test-output.txt | \ sed 's/.*github.com\/steveyegge\/gastown\///' | \ awk '{ pkg = $1 for (i=2; i<=NF; i++) { if ($i == "coverage:") { cov = $(i+1) break } } printf "| %s | %s |\n", pkg, cov }' | sort -u >> coverage-report.md echo "" >> coverage-report.md echo "---" >> coverage-report.md echo "_Generated by CI_" >> coverage-report.md # Show in logs cat coverage-report.md - name: Upload Coverage Report uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage-report.md retention-days: 30 - name: Comment Coverage on PR # Only for internal PRs - fork PRs can't write comments if: github.event.pull_request.head.repo.full_name == github.repository uses: actions/github-script@v7 with: script: | const fs = require('fs'); const report = fs.readFileSync('coverage-report.md', 'utf8'); // Find existing coverage comment const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, }); const botComment = comments.find(comment => comment.user.type === 'Bot' && comment.body.includes('## Code Coverage Report') ); if (botComment) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: botComment.id, body: report }); } else { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: report }); } - name: Coverage Note for Fork PRs if: github.event.pull_request.head.repo.full_name != github.repository run: | echo "::notice::Coverage report uploaded as artifact (fork PRs cannot post comments). Download from Actions tab." lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: version: latest args: --timeout=5m integration: name: Integration Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' - name: Configure Git run: | git config --global user.name "CI Bot" git config --global user.email "ci@gastown.test" - name: Install beads (bd) # Pin to v0.47.1 - v0.47.2 has routing defaults that cause prefix mismatch errors run: go install github.com/steveyegge/beads/cmd/bd@v0.47.1 - name: Build gt run: go build -v -o gt ./cmd/gt - name: Add to PATH run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH - name: Integration Tests run: go test -tags=integration -timeout=5m -v ./internal/cmd/...