Skip to content

Commit 5127f80

Browse files
committed
Fix automated release process
1 parent 194f55e commit 5127f80

8 files changed

Lines changed: 194 additions & 26 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Publish Release
2+
3+
on:
4+
push:
5+
branches: [next]
6+
7+
permissions:
8+
contents: write
9+
10+
concurrency:
11+
group: publish-release
12+
cancel-in-progress: false
13+
14+
jobs:
15+
publish-release:
16+
name: Publish release
17+
runs-on: ubuntu-24.04
18+
19+
env:
20+
GH_TOKEN: ${{ github.token }}
21+
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v6
25+
with:
26+
fetch-depth: 0
27+
fetch-tags: true
28+
29+
- name: Publish release when current commit is a release
30+
run: make publish-release

.github/workflows/release.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010

1111
permissions:
1212
contents: write
13+
pull-requests: write
1314

1415
concurrency:
1516
group: release
@@ -30,7 +31,7 @@ jobs:
3031
with:
3132
fetch-depth: 0
3233

33-
- name: Create release
34+
- name: Create release pull request
3435
env:
3536
RELEASE_DATE: ${{ github.run_started_at }}
3637
run: make release VERSION='${{ inputs.version }}'

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- Add automated release process
2525
- Ensure smoke tests use local scripts for bootstrap URLs
2626
- Improve install and download output layouts
27+
- Fix automated release process
2728

2829
## 0.10.0 - 2023-06-10
2930

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ test: prepare
4040
release:
4141
@sh bin/release '$(VERSION)'
4242

43+
.PHONY: publish-release
44+
publish-release:
45+
@sh bin/publish-release
46+
4347
.tmp/shellcheck:
4448
@echo "Downloading shellcheck"
4549
@echo

