Repository navigation
214 lines (205 loc) · 8.6 KB
/
Copy pathrelease_create.yml
File metadata and controls
214 lines (205 loc) · 8.6 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
name: Release / Create
run-name: Release ${{ inputs.version || github.ref_name }}${{ inputs.skip_checks && ' (checks skipped)' || '' }}
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
inputs:
version:
description: Stable version to release, without a v prefix
required: true
type: string
release-theme:
description: Optional title theme, without the version number
default: ''
type: string
skip_checks:
description: Skip pre-release tests for this release
default: false
type: boolean
skip_checks_reason:
description: Reason for skipping checks (required when skip_checks is true)
default: ''
type: string
permissions:
contents: read
concurrency:
group: release-${{ inputs.version || github.ref_name }}
cancel-in-progress: false
jobs:
prepare:
name: Validate release request
if: ${{ github.event_name != 'push' || !github.event.deleted }}
runs-on: ubuntu-latest
outputs:
sha: ${{ steps.prepare.outputs.sha }}
version: ${{ steps.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
id: checkout
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Validate release configuration and pin the commit
id: prepare
uses: actions/github-script@v7
env:
VERSION: ${{ inputs.version || github.ref_name }}
RELEASE_THEME: ${{ inputs['release-theme'] }}
CHECKOUT_SHA: ${{ steps.checkout.outputs.commit }}
SKIP_CHECKS: ${{ inputs.skip_checks }}
SKIP_CHECKS_REASON: ${{ inputs.skip_checks_reason }}
HAS_SIGNING_CERTIFICATE: ${{ secrets.SIGNING_CERTIFICATE_BASE_64 != '' }}
HAS_SIGNING_PASSWORD: ${{ secrets.SIGNING_CERTIFICATE_PASSWORD != '' }}
HAS_BINARY_REPO_TOKEN: ${{ secrets.BINARY_REPO_PAT != '' }}
with:
script: |
const { validateVersion, validateSHA, validateReleaseRef, getTagSHA } = require('./Scripts/CI/release.js');
const { validateTheme } = require('./Scripts/CI/release-notes.js');
if (process.env.RELEASE_THEME) validateTheme(process.env.RELEASE_THEME);
if (['HAS_SIGNING_CERTIFICATE', 'HAS_SIGNING_PASSWORD', 'HAS_BINARY_REPO_TOKEN'].some(name => process.env[name] !== 'true')) {
throw new Error('Signing and binary repository credentials must be configured.');
}
const version = validateVersion(process.env.VERSION);
const isTagPush = context.eventName === 'push';
if (isTagPush) {
if (context.ref !== `refs/tags/${version}`) throw new Error('Push must refer to the requested version tag.');
} else {
validateReleaseRef(version, context.ref);
}
const sha = validateSHA(process.env.CHECKOUT_SHA);
if (!isTagPush && sha !== context.sha) throw new Error('The checked-out commit must match the dispatch commit.');
const skipChecks = process.env.SKIP_CHECKS === 'true';
const skipReason = (process.env.SKIP_CHECKS_REASON || '').trim();
if (skipChecks && !skipReason) throw new Error('A reason is required when skipping pre-release checks.');
const existing = await getTagSHA(github, context.repo, version);
if (isTagPush && !existing) throw new Error(`Tag ${version} no longer exists.`);
if (existing && existing !== sha) throw new Error(`Tag ${version} already points to ${existing}.`);
core.setOutput('version', version);
core.setOutput('sha', sha);
await core.summary.addHeading('Release candidate').addTable([
['Version', version], ['Commit', sha],
['Pre-release checks', skipChecks ? 'Skipped by request' : 'Required'],
]).write();
if (skipChecks) {
core.warning('Pre-release checks were explicitly skipped for this release.');
await core.summary.addHeading('Pre-release checks skipped')
.addTable([['Requested by', context.actor]])
.addCodeBlock(skipReason, 'text').write();
}
- name: Verify source version
uses: actions/github-script@v7
env:
VERSION: ${{ steps.prepare.outputs.version }}
with:
script: |
const { validateSourceVersion } = require('./Scripts/CI/release.js');
await validateSourceVersion(process.env.VERSION);
checks:
if: ${{ !inputs.skip_checks }}
needs: prepare
uses: ./.github/workflows/release_checks.yml
permissions:
contents: read
issues: read
pull-requests: read
statuses: write
id-token: write
with:
ref: ${{ needs.prepare.outputs.sha }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
build:
needs: [prepare, checks]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
((inputs.skip_checks && needs.checks.result == 'skipped') ||
(!inputs.skip_checks && needs.checks.result == 'success'))
uses: ./.github/workflows/release_build.yml
permissions:
contents: read
id-token: write
with:
ref: ${{ needs.prepare.outputs.sha }}
version: ${{ needs.prepare.outputs.version }}
secrets:
SIGNING_CERTIFICATE_BASE_64: ${{ secrets.SIGNING_CERTIFICATE_BASE_64 }}
SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.SIGNING_CERTIFICATE_PASSWORD }}
tag:
name: Create or verify the release tag
permissions:
contents: write
needs: [prepare, checks, build]
if: >-
!cancelled() && needs.prepare.result == 'success' && needs.build.result == 'success' &&
((inputs.skip_checks && needs.checks.result == 'skipped') ||
(!inputs.skip_checks && needs.checks.result == 'success'))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.sha }}
persist-credentials: false
- name: Validate tag prerequisites
uses: actions/github-script@v7
env:
SKIP_CHECKS: ${{ inputs.skip_checks }}
CANDIDATE_SHA: ${{ needs.prepare.outputs.sha }}
VERIFIED_SHA: ${{ needs.checks.outputs.verified-sha }}
BUILT_SHA: ${{ needs.build.outputs.sha }}
ARTIFACT_ID: ${{ needs.build.outputs.artifact-id }}
with:
script: |
const { validateSHA } = require('./Scripts/CI/release.js');
const sha = validateSHA(process.env.CANDIDATE_SHA);
if (process.env.SKIP_CHECKS !== 'true' && process.env.VERIFIED_SHA !== sha) {
throw new Error('Checks must refer to the candidate commit.');
}
if (process.env.BUILT_SHA !== sha || !process.env.ARTIFACT_ID) {
throw new Error('Stored release artifacts must refer to the candidate commit.');
}
- name: Create or verify version tag
uses: actions/github-script@v7
env:
VERSION: ${{ needs.prepare.outputs.version }}
CANDIDATE_SHA: ${{ needs.prepare.outputs.sha }}
with:
github-token: ${{ github.token }}
script: |
const { ensureTag, getTagSHA } = require('./Scripts/CI/release.js');
const version = process.env.VERSION;
const sha = process.env.CANDIDATE_SHA;
if (context.eventName === 'push') {
if (await getTagSHA(github, context.repo, version) !== sha) {
throw new Error('The release tag must still point to the candidate commit.');
}
} else {
await ensureTag({ github, repo: context.repo, version, sha });
}
publish:
needs: [prepare, build, tag]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
needs.build.result == 'success' && needs.tag.result == 'success'
uses: ./.github/workflows/release_publish.yml
permissions:
contents: write
with:
ref: ${{ needs.prepare.outputs.sha }}
version: ${{ needs.prepare.outputs.version }}
artifact-id: ${{ needs.build.outputs.artifact-id }}
release-theme: ${{ inputs['release-theme'] || '' }}
secrets:
BINARY_REPO_PAT: ${{ secrets.BINARY_REPO_PAT }}
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
INTERFACE_COVERAGE_PAT: ${{ secrets.INTERFACE_COVERAGE_PAT }}
documentation:
needs: publish
if: ${{ !cancelled() && needs.publish.result == 'success' }}
uses: ./.github/workflows/documentation.yml
permissions:
contents: read
packages: write
pages: write
id-token: write