chore: add MIT license #30
Workflow file for this run
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 | |
| # Runs on every pull request targeting main and on pushes to main (so | |
| # the branch-protection status check has a baseline history once it's | |
| # enabled). The coverage gate fails the job if aggregate line coverage | |
| # across all packages drops below 90%. | |
| # | |
| # After a green push to main, a second job refreshes the shields.io | |
| # endpoint JSON used by the coverage badge in README.md. It publishes | |
| # to a dedicated `badges` branch so that branch-protection on main | |
| # remains intact — the bot never writes to main directly. | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| # Reads go 1.X from go.mod; keeps CI in lockstep with the | |
| # toolchain the repo declares rather than hard-coding a | |
| # version here. | |
| go-version-file: 'go.mod' | |
| # setup-go's default cache key hashes only go.sum. When two | |
| # PRs in a row share a go.sum but touch local source (PR B | |
| # didn't bump go.sum after PR A), the second PR's build can | |
| # restore stale instrumented test binaries from the first | |
| # PR's cache. The resulting -coverpkg=./... profile ends up | |
| # with mixed-version cover blocks for the same source file | |
| # (e.g. printHelp ending at line 125 in three packages and | |
| # line 127 in six), which `go tool cover -func` treats as | |
| # two distinct blocks and drops the function's coverage to | |
| # ~33%. This bit us in #8: 90.7% locally vs 65% on CI on | |
| # the same commit. Keeping cache off until we have a key | |
| # that hashes source files too. | |
| cache: false | |
| - name: Download modules | |
| run: go mod download | |
| - name: Vet | |
| run: go vet ./... | |
| - name: Build | |
| run: go build ./... | |
| - name: Test with coverage | |
| # -coverpkg=./... aggregates hits from every package into the | |
| # same profile so the total % matches what `go tool cover -func` | |
| # prints locally. -race catches data races in our HTTP/goroutine | |
| # paths (server, signal handling). | |
| run: go test -race -coverpkg=./... -coverprofile=coverage.out ./... | |
| - name: Enforce 90% coverage | |
| run: | | |
| set -euo pipefail | |
| total=$(go tool cover -func=coverage.out | awk '/^total:/ {print $3}' | tr -d '%') | |
| echo "Aggregate coverage: ${total}%" | |
| awk -v t="$total" 'BEGIN { if (t + 0 < 90) { print "FAIL: coverage " t "% is below the 90% threshold"; exit 1 } }' | |
| - name: Upload coverage profile | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage | |
| path: coverage.out | |
| retention-days: 14 | |
| # Publish the coverage percentage to a JSON file on the `badges` branch | |
| # so shields.io can render a live badge in README.md. Only runs after a | |
| # green push to main. The badges branch is an orphan, containing only | |
| # the endpoint JSON — nothing merges back into main. | |
| badge: | |
| needs: test | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: coverage | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: 'go.mod' | |
| - name: Compose shields.io endpoint JSON | |
| id: badge | |
| run: | | |
| set -euo pipefail | |
| total=$(go tool cover -func=coverage.out | awk '/^total:/ {print $3}' | tr -d '%') | |
| int=$(printf '%.0f' "$total") | |
| color=red | |
| if [ "$int" -ge 80 ]; then color=yellow; fi | |
| if [ "$int" -ge 90 ]; then color=brightgreen; fi | |
| if [ "$int" -ge 95 ]; then color=green; fi | |
| printf '{"schemaVersion":1,"label":"coverage","message":"%s%%","color":"%s"}\n' "$total" "$color" > /tmp/coverage.json | |
| echo "pct=${total}" >> "$GITHUB_OUTPUT" | |
| - name: Publish to `badges` branch | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| # Either check out an existing `badges` branch or create it | |
| # as an orphan. The orphan contents are scrubbed so the branch | |
| # never accumulates anything beyond what we publish. | |
| if git ls-remote --exit-code --heads origin badges > /dev/null 2>&1; then | |
| git fetch origin badges | |
| git checkout badges | |
| else | |
| git checkout --orphan badges | |
| git rm -rf . > /dev/null 2>&1 || true | |
| fi | |
| cp /tmp/coverage.json coverage.json | |
| git add coverage.json | |
| if git diff --cached --quiet; then | |
| echo "Badge JSON unchanged" | |
| else | |
| git commit -m "chore: coverage badge → ${{ steps.badge.outputs.pct }}%" | |
| git push origin badges | |
| fi |