bin/publish-release

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
die() {
7+
printf '%s\n' "$1" >&2
8+
exit 1
9+
}
10+
11+
require_command() {
12+
command -v "$1" > /dev/null 2>&1 || die "missing required command: $1"
13+
}
14+
15+
repository() {
16+
if [ -n "${GITHUB_REPOSITORY:-}" ]; then
17+
printf '%s\n' "$GITHUB_REPOSITORY"
18+
return
19+
fi
20+
21+
gh repo view --json nameWithOwner --jq .nameWithOwner
22+
}
23+
24+
validate_version() {
25+
case "$1" in
26+
*[!0-9.]* | .* | *. | *..* | "") return 1 ;;
27+
esac
28+
29+
printf '%s\n' "$1" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
30+
}
31+
32+
current_g_version() {
33+
awk '
34+
/^display_g_version\(\) \{$/ { in_version = 1; next }
35+
in_version && /^[[:space:]]*echo "[^"]+"$/ {
36+
sub(/^[[:space:]]*echo "/, "")
37+
sub(/"$/, "")
38+
print
39+
exit
40+
}
41+
' bin/g
42+
}
43+
44+
validate_release_commit() {
45+
g_version=$(current_g_version)
46+
47+
[ "$g_version" = "$version" ] \
48+
|| die "bin/g version is $g_version, expected $version"
49+
50+
grep "^## $version - " CHANGELOG.md > /dev/null \
51+
|| die "CHANGELOG.md does not contain release $version"
52+
53+
if git rev-parse -q --verify "refs/tags/$version" > /dev/null; then
54+
die "tag already exists: $version"
55+
fi
56+
57+
if gh release view "$version" > /dev/null 2>&1; then
58+
die "release already exists: $version"
59+
fi
60+
}
61+
62+
write_release_notes() {
63+
awk -v version="$version" '
64+
index($0, "## " version " - ") == 1 { in_section = 1; next }
65+
in_section && /^## / { exit }
66+
in_section { print }
67+
' CHANGELOG.md > release-notes.md
68+
69+
awk 'index($0, "- ") == 1 { found = 1 } END { exit !found }' release-notes.md \
70+
|| die "release notes are empty"
71+
72+
previous_tag=$(git describe --tags --abbrev=0)
73+
repo=$(repository)
74+
{
75+
printf '\n'
76+
printf 'See full changelog: https://github.com/%s/compare/%s...%s\n' \
77+
"$repo" "$previous_tag" "$version"
78+
} >> release-notes.md
79+
}
80+
81+
tag_release() {
82+
if [ -n "${GITHUB_ACTIONS:-}" ]; then
83+
git \
84+
-c user.name='github-actions[bot]' \
85+
-c user.email='41898282+github-actions[bot]@users.noreply.github.com' \
86+
tag -a "$version" -F release-notes.md
87+
else
88+
git tag -a "$version" -F release-notes.md
89+
fi
90+
91+
git push origin "$version"
92+
}
93+
94+
create_github_release() {
95+
gh release create "$version" \
96+
--title "$version" \
97+
--notes-file release-notes.md \
98+
--latest=false \
99+
bin/g \
100+
bin/install
101+
}
102+
103+
require_command awk
104+
require_command gh
105+
require_command git
106+
require_command grep
107+
108+
version=${1:-$(git log -1 --pretty=%s)}
109+
110+
if ! validate_version "$version"; then
111+
printf 'not a release commit: %s\n' "$version"
112+
exit 0
113+
fi
114+
115+
validate_release_commit
116+
write_release_notes
117+
tag_release
118+
create_github_release

bin/release

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ usage() {
1111
cat <<- EOF
1212
Usage: bin/release VERSION
1313
14-
Creates a release commit, annotated tag, and GitHub release for VERSION.
15-
The GitHub release is not marked as latest automatically.
14+
Creates a release branch and pull request for VERSION.
1615
EOF
1716
}
1817

@@ -85,18 +84,25 @@ validate_version() {
8584
validate_release_request() {
8685
branch=$(current_branch)
8786
default=$(default_branch)
87+
release_branch="release/$version"
8888

8989
[ "$branch" = "$default" ] \
9090
|| die "run releases from $default, not $branch"
9191

9292
git diff --quiet -- bin/g CHANGELOG.md \
9393
|| die "bin/g or CHANGELOG.md has uncommitted changes"
9494

95-
git rev-parse -q --verify "refs/tags/$version" > /dev/null \
96-
&& die "tag already exists: $version"
95+
if git rev-parse -q --verify "refs/tags/$version" > /dev/null; then
96+
die "tag already exists: $version"
97+
fi
98+
99+
if gh release view "$version" > /dev/null 2>&1; then
100+
die "release already exists: $version"
101+
fi
97102

98-
gh release view "$version" > /dev/null 2>&1 \
99-
&& die "release already exists: $version"
103+
if git ls-remote --exit-code --heads origin "$release_branch" > /dev/null 2>&1; then
104+
die "release branch already exists: $release_branch"
105+
fi
100106
}
101107

102108
prepare_release_files() {
@@ -150,6 +156,7 @@ prepare_release_files() {
150156
}
151157
}
152158
' bin/g > bin/g.tmp
159+
chmod +x bin/g.tmp
153160
mv bin/g.tmp bin/g
154161

155162
awk -v version="$version" '
@@ -173,28 +180,35 @@ prepare_release_files() {
173180
validate_release_files() {
174181
make format
175182
make lint
183+
}
184+
185+
validate_source_files() {
186+
make format
187+
make lint
176188
make test
177189
}
178190

179-
commit_and_tag_release() {
180-
if [ -n "${GITHUB_ACTIONS:-}" ]; then
181-
git config user.name 'github-actions[bot]'
182-
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
183-
fi
191+
create_release_pr() {
192+
release_branch="release/$version"
184193

194+
git checkout -B "$release_branch"
185195
git add bin/g CHANGELOG.md
186-
git commit -m "$version"
187-
git tag -a "$version" -F release-notes.md
188-
git push origin "HEAD:$(current_branch)" "$version"
189-
}
190196

191-
create_github_release() {
192-
gh release create "$version" \
197+
if [ -n "${GITHUB_ACTIONS:-}" ]; then
198+
git \
199+
-c user.name='github-actions[bot]' \
200+
-c user.email='41898282+github-actions[bot]@users.noreply.github.com' \
201+
commit -m "$version"
202+
else
203+
git commit -m "$version"
204+
fi
205+
206+
git push --set-upstream origin "$release_branch"
207+
gh pr create \
208+
--base "$(default_branch)" \
209+
--head "$release_branch" \
193210
--title "$version" \
194-
--notes-file release-notes.md \
195-
--latest=false \
196-
bin/g \
197-
bin/install
211+
--body-file release-notes.md
198212
}
199213

200214
version=${1:-}
@@ -218,7 +232,7 @@ require_command make
218232

219233
validate_version "$version"
220234
validate_release_request
235+
validate_source_files
221236
prepare_release_files
222237
validate_release_files
223-
commit_and_tag_release
224-
create_github_release
238+
create_release_pr

tests/metadata.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ test_expect_success 'create mock Go archive' '
6262

6363
test_expect_success 'g accepts exact GOPATH bin PATH entry' '
6464
output=$(run_with_path "$GOPATH/bin:$mock_path:/bin:/usr/bin" "$g_bin --version") &&
65-
test "$output" = "0.10.0"
65+
echo "$output" | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$"
6666
'
6767

6868
test_expect_success 'g rejects substring-only GOPATH bin PATH entry' '

tests/smoke.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ test_expect_success 'install script configures g' '
8585

8686
test_expect_success 'g reports its version' '
8787
output=$(run_with_g_env "g --version") &&
88-
test "$output" = "0.10.0"
88+
echo "$output" | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$"
8989
'
9090

9191
test_expect_success 'go is installed by g and present on PATH' '

0 commit comments

Comments
 (0)