-
Notifications
You must be signed in to change notification settings - Fork 390
143 lines (129 loc) · 5.66 KB
/
Copy pathcoverage-badge.yml
File metadata and controls
143 lines (129 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
name: Coverage Badge
# Updates the README coverage badge gist after a merge to main, without
# re-running the coverage pipeline. The coverage numbers that ship with main
# are the ones computed on the PR's merge commit (coverage.yml runs on every
# PR), so we just locate that PR's coverage-summary artifact and push it to
# the gist.
on:
push:
branches: [main]
workflow_dispatch:
inputs:
run_id:
description: "Coverage Summary run ID to pull artifact from (optional; defaults to latest success on main)"
required: false
type: string
permissions:
contents: read
actions: read
jobs:
update-badge:
runs-on: ubuntu-latest
# Skip release automation commits — they don't change coverage.
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
steps:
- name: Resolve Coverage Summary run ID for this merge
id: resolve
uses: actions/github-script@v7
env:
OVERRIDE_RUN_ID: ${{ github.event.inputs.run_id }}
with:
script: |
const override = process.env.OVERRIDE_RUN_ID;
if (override) {
core.info(`Using override run_id=${override}`);
core.setOutput('run_id', override);
return;
}
// Find the PR associated with the merge commit. Merge commits to
// main are produced by either squash/rebase (one PR) or a merge
// commit (one PR); the associated-PRs endpoint handles all three.
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
});
// Prefer a PR whose base is main and that is closed/merged.
const pr = prs.find(p => p.base && p.base.ref === 'main') || prs[0];
if (!pr) {
core.warning(`No PR associated with ${context.sha}; skipping badge update.`);
core.setOutput('skip', 'true');
return;
}
core.info(`Associated PR #${pr.number} head_sha=${pr.head.sha}`);
// Find the most recent successful Coverage Summary run whose head
// SHA matches the PR's head. That run's artifact reflects coverage
// against main at merge time.
const { data: runs } = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'coverage.yml',
head_sha: pr.head.sha,
status: 'success',
per_page: 10,
});
if (runs.workflow_runs.length === 0) {
core.warning(`No successful Coverage Summary run found for PR #${pr.number} (${pr.head.sha}); skipping badge update.`);
core.setOutput('skip', 'true');
return;
}
const run = runs.workflow_runs[0];
core.info(`Using Coverage Summary run ${run.id} (${run.html_url})`);
core.setOutput('run_id', run.id.toString());
core.setOutput('pr_number', pr.number.toString());
- name: Confirm artifact exists
if: steps.resolve.outputs.skip != 'true'
id: artifact
uses: actions/github-script@v7
env:
RUN_ID: ${{ steps.resolve.outputs.run_id }}
with:
script: |
const run_id = Number(process.env.RUN_ID);
const { data } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id,
});
const artifact = data.artifacts.find(a => a.name === 'coverage-summary');
if (!artifact) {
core.warning(`Run ${run_id} has no coverage-summary artifact (likely a docs-only PR); skipping badge update.`);
core.setOutput('skip', 'true');
return;
}
if (artifact.expired) {
core.warning(`coverage-summary artifact on run ${run_id} has expired; skipping badge update.`);
core.setOutput('skip', 'true');
return;
}
- name: Download coverage-summary artifact
if: steps.resolve.outputs.skip != 'true' && steps.artifact.outputs.skip != 'true'
uses: actions/download-artifact@v4
with:
name: coverage-summary
run-id: ${{ steps.resolve.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: coverage
- name: Update coverage badge gist
# GitHub Actions does not allow secrets.* inside if: expressions, so
# surface them through env and gate on that to keep this a no-op on
# forks / repos without the secrets configured.
if: steps.resolve.outputs.skip != 'true' && steps.artifact.outputs.skip != 'true' && env.COVERAGE_GIST_ID != '' && env.GIST_TOKEN != ''
uses: actions/github-script@v7
env:
COVERAGE_GIST_ID: ${{ secrets.COVERAGE_GIST_ID }}
GIST_TOKEN: ${{ secrets.GIST_TOKEN }}
with:
github-token: ${{ secrets.GIST_TOKEN }}
script: |
const fs = require('fs');
const badge = fs.readFileSync('coverage/badge.json', 'utf8');
const summary = fs.readFileSync('coverage/summary.json', 'utf8');
await github.rest.gists.update({
gist_id: process.env.COVERAGE_GIST_ID,
files: {
'badge.json': { content: badge },
'coverage-summary.json': { content: summary },
},
});
core.info('Coverage badge gist updated.');