Repository navigation
Expand file tree
/
Copy pathaction.yml
More file actions
536 lines (484 loc) · 19.2 KB
/
Copy pathaction.yml
File metadata and controls
536 lines (484 loc) · 19.2 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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
name: Simple Release
description: Opinionated, customizable GitHub Action to publish releases out of pushed v* tags.
author: Giorgio Garofalo
branding:
icon: tag
color: purple
inputs:
# Core
tag:
description: >
Tag name for the release. When empty, falls back to `github.ref_name`.
If the ref that triggered the workflow isn't a tag, most steps will
still run against this value; make sure to pass a sensible one when
calling from a non-tag ref.
required: false
default: ''
token:
description: >
Token used to create the GitHub releases. When empty, falls back to
`github.token`.
required: false
default: ''
committer-token:
description: >
Token used for pushing commits and moving tags. Falls back to `token` if
empty. A PAT with `contents: write` on the target repo is usually needed
when the workflow itself was triggered by a bot or the default token
cannot push to protected branches.
required: false
default: ''
# Version file
version-file:
description: >
Path to a file that should be overwritten with the release version (e.g.
`version.txt`). Leave empty to skip. The tag's leading `v` is stripped
unless `keep-v-prefix` is set to `true`.
required: false
default: ''
keep-v-prefix:
description: >
When `true`, the tag is written to `version-file` verbatim. When `false`
(default), the leading `v` is stripped. Does not affect
`bump-package-json`, whose version must remain plain semver.
required: false
default: 'false'
bump-package-json:
description: >
When `true`, sets the `version` field of `package.json` in the repository
root to the release version, leaving the rest of the manifest untouched.
Use this instead of `version-file` for npm-shaped projects: `version-file`
overwrites its target with the bare version string, which would destroy a
JSON manifest. The leading `v` is always stripped, since npm requires
plain semver. The manifest is included in the commit when `commit` is
`true`.
required: false
default: 'false'
replace-version-in-files:
description: >
Newline- or comma-separated list of files (e.g. `README.md`) in which
every occurrence of the previous release version is replaced with the
new one, as literal text with no syntax awareness. The previous version
is the highest `v`-prefixed tag (e.g. `v1.2.3`) below the current one in
version order, so all tags must be available (`fetch-depth: 0`). A leading
`v` is stripped from both versions before matching: bare occurrences
(`1.2.3`) are replaced directly and `v`-prefixed ones (`v1.2.3`) keep
their prefix. When no previous tag exists the step is skipped. The files
are included in the auto-commit when `commit` is `true`.
required: false
default: ''
# Changelog
changelog-file:
description: >
Path to a `keep-a-changelog` style changelog file. Leave empty to skip
changelog handling entirely.
required: false
default: ''
update-changelog:
description: >
Whether to turn the changelog's `[Unreleased]` section into a dated
release section. Defaults to `true` when `changelog-file` is set. Set to
`false` when calling the action a second time in the same job and the
changelog was already updated in an earlier call.
required: false
default: 'true'
# Release notes extraction
extract-release-notes:
description: >
Whether to extract release notes from the changelog for the current tag.
Defaults to `true` when `changelog-file` is set and a release will be
created (see `release`). Explicit `true`/`false` overrides.
required: false
default: ''
# Extra file appended to the release body (e.g. sponsors, footer, extra
# notes) after the extracted release notes.
body-append-file:
description: >
Path to a file whose content is appended after the release notes in the
release body, separated by a blank line (e.g. `SPONSORS.md`, a footer
section, extra notes). Leave empty to skip. Ignored when `release-body`
is provided literally.
required: false
default: ''
insert-changelog-spacers:
description: >
When `true`, inserts a ` ` spacer line before every `###` and
`####` heading in the extracted release notes, except the very first
heading (so the notes never start with a stray spacer). Useful when the
source changelog is written without manual spacers but the release notes
should carry them for GitHub's rendering. Only touches the composed
body; the pushed changelog file is never rewritten. Ignored when
`release-body` is provided literally.
required: false
default: 'false'
# Commit updates back
commit:
description: >
When `true`, commits changes to `version-file`, `changelog-file` and, when
`bump-package-json` is enabled, `package.json` back to the repository.
required: false
default: 'false'
commit-message:
description: Commit message template. `{tag}` is substituted with the tag.
required: false
default: 'chore: release {tag}'
commit-user-name:
description: Name for the auto-commit author.
required: false
default: 'github-actions'
commit-user-email:
description: Email for the auto-commit author.
required: false
default: 'github-actions[bot]@users.noreply.github.com'
commit-branch:
description: >
Branch to push the auto-commit to. Defaults to `main`. Only used when
`commit` is `true`.
required: false
default: 'main'
# Devbuild (rolling prerelease)
devbuild-tag:
description: >
Name of a rolling tag (e.g. `latest`) to move to the current commit and
publish as a prerelease. Leave empty to skip devbuild entirely.
required: false
default: ''
devbuild-name:
description: Display name for the devbuild release.
required: false
default: 'Development build'
devbuild-files:
description: >
Newline- or comma-separated glob patterns of files to attach to the
devbuild release. Passed through to `softprops/action-gh-release`.
required: false
default: ''
# Final release
release:
description: >
Whether to create the tagged release. `auto` (default) creates the
release only when the workflow ref is a tag. Use `true` or `false` to
force behavior.
required: false
default: 'auto'
release-name:
description: Display name for the release. Defaults to the tag.
required: false
default: ''
release-body:
description: >
Literal Markdown body for the release. If empty, the body is composed
from the extracted release notes and the `body-append-file` content.
required: false
default: ''
release-files:
description: >
Newline- or comma-separated glob patterns of files to attach to the
release. Passed through to `softprops/action-gh-release`.
required: false
default: ''
release-draft:
description: Whether the release should be created as a draft.
required: false
default: 'false'
release-prerelease:
description: >
Whether the release should be marked as a prerelease. Ignored when
`auto-detect-prerelease` is `true` (the flag is then derived from the
tag instead).
required: false
default: 'false'
auto-detect-prerelease:
description: >
When `true`, the release's prerelease flag is derived from the tag: the
release is marked as a prerelease if the tag contains any of
`prerelease-keywords` (e.g. `v1.2.3-rc.1`, `v1.2.3-beta`). This fully
overrides `release-prerelease`.
required: false
default: 'false'
prerelease-keywords:
description: >
Comma-separated list of case-insensitive keywords that mark a tag as a
prerelease. Matched as plain substrings against the tag. Only used when
`auto-detect-prerelease` is `true`.
required: false
default: 'alpha,beta,rc,pre,preview,snapshot,nightly,dev'
# Major-version tag alias (typical for reusable GitHub Actions)
major-tag:
description: >
When `true`, extracts the major version from `tag` (e.g. `v2` from
`v2.3.4`) and force-pushes it to the current commit. Useful for reusable
GitHub Actions that want consumers to pin `@v2`. Fails if the tag isn't
semver-like.
required: false
default: 'false'
outputs:
version:
description: The computed version string (tag with leading `v` optionally stripped).
value: ${{ steps.compute.outputs.version }}
is-tag:
description: Whether the workflow ref is a tag.
value: ${{ steps.compute.outputs.is-tag }}
release-notes:
description: Extracted release notes for the tag, if the changelog step ran.
value: ${{ steps.notes.outputs.release_notes }}
release-url:
description: URL of the created release, if any.
value: ${{ steps.release.outputs.url }}
devbuild-url:
description: URL of the created devbuild release, if any.
value: ${{ steps.devbuild-release.outputs.url }}
runs:
using: composite
steps:
- name: Compute derived values
id: compute
shell: bash
env:
INPUT_TAG: ${{ inputs.tag }}
INPUT_TOKEN: ${{ inputs.token }}
INPUT_COMMITTER_TOKEN: ${{ inputs.committer-token }}
INPUT_KEEP_V: ${{ inputs.keep-v-prefix }}
INPUT_RELEASE: ${{ inputs.release }}
INPUT_EXTRACT: ${{ inputs.extract-release-notes }}
INPUT_CHANGELOG: ${{ inputs.changelog-file }}
INPUT_COMMIT_MESSAGE: ${{ inputs.commit-message }}
INPUT_REPLACE_FILES: ${{ inputs.replace-version-in-files }}
REF_NAME: ${{ github.ref_name }}
REF_TYPE: ${{ github.ref_type }}
DEFAULT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Resolve effective tag and tokens; composite input defaults can't
# reference `github.*` contexts, so we resolve them here instead.
tag="${INPUT_TAG:-$REF_NAME}"
token="${INPUT_TOKEN:-$DEFAULT_TOKEN}"
committer_token="${INPUT_COMMITTER_TOKEN:-$token}"
if [ "${INPUT_KEEP_V}" = "true" ]; then
version="$tag"
else
version="${tag#v}"
fi
is_tag=false
if [ "${REF_TYPE}" = "tag" ]; then is_tag=true; fi
case "${INPUT_RELEASE}" in
true) do_release=true ;;
false) do_release=false ;;
auto|"") do_release="$is_tag" ;;
*) echo "release must be true, false, or auto" >&2; exit 2 ;;
esac
case "${INPUT_EXTRACT}" in
true) do_extract=true ;;
false) do_extract=false ;;
"")
if [ -n "${INPUT_CHANGELOG}" ] && [ "$do_release" = "true" ]; then
do_extract=true
else
do_extract=false
fi
;;
*) echo "extract-release-notes must be true, false, or empty" >&2; exit 2 ;;
esac
commit_message="${INPUT_COMMIT_MESSAGE//\{tag\}/$tag}"
# Normalized to a space-separated list for the commit step's
# file_pattern; the replacement step re-parses the raw input instead.
replace_files="$(printf '%s' "$INPUT_REPLACE_FILES" | tr ',\n' ' ')"
{
echo "tag=$tag"
echo "token=$token"
echo "version=$version"
echo "is-tag=$is_tag"
echo "do-release=$do_release"
echo "do-extract=$do_extract"
echo "committer-token=$committer_token"
echo "commit-message=$commit_message"
echo "replace-files=$replace_files"
} >> "$GITHUB_OUTPUT"
- name: Check out commit branch
if: inputs.commit == 'true' && inputs.commit-branch != ''
shell: bash
env:
BRANCH: ${{ inputs.commit-branch }}
run: |
set -euo pipefail
"${GITHUB_ACTION_PATH}/scripts/checkout-commit-branch.sh" "$BRANCH"
- name: Bump version file
if: inputs.version-file != ''
shell: bash
env:
VERSION_FILE: ${{ inputs.version-file }}
TAG: ${{ steps.compute.outputs.tag }}
KEEP_V: ${{ inputs.keep-v-prefix }}
run: |
set -euo pipefail
args=()
if [ "$KEEP_V" = "true" ]; then args+=("--keep-v"); fi
args+=("$VERSION_FILE" "$TAG")
"${GITHUB_ACTION_PATH}/scripts/bump-version.sh" "${args[@]}"
- name: Bump package.json
if: inputs.bump-package-json == 'true'
shell: bash
env:
TAG: ${{ steps.compute.outputs.tag }}
run: |
set -euo pipefail
"${GITHUB_ACTION_PATH}/scripts/bump-package-json.sh" package.json "$TAG"
- name: Replace version in files
if: inputs.replace-version-in-files != ''
shell: bash
env:
FILES: ${{ inputs.replace-version-in-files }}
TAG: ${{ steps.compute.outputs.tag }}
run: |
set -euo pipefail
previous="$("${GITHUB_ACTION_PATH}/scripts/previous-tag.sh" "$TAG")"
if [ -z "$previous" ]; then
echo "::notice::No previous version tag found; skipping version replacement."
exit 0
fi
files=()
while IFS= read -r file; do
file="$(printf '%s' "$file" | xargs)"
if [ -n "$file" ]; then files+=("$file"); fi
done < <(printf '%s\n' "$FILES" | tr ',' '\n')
"${GITHUB_ACTION_PATH}/scripts/replace-version.sh" "$previous" "$TAG" "${files[@]}"
- name: Update changelog
if: inputs.changelog-file != '' && inputs.update-changelog != 'false'
uses: thomaseizinger/keep-a-changelog-new-release@v2
with:
tag: ${{ steps.compute.outputs.tag }}
changelogPath: ${{ inputs.changelog-file }}
- name: Extract release notes
id: notes
if: steps.compute.outputs.do-extract == 'true'
uses: ffurrer2/extract-release-notes@v3
with:
changelog_file: ${{ inputs.changelog-file }}
- name: Commit release updates
if: inputs.commit == 'true'
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: ${{ steps.compute.outputs.commit-message }}
commit_user_name: ${{ inputs.commit-user-name }}
commit_user_email: ${{ inputs.commit-user-email }}
file_pattern: >-
${{ inputs.version-file }}
${{ inputs.bump-package-json == 'true' && 'package.json' || '' }}
${{ steps.compute.outputs.replace-files }}
${{ inputs.changelog-file }}
branch: ${{ inputs.commit-branch }}
- name: Move devbuild tag
if: inputs.devbuild-tag != ''
shell: bash
env:
DEVBUILD_TAG: ${{ inputs.devbuild-tag }}
REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
TOKEN: ${{ steps.compute.outputs.committer-token }}
COMMIT_USER_NAME: ${{ inputs.commit-user-name }}
COMMIT_USER_EMAIL: ${{ inputs.commit-user-email }}
run: |
set -euo pipefail
git config user.name "$COMMIT_USER_NAME"
git config user.email "$COMMIT_USER_EMAIL"
git tag -d "$DEVBUILD_TAG" || true
git tag -f "$DEVBUILD_TAG"
push_url="${SERVER_URL#https://}"
git push "https://x-access-token:${TOKEN}@${push_url}/${REPO}.git" "+refs/tags/${DEVBUILD_TAG}"
- name: Create devbuild release
id: devbuild-release
if: inputs.devbuild-tag != ''
uses: softprops/action-gh-release@v2
with:
token: ${{ steps.compute.outputs.token }}
tag_name: ${{ inputs.devbuild-tag }}
prerelease: true
name: ${{ inputs.devbuild-name }}
body: "**Full Changelog**: ${{ github.server_url }}/${{ github.repository }}/commits/${{ inputs.devbuild-tag }}"
files: ${{ inputs.devbuild-files }}
- name: Compose release body
id: body
if: steps.compute.outputs.do-release == 'true'
shell: bash
env:
RELEASE_BODY: ${{ inputs.release-body }}
NOTES: ${{ steps.notes.outputs.release_notes }}
APPEND_FILE: ${{ inputs.body-append-file }}
INSERT_SPACERS: ${{ inputs.insert-changelog-spacers }}
run: |
set -euo pipefail
out="${RUNNER_TEMP}/simple-release-body.md"
if [ -n "${RELEASE_BODY}" ]; then
printf '%s' "${RELEASE_BODY}" > "$out"
else
notes_file="${RUNNER_TEMP}/simple-release-notes.md"
if [ -n "${NOTES}" ]; then
printf '%s' "${NOTES}" > "$notes_file"
else
: > "$notes_file"
fi
if [ "${INSERT_SPACERS}" = "true" ]; then
spaced_file="${RUNNER_TEMP}/simple-release-notes-spaced.md"
"${GITHUB_ACTION_PATH}/scripts/insert-changelog-spacers.sh" "$notes_file" "$spaced_file"
mv "$spaced_file" "$notes_file"
fi
"${GITHUB_ACTION_PATH}/scripts/compose-body.sh" "$out" "$notes_file" "${APPEND_FILE}"
fi
echo "path=$out" >> "$GITHUB_OUTPUT"
- name: Parse semver for prerelease detection
id: semver
if: steps.compute.outputs.do-release == 'true' && inputs.auto-detect-prerelease == 'true'
uses: madhead/semver-utils@v5
with:
version: ${{ steps.compute.outputs.tag }}
lenient: true
- name: Resolve prerelease flag
id: prerelease
if: steps.compute.outputs.do-release == 'true'
shell: bash
env:
AUTO_DETECT: ${{ inputs.auto-detect-prerelease }}
RELEASE_PRERELEASE: ${{ inputs.release-prerelease }}
PRERELEASE_SEGMENT: ${{ steps.semver.outputs.prerelease }}
KEYWORDS: ${{ inputs.prerelease-keywords }}
run: |
set -euo pipefail
# When auto-detect is on, derive the flag from the tag's semver
# prerelease segment (extracted by madhead/semver-utils) and fully
# override release-prerelease; otherwise pass release-prerelease through.
if [ "${AUTO_DETECT}" = "true" ]; then
flag="$("${GITHUB_ACTION_PATH}/scripts/detect-prerelease.sh" "${PRERELEASE_SEGMENT}" "${KEYWORDS}")"
else
flag="${RELEASE_PRERELEASE}"
fi
echo "flag=$flag" >> "$GITHUB_OUTPUT"
- name: Create release
id: release
if: steps.compute.outputs.do-release == 'true'
uses: softprops/action-gh-release@v2
with:
token: ${{ steps.compute.outputs.token }}
tag_name: ${{ steps.compute.outputs.tag }}
name: ${{ inputs.release-name }}
body_path: ${{ steps.body.outputs.path }}
files: ${{ inputs.release-files }}
draft: ${{ inputs.release-draft }}
prerelease: ${{ steps.prerelease.outputs.flag }}
- name: Move major-version tag
if: inputs.major-tag == 'true'
shell: bash
env:
TAG: ${{ steps.compute.outputs.tag }}
REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
TOKEN: ${{ steps.compute.outputs.committer-token }}
COMMIT_USER_NAME: ${{ inputs.commit-user-name }}
COMMIT_USER_EMAIL: ${{ inputs.commit-user-email }}
run: |
set -euo pipefail
major="$("${GITHUB_ACTION_PATH}/scripts/major-tag.sh" "$TAG")"
git config user.name "$COMMIT_USER_NAME"
git config user.email "$COMMIT_USER_EMAIL"
git tag -f "$major"
push_url="${SERVER_URL#https://}"
git push "https://x-access-token:${TOKEN}@${push_url}/${REPO}.git" "+refs/tags/${major}"