Repository navigation
243 lines (225 loc) · 10.9 KB
/
Copy pathprepare-release.yml
File metadata and controls
243 lines (225 loc) · 10.9 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
name: Prepare release
description: >-
Opens a release PR that finalizes CHANGELOG.md for the given tag (via the
shared create-release-pr.yml reusable workflow), then regenerates the
auto-generated wiki/ and appends it to the PR's branch so both are reviewed
and merged together. After merge, publish-release.yml takes over.
on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag WITH "v" prefix (e.g. v2.5.0, v2.5.0-beta.1)'
required: true
type: string
# These workflow-level scopes are inherited only by the reusable
# `create-release-pr` job: `contents: write` lets it push the release branch,
# and `id-token: write` lets the create-pr action inside it exchange the OIDC
# token for a short-lived App installation token (the PR itself is opened with
# that token, not GITHUB_TOKEN). The `update-wiki` job declares its own scopes
# below.
permissions:
contents: write
id-token: write
jobs:
create-release-pr:
uses: AdGuardSoftwareLimited/actions/.github/workflows/create-release-pr.yml@master
with:
team: extensions
tag: ${{ inputs.tag }}
# Regenerate the auto-generated wiki/ from JSDoc and append it to the release
# PR's branch after the PR is open, so the wiki update is reviewed and merged
# TOGETHER with the release. This way released code changes and docs are
# always in sync.
update-wiki:
needs: create-release-pr
runs-on: team-extensions
# Scopes of the update-wiki job only:
# `contents: read` — checkout reads the release branch; nothing in this
# job writes with GITHUB_TOKEN (protected-push swaps in the Octopass
# token instead);
# `id-token: write` — protected-push exchanges the OIDC token for the
# short-lived Octopass App installation token;
# `pull-requests: write` — post, update and delete the wiki build failure
# comment on the release PR.
permissions:
contents: read
id-token: write
pull-requests: write
env:
RELEASE_BRANCH: release-bump/${{ inputs.tag }}
# File which `tee` fills with the build log. The notification step reads
# the name from here too, so the shell and the script cannot drift apart.
WIKI_BUILD_LOG: wiki-build.log
steps:
# The release branch always has scripts/wiki-build-failure-notify.cjs:
# the create-release-pr job above cuts it from this same github.sha, and
# a dispatch from an older ref runs the older workflow, which has no
# notification step at all.
- name: Checkout release branch
uses: actions/checkout@v5
with:
ref: ${{ env.RELEASE_BRANCH }}
# Build the wiki but do NOT block the release on failure: the job stays
# green (continue-on-error). The full build log is captured so the step
# below can quote its tail, even when this step times out.
- name: Build wiki
id: build-wiki
continue-on-error: true
timeout-minutes: 30
shell: bash
run: |
# BuildKit writes its progress and the final error to stderr, so
# merge stderr into the pipe before tee. With pipefail the step
# already exits with docker's status; tee keeps filling the log until
# the step is killed, so a timed-out build still has a tail.
DOCKER_BUILDKIT=1 docker build --progress plain \
--target wiki-output --output ./wiki-out . 2>&1 \
| tee "$RUNNER_TEMP/$WIKI_BUILD_LOG"
# Only append when the wiki was actually built; a transient build failure
# (steps.build-wiki.outcome != 'success') is skipped so we don't try to
# mv a non-existent wiki-out/ directory. The PR still contains just the
# CHANGELOG bump.
- name: Append regenerated wiki
if: steps.build-wiki.outcome == 'success'
run: |
rm -rf wiki
mv wiki-out/wiki wiki
rm -rf wiki-out
# Commit and push the wiki to the release branch. protected-push:
# - no-ops (has_changes=false, exit 0) when the rebuild is a no-op;
# - resolves the remote branch head right before the push and uses it
# as the --force-with-lease value, so it does NOT protect a commit
# pushed while the build was running (see AG-61398);
# - authenticates with an Octopass token instead of GITHUB_TOKEN.
- name: Commit and push wiki
if: steps.build-wiki.outcome == 'success'
uses: AdGuardSoftwareLimited/actions/actions/protected-push@master
with:
# Required: protected-push forwards this to Octopass as the target
# repository for the minted token. Without it, Octopass gets an
# empty repository and rejects the request ("invalid token scope
# request").
repository: ${{ github.repository }}
branch: ${{ env.RELEASE_BRANCH }}
commit-message: 'docs: regenerate wiki docs for release'
files: wiki
# The update-wiki job intentionally stays green when the wiki build fails
# (see build-wiki's continue-on-error), so a silent failure would leave
# the release PR without regenerated docs and nobody would notice. Post a
# comment on the release PR with the captured error. Only `failure` is
# handled: a step that hits its own timeout-minutes also reports
# `failure`, while a manual cancel has nothing to fix.
- name: Comment on release PR about wiki build failure
if: steps.build-wiki.outcome == 'failure'
# Best-effort notification: update-wiki must stay green even if the
# comment cannot be posted (e.g. transient API error). An uncaught
# error of this step is annotated on the run page, and continue-on-error
# keeps the job green either way.
continue-on-error: true
uses: actions/github-script@v8
with:
# One 5xx response would lose what the comment text calls the only
# notification.
retries: 3
script: |
const {
readWikiBuildLogTail,
resolveReleasePr,
upsertFailureComment,
} = require('./scripts/wiki-build-failure-notify.cjs');
const releaseBranch = process.env.RELEASE_BRANCH;
const runUrl =
`${context.serverUrl}/${context.repo.owner}/${context.repo.repo}` +
`/actions/runs/${context.runId}`;
// The job stays green, so a failure that cannot be reported on the
// PR must at least raise an error annotation on the run page.
const number = await resolveReleasePr({ github, context, releaseBranch });
if (number === null) {
core.setFailed(
`No open release PR found for branch ${releaseBranch}; ` +
'the wiki build failure is not reported on a PR.',
);
return;
}
const errorText =
readWikiBuildLogTail(process.env.RUNNER_TEMP, process.env.WIKI_BUILD_LOG) ||
'(no error output captured)';
const body = [
`⚠️ **Wiki regeneration failed** for the release of \`${releaseBranch}\`.`,
'',
'The `update-wiki` job stays green (it is not a blocking stage), ' +
'so this comment is the only notification. The regenerated ' +
'`wiki/` was **not** committed to this PR. To recover, push ' +
'the fix to the release branch if there is one, then re-run ' +
'**only the `update-wiki` job** — not "Re-run all jobs", ' +
'which recreates the release branch and drops the fix. The ' +
'job rebuilds the wiki, commits it to this PR and removes ' +
'this comment. Alternatively, run `pnpm wiki` locally, ' +
'commit the generated `wiki/` together with ' +
'`scripts/compatibility-table.json`, push the commit to ' +
'the release branch, then delete this comment by hand. ' +
'If `pnpm wiki` reports that the sources ' +
'have changed, review `scripts/compatibility-table.json` ' +
'and run `pnpm wiki` again before the commit.',
'',
`Full log: ${runUrl}`,
'',
'<details><summary>Build error (last lines)</summary>',
'',
'````text',
errorText,
'````',
'',
'</details>',
].join('\n');
// A re-run must update the previous notification instead of posting
// a duplicate comment on the human-reviewed release PR. Overlapping
// runs can both post one, so the first comment is updated and the
// rest are deleted.
const { created, removed } = await upsertFailureComment({
github,
context,
number,
body,
});
core.info(created
? `Commented about the wiki build failure on release PR #${number}`
: `Updated the wiki build failure comment on release PR #${number}`);
if (removed > 0) {
core.info(`Removed ${removed} duplicate marker comment(s)`);
}
# A later successful run must not leave the stale failure comment on the
# release PR: it would contradict the committed wiki/ exactly when a
# maintainer reviews and merges the PR. Remove every such comment
# (best-effort).
- name: Remove stale wiki build failure comment
if: steps.build-wiki.outcome == 'success'
continue-on-error: true
uses: actions/github-script@v8
with:
retries: 3
script: |
const {
deleteFailureComments,
resolveReleasePr,
} = require('./scripts/wiki-build-failure-notify.cjs');
const releaseBranch = process.env.RELEASE_BRANCH;
const number = await resolveReleasePr({ github, context, releaseBranch });
if (number === null) {
core.info(
`No open release PR found for branch ${releaseBranch}; nothing to clean up.`,
);
return;
}
// Overlapping failure runs can leave several marker comments, so
// delete every one. Only comments of github-actions[bot] are
// matched, so a quote of the comment by a human is kept.
const removed = await deleteFailureComments({ github, context, number });
if (removed === 0) {
core.info(`No stale wiki build failure comment on release PR #${number}.`);
return;
}
core.info(
`Removed ${removed} stale wiki build failure comment(s) ` +
`from release PR #${number}.`,
);