-
Notifications
You must be signed in to change notification settings - Fork 1
147 lines (136 loc) · 5.25 KB
/
Copy pathrelease.yml
File metadata and controls
147 lines (136 loc) · 5.25 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
name: Release
# Three trigger modes:
# 1. `push` of a `vX.Y.Z` tag (manual `git push origin vX.Y.Z` from a maintainer's
# terminal) — runs end-to-end automatically.
# 2. `workflow_dispatch` (Run workflow button in Actions UI) with a `tag` input —
# re-generates the GitHub Release for any existing tag. Useful when an earlier
# run failed, when release notes need refreshing, or when manually back-filling
# an older tag.
# 3. Cross-workflow dispatch from `release-stamp.yml` (which can't rely on its own
# tag push to fire this workflow — pushes made by the default GITHUB_TOKEN
# don't trigger downstream workflows by design). The stamp workflow calls
# `gh workflow run release.yml --field tag=vX.Y.Z` after pushing the tag.
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Existing tag (e.g. v1.4.1) to (re)generate the GitHub Release for'
required: true
type: string
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Resolve tag and previous tag
id: meta
env:
INPUT_TAG: ${{ inputs.tag }}
EVENT: ${{ github.event_name }}
run: |
if [ "$EVENT" = "workflow_dispatch" ]; then
TAG="$INPUT_TAG"
if ! printf '%s' "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::tag must match vX.Y.Z; got: $TAG"
exit 1
fi
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::tag $TAG does not exist in this repository"
exit 1
fi
else
TAG="${GITHUB_REF#refs/tags/}"
fi
VERSION="${TAG#v}"
PREV=$(git tag --sort=-v:refname | grep -v "^${TAG}$" | head -1 || true)
{
echo "tag=${TAG}"
echo "version=${VERSION}"
echo "prev_tag=${PREV}"
} >> "$GITHUB_OUTPUT"
- name: Build release notes
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ steps.meta.outputs.tag }}
VERSION: ${{ steps.meta.outputs.version }}
PREV: ${{ steps.meta.outputs.prev_tag }}
run: |
# Extract the CHANGELOG section for this version (between
# "## [VERSION]" and the next "## [" heading). Use the tag's
# CHANGELOG, not main's — manual re-runs against an older tag
# should pick up that tag's content, not whatever is on main now.
CHANGELOG=$(git show "${TAG}:CHANGELOG.md" 2>/dev/null \
| awk -v v="$VERSION" '
/^## \[/ {
if ($0 ~ "^## \\[" v "\\]") { found=1; next }
else if (found) { exit }
}
found { print }
' | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}')
# PRs merged between previous tag and this tag — parse `(#N)`
# suffixes from squash-merge commit subjects.
PR_LIST=""
if [ -n "$PREV" ]; then
PR_NUMBERS=$(git log "${PREV}..${TAG}" --pretty=format:'%s' \
| grep -oE '\(#[0-9]+\)' | grep -oE '[0-9]+' | sort -un)
for n in $PR_NUMBERS; do
TITLE=$(gh pr view "$n" --repo "$REPO" --json title --jq .title 2>/dev/null || true)
[ -n "$TITLE" ] && PR_LIST="${PR_LIST}- #${n} — ${TITLE}"$'\n'
done
fi
{
if [ -n "$CHANGELOG" ]; then
echo "## What's Changed"
echo
echo "$CHANGELOG"
echo
fi
if [ -n "$PR_LIST" ]; then
echo "## Pull Requests"
echo
printf '%s' "$PR_LIST"
echo
fi
if [ -n "$PREV" ]; then
echo "**Full Changelog**: https://github.com/${REPO}/compare/${PREV}...${TAG}"
fi
} > release-notes.md
echo "--- release notes preview ---"
cat release-notes.md
- name: Create or update GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.meta.outputs.tag }}
run: |
# `--latest=false` on create; we'll re-mark below only when this
# tag is the highest semver, so a back-dated tag push doesn't
# steal the Latest badge from a newer release. On manual re-runs
# against an existing release, just refresh the notes.
if gh release view "$TAG" >/dev/null 2>&1; then
gh release edit "$TAG" --notes-file release-notes.md
echo "Updated existing release for $TAG"
else
gh release create "$TAG" \
--title "$TAG" \
--notes-file release-notes.md \
--latest=false
echo "Created release for $TAG"
fi
- name: Mark as latest if highest semver
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.meta.outputs.tag }}
run: |
HIGHEST=$(git tag --sort=-v:refname \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
if [ "$HIGHEST" = "$TAG" ]; then
gh release edit "$TAG" --latest
fi