Skip to content

Go Coverage (Publish) #1008

Go Coverage (Publish)

Go Coverage (Publish) #1008

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