-
Notifications
You must be signed in to change notification settings - Fork 14
286 lines (251 loc) · 9.45 KB
/
Copy pathrelease.yml
File metadata and controls
286 lines (251 loc) · 9.45 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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
name: Release
on:
workflow_dispatch:
inputs:
release_type:
description: 'Version segment to bump'
required: true
default: patch
type: choice
options:
- patch
- minor
- major
version_override:
description: 'Optional explicit version, for example v1.1.0'
required: false
type: string
create_github_release:
description: 'Create a GitHub Release'
required: true
default: false
type: boolean
publish_docker_image:
description: 'Publish Docker image for this version'
required: true
default: true
type: boolean
dry_run:
description: 'Preview only; do not create tag or release'
required: true
default: true
type: boolean
permissions:
actions: write
contents: write
concurrency:
group: release-${{ github.repository }}
cancel-in-progress: false
jobs:
release:
name: Prepare Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Resolve version
id: version
shell: bash
env:
RELEASE_TYPE: ${{ inputs.release_type }}
VERSION_OVERRIDE: ${{ inputs.version_override }}
run: |
set -euo pipefail
git fetch --tags --force
latest_tag="$(
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' |
grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' |
sort -V |
tail -n 1 || true
)"
latest_tag="${latest_tag:-v0.0.0}"
version_re='^v([0-9]+)\.([0-9]+)\.([0-9]+)$'
if [[ ! "${latest_tag}" =~ ${version_re} ]]; then
echo "Latest stable tag is invalid: ${latest_tag}" >&2
exit 1
fi
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch="${BASH_REMATCH[3]}"
if [[ -n "${VERSION_OVERRIDE}" ]]; then
new_version="${VERSION_OVERRIDE}"
if [[ ! "${new_version}" =~ ${version_re} ]]; then
echo "version_override must match vX.Y.Z: ${new_version}" >&2
exit 1
fi
else
case "${RELEASE_TYPE}" in
patch)
new_version="v${major}.${minor}.$((patch + 1))"
;;
minor)
new_version="v${major}.$((minor + 1)).0"
;;
major)
new_version="v$((major + 1)).0.0"
;;
*)
echo "Unsupported release_type: ${RELEASE_TYPE}" >&2
exit 1
;;
esac
fi
if git rev-parse -q --verify "refs/tags/${new_version}" >/dev/null; then
echo "Tag already exists: ${new_version}" >&2
exit 1
fi
highest="$(printf '%s\n%s\n' "${latest_tag}" "${new_version}" | sort -V | tail -n 1)"
if [[ "${highest}" != "${new_version}" || "${new_version}" == "${latest_tag}" ]]; then
echo "New version must be greater than latest stable tag (${latest_tag}): ${new_version}" >&2
exit 1
fi
release_doc="document/release/${new_version}.md"
release_doc_status="not-required"
if [[ -f "${release_doc}" ]]; then
release_doc_status="present"
elif [[ "${RELEASE_TYPE}" != "patch" ]]; then
release_doc_status="missing"
fi
{
echo "latest_tag=${latest_tag}"
echo "new_version=${new_version}"
echo "release_doc=${release_doc}"
echo "release_doc_status=${release_doc_status}"
} >> "${GITHUB_OUTPUT}"
{
echo "## Release Preview"
echo ""
echo "- Latest stable tag: \`${latest_tag}\`"
echo "- New version: \`${new_version}\`"
echo "- Release type: \`${RELEASE_TYPE}\`"
echo "- Create GitHub Release: \`${{ inputs.create_github_release }}\`"
echo "- Publish Docker image: \`${{ inputs.publish_docker_image }}\`"
echo "- Release document: \`${release_doc_status}\`"
} >> "${GITHUB_STEP_SUMMARY}"
- name: Validate release files
shell: bash
env:
RELEASE_TYPE: ${{ inputs.release_type }}
RELEASE_DOC: ${{ steps.version.outputs.release_doc }}
RELEASE_DOC_STATUS: ${{ steps.version.outputs.release_doc_status }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
set -euo pipefail
test -f changelog/CHANGELOG.md
test -f document/release/versioning-strategy.md
if [[ "${RELEASE_TYPE}" != "patch" && "${RELEASE_DOC_STATUS}" == "missing" ]]; then
if [[ "${DRY_RUN}" == "true" ]]; then
echo "MINOR/MAJOR release document is missing for this dry run: ${RELEASE_DOC}" >&2
else
echo "MINOR/MAJOR releases require a release document: ${RELEASE_DOC}" >&2
exit 1
fi
fi
- name: Build documentation
run: |
pnpm install --frozen-lockfile
pnpm build
- name: Generate release body
id: body
shell: bash
env:
LATEST_TAG: ${{ steps.version.outputs.latest_tag }}
NEW_VERSION: ${{ steps.version.outputs.new_version }}
RELEASE_DOC: ${{ steps.version.outputs.release_doc }}
RELEASE_DOC_STATUS: ${{ steps.version.outputs.release_doc_status }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
compare_range="${LATEST_TAG}..HEAD"
if [[ "${LATEST_TAG}" == "v0.0.0" ]]; then
compare_range="HEAD"
fi
{
echo "## Highlights"
echo ""
echo "- Stable release ${NEW_VERSION}."
echo ""
echo "## Changes"
echo ""
commits="$(git log --no-merges --pretty='- %s (%h)' "${compare_range}" | sed -n '1,80p')"
if [[ -n "${commits}" ]]; then
echo "${commits}"
else
echo "- No commit summary available."
fi
echo ""
echo "## Documents"
echo ""
echo "- Changelog: [changelog/CHANGELOG.md](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/changelog/CHANGELOG.md)"
if [[ "${RELEASE_DOC_STATUS}" == "present" ]]; then
echo "- Release note: [${RELEASE_DOC}](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/${RELEASE_DOC})"
else
echo "- Release note: not required for this release."
fi
echo "- Versioning strategy: [document/release/versioning-strategy.md](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/document/release/versioning-strategy.md)"
echo ""
echo "## Release Metadata"
echo ""
echo "- Previous tag: ${LATEST_TAG}"
echo "- New tag: ${NEW_VERSION}"
echo "- GitHub Release: ${{ inputs.create_github_release }}"
echo "- Docker image: ${{ inputs.publish_docker_image }}"
} > release-body.md
echo "body_file=release-body.md" >> "${GITHUB_OUTPUT}"
- name: Preview release body
run: sed -n '1,200p' release-body.md
- name: Create and push tag
if: ${{ !inputs.dry_run }}
shell: bash
env:
NEW_VERSION: ${{ steps.version.outputs.new_version }}
RELEASE_TYPE: ${{ inputs.release_type }}
CREATE_GITHUB_RELEASE: ${{ inputs.create_github_release }}
PUBLISH_DOCKER_IMAGE: ${{ inputs.publish_docker_image }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git fetch --tags --force
if git rev-parse -q --verify "refs/tags/${NEW_VERSION}" >/dev/null; then
echo "Tag already exists after refetch: ${NEW_VERSION}" >&2
exit 1
fi
{
echo "IMX-Forge ${NEW_VERSION}"
echo ""
echo "Release-Type: ${RELEASE_TYPE}"
echo "GitHub-Release: ${CREATE_GITHUB_RELEASE}"
echo "Docker-Image: ${PUBLISH_DOCKER_IMAGE}"
} > tag-message.txt
git tag -a "${NEW_VERSION}" -F tag-message.txt
git push origin "refs/tags/${NEW_VERSION}"
- name: Create GitHub Release
if: ${{ !inputs.dry_run && inputs.create_github_release }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.version.outputs.new_version }}
run: |
gh release create "${NEW_VERSION}" \
--title "IMX-Forge ${NEW_VERSION}" \
--notes-file release-body.md
- name: Dispatch Docker publish
if: ${{ !inputs.dry_run && inputs.publish_docker_image }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.version.outputs.new_version }}
run: |
gh workflow run docker-publish.yml --ref "${NEW_VERSION}"
- name: Dry run notice
if: ${{ inputs.dry_run }}
run: echo "Dry run enabled. No tag or GitHub Release was created."