Skip to content

Commit 5a5189f

Browse files
committed
Add release automation script
1 parent 8b1d1f5 commit 5a5189f

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

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+
set -euo pipefail
3+
4+
VERSION_FILE="src/pyscipopt/_version.py"
5+
SETUP_FILE="setup.py"
6+
CHANGELOG="CHANGELOG.md"
7+
REPO="scipopt/PySCIPOpt"
8+
9+
# --- Pre-flight checks ---
10+
11+
if ! command -v gh &>/dev/null; then
12+
echo "Error: gh CLI is not installed. Install it from https://cli.github.com"
13+
exit 1
14+
fi
15+
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+
if ! git diff --quiet || ! git diff --cached --quiet; then
22+
echo "Error: working directory has uncommitted changes. Commit or stash them first."
23+
exit 1
24+
fi
25+
26+
CURRENT_BRANCH=$(git branch --show-current)
27+
if [[ "$CURRENT_BRANCH" != "master" ]]; then
28+
echo "Error: must be on 'master' branch (currently on '${CURRENT_BRANCH}')."
29+
exit 1
30+
fi
31+
32+
git pull --ff-only
33+
34+
# --- Read current version ---
35+
36+
CURRENT_VERSION=$(sed -n "s/^__version__.*'\(.*\)'/\1/p" "$VERSION_FILE")
37+
MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
38+
MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
39+
PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3)
40+
41+
echo "Current version: ${CURRENT_VERSION}"
42+
43+
# --- Prompt for bump type ---
44+
45+
echo ""
46+
echo "Release type:"
47+
echo " 1) patch -> $((MAJOR)).$((MINOR)).$((PATCH + 1))"
48+
echo " 2) minor -> $((MAJOR)).$((MINOR + 1)).0"
49+
echo " 3) major -> $((MAJOR + 1)).0.0"
50+
echo ""
51+
read -rp "Select [1/2/3]: " bump_type
52+
53+
case "$bump_type" in
54+
1|patch) NEW_VERSION="$((MAJOR)).$((MINOR)).$((PATCH + 1))" ;;
55+
2|minor) NEW_VERSION="$((MAJOR)).$((MINOR + 1)).0" ;;
56+
3|major) NEW_VERSION="$((MAJOR + 1)).0.0" ;;
57+
*) echo "Error: invalid selection '${bump_type}'"; exit 1 ;;
58+
esac
59+
60+
# --- Check tag doesn't already exist ---
61+
62+
if git rev-parse "v${NEW_VERSION}" &>/dev/null; then
63+
echo "Error: tag 'v${NEW_VERSION}' already exists."
64+
exit 1
65+
fi
66+
67+
# --- Show changelog preview ---
68+
69+
echo ""
70+
echo "Unreleased changelog entries:"
71+
echo "-----------------------------"
72+
# Print lines between "## Unreleased" and the next "## " header
73+
sed -n '/^## Unreleased$/,/^## [0-9]/{/^## [0-9]/!p;}' "$CHANGELOG" | head -30
74+
echo "-----------------------------"
75+
echo ""
76+
77+
TODAY=$(date +%Y.%m.%d)
78+
echo ""
79+
echo "This script will:"
80+
echo " 1. Update version ${CURRENT_VERSION} -> ${NEW_VERSION} in _version.py and setup.py"
81+
echo " 2. Update CHANGELOG.md (${NEW_VERSION} - ${TODAY})"
82+
echo " 3. Commit, tag v${NEW_VERSION}, and push to origin"
83+
echo " 4. Trigger the build wheels workflow (test-pypi)"
84+
echo ""
85+
read -rp "Proceed? [Y/n] " confirm
86+
[[ "${confirm:-Y}" =~ ^[Nn] ]] && exit 0
87+
88+
# --- Update version files ---
89+
90+
sed -i.bak "s/__version__.*=.*'.*'/__version__: str = '${NEW_VERSION}'/" "$VERSION_FILE"
91+
rm -f "${VERSION_FILE}.bak"
92+
93+
sed -i.bak "s/version=\"${CURRENT_VERSION}\"/version=\"${NEW_VERSION}\"/" "$SETUP_FILE"
94+
rm -f "${SETUP_FILE}.bak"
95+
96+
echo "Updated version: ${CURRENT_VERSION} -> ${NEW_VERSION}"
97+
98+
# --- Update changelog ---
99+
100+
UNRELEASED_HEADER="## Unreleased"
101+
NEW_HEADER="## ${NEW_VERSION} - ${TODAY}"
102+
EMPTY_UNRELEASED="## Unreleased\n### Added\n### Fixed\n### Changed\n### Removed\n"
103+
104+
sed -i.bak "s/^${UNRELEASED_HEADER}$/${NEW_HEADER}/" "$CHANGELOG"
105+
rm -f "${CHANGELOG}.bak"
106+
107+
# Add empty Unreleased section at the top (after "# CHANGELOG" line)
108+
sed -i.bak "/^# CHANGELOG$/a\\
109+
\\
110+
${EMPTY_UNRELEASED}" "$CHANGELOG"
111+
rm -f "${CHANGELOG}.bak"
112+
113+
echo "Updated CHANGELOG.md"
114+
115+
# --- Commit, tag, and push ---
116+
117+
git add "$VERSION_FILE" "$SETUP_FILE" "$CHANGELOG"
118+
git commit -m "release v${NEW_VERSION}"
119+
git tag "v${NEW_VERSION}"
120+
git push origin master
121+
git push origin "v${NEW_VERSION}"
122+
123+
# --- Trigger test-pypi build ---
124+
125+
gh workflow run build_wheels.yml --repo "$REPO" -f upload_to_pypi=true -f test_pypi=true
126+
127+
echo ""
128+
echo "Done! v${NEW_VERSION} committed, tagged, pushed, and test-pypi build triggered."
129+
echo "Monitor at: gh run list --workflow=build_wheels.yml --repo ${REPO}"
130+
echo ""
131+
echo "Remaining manual steps:"
132+
echo " 1. Test the test-pypi package:"
133+
echo " pip install -i https://test.pypi.org/simple/ PySCIPOpt==${NEW_VERSION}"
134+
echo " 2. Release to production pypi:"
135+
echo " gh workflow run build_wheels.yml --repo ${REPO} -f upload_to_pypi=true -f test_pypi=false"
136+
echo " 3. Create a GitHub release from tag v${NEW_VERSION}:"
137+
echo " gh release create v${NEW_VERSION} --repo ${REPO} --title v${NEW_VERSION} --generate-notes"
138+
echo " 4. Update readthedocs: Builds -> Build version (latest and stable)"

0 commit comments

Comments
 (0)