Repository navigation
config: add JSON path output (#369) #526
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - '.github/*.md' | |
| pull_request: | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - '.github/*.md' | |
| workflow_call: | |
| # Cancel in-progress runs for the same branch/PR to avoid wasted resources. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| continue-on-error: ${{ matrix.os == 'macos-latest' }} | |
| # Skip push-triggered runs from automated release commits. The release | |
| # workflow already calls ci.yml via workflow_call — an independent | |
| # push-triggered run is a duplicate that races the release's concurrency | |
| # group and can cancel or conflict with the in-flight release CI step. | |
| if: >- | |
| github.event_name != 'push' || | |
| github.actor != 'github-actions[bot]' | |
| env: | |
| CGO_ENABLED: "0" # Match release build settings | |
| steps: | |
| # Pin actions to full SHA for supply-chain security. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: "1.26.5" | |
| - name: Check formatting (gofmt) | |
| if: runner.os == 'Linux' | |
| run: | | |
| unformatted=$(gofmt -l .) | |
| if [ -n "$unformatted" ]; then | |
| echo "::error::Files need formatting:" | |
| echo "$unformatted" | |
| exit 1 | |
| fi | |
| - name: Check modules tidy | |
| if: runner.os == 'Linux' | |
| run: | | |
| go mod tidy | |
| git diff --exit-code go.mod go.sum | |
| - name: Verify module integrity | |
| if: runner.os == 'Linux' | |
| run: go mod verify | |
| - name: Check strict formatting (gofumpt) | |
| if: runner.os == 'Linux' | |
| run: | | |
| go install mvdan.cc/gofumpt@v0.10.0 || { echo "gofumpt install failed, skipping"; exit 0; } | |
| unformatted=$(gofumpt -l .) | |
| if [ -n "$unformatted" ]; then | |
| echo "::error::Files need strict formatting (gofumpt):" | |
| echo "$unformatted" | |
| exit 1 | |
| fi | |
| - name: Check dead code | |
| if: runner.os == 'Linux' | |
| run: | | |
| go install golang.org/x/tools/cmd/deadcode@v0.47.0 | |
| go install github.com/magefile/mage@v1.17.2 | |
| mage deadcode | |
| - run: go build ./... | |
| - run: go vet ./... | |
| - name: golangci-lint | |
| if: runner.os == 'Linux' | |
| uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 | |
| with: | |
| version: v2.12.2 | |
| install-mode: goinstall | |
| - name: Test with coverage | |
| if: runner.os == 'Linux' | |
| run: go test -coverprofile=coverage.out -covermode=atomic ./... | |
| - name: Test | |
| if: runner.os != 'Linux' | |
| run: go test ./... | |
| - name: Coverage summary | |
| if: runner.os == 'Linux' | |
| run: go tool cover -func=coverage.out | tail -1 | |
| - name: Coverage threshold check | |
| if: runner.os == 'Linux' | |
| run: | | |
| coverage=$(go tool cover -func=coverage.out | tail -1 | awk '{print $NF}' | tr -d '%') | |
| echo "Total coverage: ${coverage}%" | |
| threshold=75 | |
| if [ "$(echo "$coverage < $threshold" | bc -l)" -eq 1 ]; then | |
| echo "::error::Coverage ${coverage}% is below minimum threshold ${threshold}%" | |
| exit 1 | |
| fi | |
| - name: Upload coverage artifact | |
| if: runner.os == 'Linux' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: coverage-${{ github.sha }} | |
| path: coverage.out | |
| retention-days: 30 | |
| - name: Test (race detector) | |
| if: runner.os == 'Linux' | |
| env: | |
| CGO_ENABLED: "1" | |
| run: go test -race ./... -count=1 | |
| - name: govulncheck | |
| if: runner.os == 'Linux' | |
| run: | | |
| go install golang.org/x/vuln/cmd/govulncheck@v1.5.0 | |
| govulncheck ./... | |
| # Verify the binary compiles for all release platforms. | |
| # CGO_ENABLED=0 means cross-compilation is pure Go — no native runners needed. | |
| cross-compile: | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && !github.event.pull_request.draft | |
| env: | |
| CGO_ENABLED: "0" | |
| steps: | |
| # Pin actions to full SHA for supply-chain security. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: "1.26.5" | |
| - name: Cross-compile | |
| run: | | |
| for platform in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do | |
| os=$(echo $platform | cut -d/ -f1) | |
| arch=$(echo $platform | cut -d/ -f2) | |
| ext="" | |
| if [ "$os" = "windows" ]; then ext=".exe"; fi | |
| echo "Building $os/$arch..." | |
| GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build \ | |
| -ldflags "-X github.com/jongio/grut/internal/config.AppVersion=pr-${{ github.event.pull_request.number }}" \ | |
| -o "dist/grut_${os}_${arch}${ext}" . | |
| done | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: grut-pr-${{ github.event.pull_request.number }} | |
| path: dist/ | |
| retention-days: 7 |