Skip to content

Commit 5aa0552

Browse files
committed
Add release script
Validate release metadata, repository state, module files, tests, and lint before creating a GitHub release from the current branch.
1 parent 376a253 commit 5aa0552

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

dev-bin/release.sh

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
cd "$(git rev-parse --show-toplevel)"
6+
7+
die() {
8+
echo "Error: $*" >&2
9+
exit 1
10+
}
11+
12+
require_command() {
13+
if ! command -v "$1" >/dev/null 2>&1; then
14+
die "$1 is not installed or not in PATH"
15+
fi
16+
}
17+
18+
for command in gh git go golangci-lint; do
19+
require_command "$command"
20+
done
21+
22+
current_branch=$(git branch --show-current)
23+
if [ -z "$current_branch" ]; then
24+
die "releases cannot be created from a detached HEAD"
25+
fi
26+
27+
echo "Fetching from origin..."
28+
git fetch origin
29+
git submodule update --init --recursive
30+
31+
if ! git merge-base --is-ancestor origin/main HEAD; then
32+
die "the current branch does not contain the latest origin/main"
33+
fi
34+
35+
if [ -n "$(git status --porcelain)" ]; then
36+
die "the working directory is not clean"
37+
fi
38+
39+
if grep -q '^## Unreleased$' CHANGELOG.md; then
40+
die "replace the Unreleased heading with a version and release date"
41+
fi
42+
43+
heading_pattern='^## [0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z][0-9A-Za-z.-]*)? - [0-9]{4}-[0-9]{2}-[0-9]{2}$'
44+
release_heading=$(grep -m1 -E "$heading_pattern" CHANGELOG.md || true)
45+
if [ -z "$release_heading" ]; then
46+
die "CHANGELOG.md does not start with a release heading such as ## 2.6.0 - 2026-08-08"
47+
fi
48+
49+
heading_regex='^##[[:space:]]([^[:space:]]+)[[:space:]]-[[:space:]]([0-9]{4}-[0-9]{2}-[0-9]{2})$'
50+
if [[ ! $release_heading =~ $heading_regex ]]; then
51+
die "could not parse release heading: $release_heading"
52+
fi
53+
54+
version=${BASH_REMATCH[1]}
55+
release_date=${BASH_REMATCH[2]}
56+
today=$(date +%Y-%m-%d)
57+
tag="v$version"
58+
59+
if [ "$release_date" != "$today" ]; then
60+
die "release date $release_date is not today ($today)"
61+
fi
62+
63+
notes=$(
64+
awk -v heading="$release_heading" '
65+
$0 == heading { found = 1; next }
66+
found && /^## / { exit }
67+
found { print }
68+
' CHANGELOG.md | sed '/./,$!d'
69+
)
70+
if [ -z "$notes" ]; then
71+
die "the $version changelog section has no release notes"
72+
fi
73+
74+
module_path=$(go list -m -f '{{.Path}}')
75+
major=${version%%.*}
76+
case "$major" in
77+
0 | 1)
78+
if [[ $module_path =~ /v[0-9]+$ ]]; then
79+
die "tag $tag does not match module path $module_path"
80+
fi
81+
;;
82+
*)
83+
if [[ $module_path != */v"$major" ]]; then
84+
die "tag $tag does not match module path $module_path"
85+
fi
86+
;;
87+
esac
88+
89+
if git show-ref --verify --quiet "refs/tags/$tag"; then
90+
die "tag $tag already exists locally"
91+
fi
92+
if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then
93+
die "tag $tag already exists on origin"
94+
fi
95+
96+
if ! gh auth status >/dev/null 2>&1; then
97+
die "gh is not authenticated"
98+
fi
99+
100+
echo "Running release checks..."
101+
golangci-lint fmt
102+
go mod tidy
103+
go test ./...
104+
go test -race ./...
105+
CGO_ENABLED=0 GOARCH=386 go test ./...
106+
golangci-lint run
107+
108+
if [ -n "$(git status --porcelain)" ]; then
109+
echo "Release checks modified the working directory:" >&2
110+
git status --short >&2
111+
git diff >&2
112+
exit 1
113+
fi
114+
115+
echo
116+
echo "Version: $version"
117+
echo
118+
echo "Release notes:"
119+
echo "$notes"
120+
echo
121+
122+
read -r -p "Create $tag from $current_branch and push to origin? [y/N] " should_release
123+
if [[ ! $should_release =~ ^[Yy]$ ]]; then
124+
echo "Aborting"
125+
exit 1
126+
fi
127+
128+
git push origin "$current_branch"
129+
130+
notes_file=$(mktemp "${TMPDIR:-/tmp}/maxminddb-release-notes.XXXXXX")
131+
trap 'rm -f -- "$notes_file"' EXIT
132+
printf '%s\n' "$notes" >"$notes_file"
133+
134+
gh release create \
135+
--target "$current_branch" \
136+
--title "$version" \
137+
--notes-file "$notes_file" \
138+
"$tag"

0 commit comments

Comments
 (0)