| name: Go Verification |
|
|
| on: |
| push: |
| branches: [main] |
| paths-ignore: |
| - '**.md' |
| - 'docs/**' |
| - 'LICENSE' |
| - '.gitignore' |
| - 'skill/**' |
| - 'plugin/**' |
| pull_request: |
| branches: [main] |
| paths-ignore: |
| - '**.md' |
| - 'docs/**' |
| - 'LICENSE' |
| - '.gitignore' |
| - 'skill/**' |
| - 'plugin/**' |
|
|
| env: |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true |
|
|
| permissions: |
| contents: read |
|
|
| jobs: |
| |
| changes: |
| name: Detect Changes |
| runs-on: ubuntu-latest |
| if: github.event.pull_request.draft == false |
| outputs: |
| go: ${{ steps.filter.outputs.go }} |
| steps: |
| - uses: actions/checkout@v5 |
| - uses: dorny/paths-filter@v3 |
| id: filter |
| with: |
| filters: | |
| go: |
| - '**.go' |
| - 'go.mod' |
| - 'go.sum' |
| - 'cmd/**' |
| - 'internal/**' |
| |
| build: |
| name: Build & Vet |
| needs: changes |
| runs-on: ubuntu-latest |
| steps: |
| - name: Check for Go changes |
| id: check |
| run: | |
| if [ "${{ needs.changes.outputs.go }}" = "true" ]; then |
| echo "Go files detected" |
| echo "has_go_changes=true" >> $GITHUB_OUTPUT |
| else |
| echo "⏭️ No Go files changed - docs/workflow-only changes detected" |
| echo "has_go_changes=false" >> $GITHUB_OUTPUT |
| fi |
| |
| - uses: actions/checkout@v5 |
|
|
| - uses: actions/setup-go@v6 |
| if: steps.check.outputs.has_go_changes == 'true' |
| with: |
| go-version: '1.26' |
|
|
| - name: Download deps |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: go mod download |
|
|
| - name: Format check |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: | |
| unformatted=$(gofmt -l .) |
| if [ -n "$unformatted" ]; then |
| echo "Files not formatted:" |
| echo "$unformatted" |
| exit 1 |
| fi |
| |
| - name: Vet |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: go vet ./... |
|
|
| - name: Build |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: go build -o pinchtab ./cmd/pinchtab |
|
|
| - name: Test with coverage |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: go test ./... -v -count=1 -coverprofile=coverage.out -covermode=atomic |
|
|
| - name: Coverage summary |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: | |
| coverage=$(go tool cover -func=coverage.out | tail -1) |
| echo "### Coverage" >> $GITHUB_STEP_SUMMARY |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY |
| echo "$coverage" >> $GITHUB_STEP_SUMMARY |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY |
| |
| lint: |
| name: Lint |
| needs: changes |
| runs-on: ubuntu-latest |
| steps: |
| - name: Check for Go changes |
| id: check |
| run: | |
| if [ "${{ needs.changes.outputs.go }}" = "true" ]; then |
| echo "has_go_changes=true" >> $GITHUB_OUTPUT |
| else |
| echo "⏭️ No Go files changed, skipping lint" |
| echo "has_go_changes=false" >> $GITHUB_OUTPUT |
| fi |
| |
| - uses: actions/checkout@v5 |
|
|
| - uses: actions/setup-go@v6 |
| if: steps.check.outputs.has_go_changes == 'true' |
| with: |
| go-version: '1.26' |
|
|
| - uses: oven-sh/setup-bun@v2 |
| if: needs.changes.outputs.go == 'true' |
| with: |
| bun-version: latest |
|
|
| - name: Install tygo |
| if: needs.changes.outputs.go == 'true' |
| run: go install github.com/gzuidhof/tygo@latest |
|
|
| - name: Build dashboard |
| if: needs.changes.outputs.go == 'true' |
| run: | |
| cd dashboard && bun install --frozen-lockfile && cd .. |
| ./scripts/build-dashboard.sh |
| |
| - name: golangci-lint |
| if: steps.check.outputs.has_go_changes == 'true' |
| uses: golangci/golangci-lint-action@v7 |
| with: |
| version: v2.9.0 |
|
|
| security: |
| name: Security Scan |
| needs: changes |
| runs-on: ubuntu-latest |
| steps: |
| - name: Check for Go changes |
| id: check |
| run: | |
| if [ "${{ needs.changes.outputs.go }}" = "true" ]; then |
| echo "has_go_changes=true" >> $GITHUB_OUTPUT |
| else |
| echo "⏭️ No Go files changed, skipping security scan" |
| echo "has_go_changes=false" >> $GITHUB_OUTPUT |
| fi |
| |
| - uses: actions/checkout@v5 |
|
|
| - uses: actions/setup-go@v6 |
| if: steps.check.outputs.has_go_changes == 'true' |
| with: |
| go-version: '1.26' |
|
|
| - name: Install gosec |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: go install github.com/securego/gosec/v2/cmd/gosec@latest |
|
|
| - name: Run gosec |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: gosec -exclude=G301,G302,G304,G306,G404,G107,G115,G703,G704,G705,G706 -fmt=json -out=gosec-results.json ./... || true |
|
|
| - name: Check critical findings |
| if: steps.check.outputs.has_go_changes == 'true' |
| run: | |
| # Fail on G112 (Slowloris), G204 (command injection) |
| ISSUES=$(cat gosec-results.json | jq '[.Issues[] | select(.rule_id == "G112" or .rule_id == "G204")] | length') |
| echo "Critical issues: $ISSUES" |
| cat gosec-results.json | jq '.Stats' |
| if [ "$ISSUES" -gt 0 ]; then |
| cat gosec-results.json | jq '.Issues[] | select(.rule_id == "G112" or .rule_id == "G204")' |
| exit 1 |
| fi |
| |