Skip to content

Commit d495e8a

Browse files
oschwaldclaude
andcommitted
Add release script
Same release workflow as GeoIP2-python: validates tooling and branch state, parses version and notes from HISTORY.rst, runs the full tox matrix, then commits, pushes, and creates a GitHub release (which triggers the PyPI upload workflow). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0481d38 commit d495e8a

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

dev-bin/release.sh

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/bin/bash
2+
3+
set -eu -o pipefail
4+
5+
# Pre-flight checks - verify all required tools are available and configured
6+
# before making any changes to the repository
7+
8+
check_command() {
9+
if ! command -v "$1" &>/dev/null; then
10+
echo "Error: $1 is not installed or not in PATH"
11+
exit 1
12+
fi
13+
}
14+
15+
# Verify gh CLI is authenticated
16+
if ! gh auth status &>/dev/null; then
17+
echo "Error: gh CLI is not authenticated. Run 'gh auth login' first."
18+
exit 1
19+
fi
20+
21+
# Verify we can access this repository via gh
22+
if ! gh repo view --json name &>/dev/null; then
23+
echo "Error: Cannot access repository via gh. Check your authentication and repository access."
24+
exit 1
25+
fi
26+
27+
# Verify git can connect to the remote (catches SSH key issues, etc.)
28+
if ! git ls-remote origin &>/dev/null; then
29+
echo "Error: Cannot connect to git remote. Check your git credentials/SSH keys."
30+
exit 1
31+
fi
32+
33+
check_command perl
34+
check_command uv
35+
36+
# Check that we're not on the main branch
37+
current_branch=$(git branch --show-current)
38+
if [ "$current_branch" = "main" ]; then
39+
echo "Error: Releases should not be done directly on the main branch."
40+
echo "Please create a release branch and run this script from there."
41+
exit 1
42+
fi
43+
44+
# Fetch latest changes and check that we're not behind origin/main
45+
echo "Fetching from origin..."
46+
git fetch origin
47+
48+
if ! git merge-base --is-ancestor origin/main HEAD; then
49+
echo "Error: Current branch is behind origin/main."
50+
echo "Please merge or rebase with origin/main before releasing."
51+
exit 1
52+
fi
53+
54+
changelog=$(cat HISTORY.rst)
55+
56+
regex='
57+
([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?) \(([0-9]{4}-[0-9]{2}-[0-9]{2})\)
58+
\+*
59+
60+
((.|
61+
)*)
62+
'
63+
64+
if [[ ! $changelog =~ $regex ]]; then
65+
echo "Could not find date line in change log!"
66+
exit 1
67+
fi
68+
69+
version="${BASH_REMATCH[1]}"
70+
date="${BASH_REMATCH[3]}"
71+
notes="$(echo "${BASH_REMATCH[4]}" | sed -n -E '/^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?/,$!p')"
72+
73+
if [[ "$date" != "$(date +"%Y-%m-%d")" ]]; then
74+
echo "$date is not today!"
75+
exit 1
76+
fi
77+
78+
tag="v$version"
79+
80+
if [ -n "$(git status --porcelain)" ]; then
81+
echo ". is not clean." >&2
82+
exit 1
83+
fi
84+
85+
perl -pi -e "s/(?<=^version = \").+?(?=\")/$version/gsm" pyproject.toml
86+
87+
echo $"Test results:"
88+
uv run tox
89+
90+
echo $'\nDiff:'
91+
git diff
92+
93+
echo $'\nRelease notes:'
94+
echo "$notes"
95+
96+
read -r -e -p "Commit changes and push to origin? " should_push
97+
98+
if [ "$should_push" != "y" ]; then
99+
echo "Aborting"
100+
exit 1
101+
fi
102+
103+
git commit -m "Update for $tag" -a
104+
105+
git push
106+
107+
gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag"

0 commit comments

Comments
 (0)