Repository navigation
Go Coverage (Publish) #1008
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: Go Coverage (Publish) | |
| # Publishes the coverage comment produced by go-test.yml's go-test job, from a workflow_run | |
| # trigger rather than a second job in that same pull_request-triggered workflow. | |
| # | |
| # Why: go-test.yml is a `pull_request` workflow, so its *entire file* - every job, every | |
| # permission - is read from the PR's own branch. A same-repo PR (this repo doesn't accept | |
| # forks, so that's the only kind we get) can edit that file to add steps to any job in it, | |
| # including a "publish" job, and have them run with whatever permissions that job requests. | |
| # Splitting into two jobs in one file only stops PR-controlled *test code* from reaching a | |
| # write-scoped token in the same job - it does nothing against a PR editing the workflow | |
| # definition itself. | |
| # | |
| # workflow_run is what actually fixes this: this file's definition is always read from the | |
| # default branch, never the PR branch, no matter what the PR changes. It runs after go-test.yml | |
| # completes, downloads that run's artifact by run ID, and does the actual publishing here, | |
| # where the PR can't alter it. | |
| on: # zizmor: ignore[dangerous-triggers] no PR code is checked out or executed here - see the | |
| # comments above and in the job below: only trusted actions run, against artifacts and | |
| # metadata from the completed run, never the PR's own workflow-controlled checkout. | |
| workflow_run: | |
| workflows: ["Go Unit Tests"] | |
| types: | |
| - completed | |
| permissions: {} | |
| jobs: | |
| publish-coverage: | |
| name: Publish coverage report | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event.workflow_run.event == 'pull_request' && | |
| (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure') | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| actions: read | |
| steps: | |
| # Belt-and-braces alongside go-test.yml's concurrency group: that cancels most | |
| # superseded runs before they finish, but doesn't cover every case (e.g. a manual | |
| # re-run of an old workflow run). Recheck the PR's live head SHA here so an | |
| # out-of-order completion can't overwrite a newer, correct coverage comment with a | |
| # stale one. | |
| - name: Check PR head is still current | |
| id: freshness | |
| if: github.event.workflow_run.pull_requests[0] != null | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} | |
| RUN_SHA: ${{ github.event.workflow_run.head_sha }} | |
| run: | | |
| set -ex | |
| current_sha=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}" --jq .head.sha) | |
| if [ "$current_sha" != "$RUN_SHA" ]; then | |
| echo "::notice::Skipping publish: PR #${PR_NUMBER} head is now ${current_sha}, this run tested ${RUN_SHA} (superseded by a newer push)." | |
| echo "current=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "current=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download coverage reports | |
| if: steps.freshness.outputs.current == 'true' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: go-coverage | |
| path: coverage | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Code Coverage Report | |
| if: steps.freshness.outputs.current == 'true' | |
| # irongut/CodeCoverageSummary's action.yml resolves to docker://ghcr.io/irongut/ | |
| # codecoveragesummary:v1.3.0 even when the action is pinned to a commit SHA - that's | |
| # a mutable tag, so the SHA pin doesn't actually pin what runs with pull-requests: | |
| # write. Invoke the image by digest directly instead of going through the action. | |
| # --hidebranch: Go coverage profiles don't record branch data (gocover-cobertura | |
| # always emits branches-covered=0/branches-valid=0/branch-rate=0), so there's nothing | |
| # meaningful to show there. | |
| run: | | |
| set -ex | |
| docker run --rm \ | |
| -v "${GITHUB_WORKSPACE}:/github/workspace" \ | |
| -w /github/workspace \ | |
| ghcr.io/irongut/codecoveragesummary@sha256:daebdede906ca84788b94378a1504a88d38621198d3836df24d60d6215e64a86 \ | |
| --files "coverage/*.xml" \ | |
| --badge true \ | |
| --fail false \ | |
| --format markdown \ | |
| --hidebranch true \ | |
| --hidecomplexity true \ | |
| --indicators true \ | |
| --output both \ | |
| --thresholds "60 80" | |
| - name: Add Coverage PR Comment | |
| if: steps.freshness.outputs.current == 'true' | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 | |
| with: | |
| number: ${{ github.event.workflow_run.pull_requests[0].number }} | |
| header: go-coverage | |
| recreate: true | |
| path: code-coverage-results.md |