Skip to content

Commit e3c2136

Browse files
committed
Rename staging branch, drop time estimates, add --version override
1 parent 3d190ee commit e3c2136

2 files changed

Lines changed: 79 additions & 61 deletions

File tree

RELEASE.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,27 @@ On the PR:
1515

1616
## Releasing PySCIPOpt
1717

18-
Releases run in two phases from `master`, driven by `./release.sh`. The tag and master push only happen in phase 2, so a failed RC leaves no semantic public trace — just a deletable `release-candidate-vX.Y.Z` branch.
18+
Releases run in two phases from `master`, driven by `./release.sh`. The tag and master push only happen in phase 2, so an aborted release leaves no semantic public trace — just a deletable `staging-vX.Y.Z` branch.
1919

2020
Use `--dry-run` with any command to preview without side effects.
2121

22-
### Phase 1 — start a release candidate
22+
### Phase 1 — start
2323

2424
```bash
2525
./release.sh
2626
```
2727

28-
Prompts for the version bump (patch/minor/major), updates `_version.py`, `setup.py`, and `CHANGELOG.md`, commits **locally**, pushes the commit to `release-candidate-vX.Y.Z` on origin, and triggers the build-wheels workflow on that branch (uploads to test-pypi). **Master is not pushed, no tag is created.** The script exits as soon as the workflow is dispatched — you do not wait.
28+
Prompts for the version bump (patch/minor/major), updates `_version.py`, `setup.py`, and `CHANGELOG.md`, commits **locally**, pushes the commit to `staging-vX.Y.Z` on origin, and triggers the build-wheels workflow on that branch (uploads to test-pypi). **Master is not pushed, no tag is created.** The script exits as soon as the workflow is dispatched.
29+
30+
To skip the bump prompt (e.g., when test-pypi has already burnt the default next version and you need to jump ahead):
31+
32+
```bash
33+
./release.sh --version=X.Y.Z
34+
```
2935

3036
### Manual verification
3137

32-
Once the RC workflow finishes (~15–30 min), install from test-pypi and smoke-test:
38+
Once the staging workflow finishes, install from test-pypi and smoke-test:
3339

3440
```bash
3541
pip install -i https://test.pypi.org/simple/ PySCIPOpt==X.Y.Z
@@ -43,15 +49,15 @@ If the smoke test **passes**:
4349
./release.sh --finalize
4450
```
4551

46-
Checks the RC workflow succeeded, then tags `vX.Y.Z`, pushes master, and deletes the RC branch.
52+
Checks the staging workflow succeeded, then tags `vX.Y.Z`, pushes master, and deletes the staging branch.
4753

4854
If the smoke test **fails** (or you change your mind):
4955

5056
```bash
5157
./release.sh --rollback
5258
```
5359

54-
Deletes the RC branch and resets the local release commit. test-pypi keeps the uploaded version string, so the next attempt must use a different bump.
60+
Deletes the staging branch and resets the local release commit. test-pypi has already burnt the uploaded version string, so the next attempt must use `--version=` to pick a different one.
5561

5662
### After finalize
5763

release.sh

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,25 @@ REPO="scipopt/PySCIPOpt"
88

99
DRY_RUN=false
1010
ACTION=start
11+
NEW_VERSION_OVERRIDE=""
1112
for arg in "$@"; do
1213
case "$arg" in
1314
--dry-run) DRY_RUN=true ;;
1415
--finalize) ACTION=finalize ;;
1516
--rollback) ACTION=rollback ;;
17+
--version=*) NEW_VERSION_OVERRIDE="${arg#--version=}" ;;
1618
-h|--help)
1719
cat <<USAGE
18-
Usage: $0 [--dry-run] [--finalize | --rollback]
19-
20-
(default) Start a release candidate: bump version, push release-candidate-vX.Y.Z, trigger test-pypi build.
21-
--finalize Promote a successful candidate: tag vX.Y.Z, push master, delete RC branch. Requires the RC workflow to have succeeded.
22-
--rollback Abandon a candidate: delete RC branch, reset local release commit.
23-
--dry-run Preview without side effects. Combinable with --finalize/--rollback.
20+
Usage: $0 [--dry-run] [--version=X.Y.Z] [--finalize | --rollback]
21+
22+
(default) Start a new release: bump version, push the staging branch,
23+
trigger the test-pypi build.
24+
--version=X.Y.Z Use this exact version instead of prompting for patch/minor/major.
25+
Useful if test-pypi has already burnt the default next version.
26+
--finalize Promote: tag vX.Y.Z, push master, delete the staging branch.
27+
Requires the staging workflow to have succeeded.
28+
--rollback Abandon: delete the staging branch and reset the local commit.
29+
--dry-run Preview without side effects. Combinable with the flags above.
2430
USAGE
2531
exit 0
2632
;;
@@ -68,24 +74,24 @@ validate_version() {
6874
fi
6975
}
7076

71-
# Promote a successful release candidate: tag, push master, clean up RC branch.
77+
# Promote a successful pending release: tag, push master, clean up staging branch.
7278
finalize_release() {
7379
local version="$1"
74-
local rc_branch="release-candidate-v${version}"
80+
local staging_branch="staging-v${version}"
7581

76-
echo "Pending release candidate: v${version}"
77-
echo "Checking workflow status on ${rc_branch}..."
82+
echo "Pending release: v${version}"
83+
echo "Checking workflow status on ${staging_branch}..."
7884

7985
local run_data run_count status conclusion url run_id
8086
run_data=$(gh run list --workflow=build_wheels.yml --repo "$REPO" \
81-
--branch "$rc_branch" --limit 1 \
87+
--branch "$staging_branch" --limit 1 \
8288
--json databaseId,status,conclusion,url 2>/dev/null || echo "[]")
8389
run_count=$(echo "$run_data" | jq 'length')
8490

8591
if [[ "$run_count" == "0" ]]; then
86-
echo "Error: no workflow run found for branch '${rc_branch}'."
92+
echo "Error: no workflow run found for branch '${staging_branch}'."
8793
echo "Either wait a moment and try again, or clean up manually:"
88-
echo " git push ${PUSH_REMOTE} --delete ${rc_branch}"
94+
echo " git push ${PUSH_REMOTE} --delete ${staging_branch}"
8995
echo " git reset --hard HEAD~1"
9096
exit 1
9197
fi
@@ -107,12 +113,12 @@ finalize_release() {
107113
echo "URL: ${url}"
108114
echo ""
109115
if [[ "$DRY_RUN" == true ]]; then
110-
echo "DRY RUN: would prompt to roll back (delete ${rc_branch} + reset local commit)."
116+
echo "DRY RUN: would prompt to roll back (delete ${staging_branch} + reset local commit)."
111117
exit 0
112118
fi
113-
read -rp "Roll back (delete ${rc_branch} and reset local release commit)? [Y/n] " confirm
119+
read -rp "Roll back (delete ${staging_branch} and reset local release commit)? [Y/n] " confirm
114120
if [[ ! "${confirm:-Y}" =~ ^[Nn] ]]; then
115-
git push "$PUSH_REMOTE" --delete "$rc_branch" || echo "(note: remote RC delete failed; clean up manually)"
121+
git push "$PUSH_REMOTE" --delete "$staging_branch" || echo "(note: remote staging branch delete failed; clean up manually)"
116122
git reset --hard HEAD~1
117123
echo "Rolled back. Repo is at pre-release state."
118124
fi
@@ -125,15 +131,15 @@ finalize_release() {
125131
echo " git tag v${version}"
126132
echo " git push ${PUSH_REMOTE} ${CURRENT_BRANCH}"
127133
echo " git push ${PUSH_REMOTE} v${version}"
128-
echo " git push ${PUSH_REMOTE} --delete ${rc_branch}"
134+
echo " git push ${PUSH_REMOTE} --delete ${staging_branch}"
129135
exit 0
130136
fi
131137

132138
echo "Workflow succeeded. Promoting release v${version}..."
133139
git tag "v${version}"
134140
git push "$PUSH_REMOTE" "$CURRENT_BRANCH"
135141
git push "$PUSH_REMOTE" "v${version}"
136-
git push "$PUSH_REMOTE" --delete "$rc_branch"
142+
git push "$PUSH_REMOTE" --delete "$staging_branch"
137143

138144
echo ""
139145
echo "Done! v${version} is now tagged on ${PUSH_REMOTE}."
@@ -148,23 +154,23 @@ finalize_release() {
148154
echo " 4. Update readthedocs: Builds -> Build version (latest and stable)"
149155
}
150156

151-
# Abandon a release candidate: delete RC branch, reset local release commit.
157+
# Abandon a pending release: delete staging branch, reset local release commit.
152158
rollback_release() {
153159
local version="$1"
154-
local rc_branch="release-candidate-v${version}"
160+
local staging_branch="staging-v${version}"
155161

156-
echo "Rolling back release candidate v${version}..."
162+
echo "Rolling back pending release v${version}..."
157163
if [[ "$DRY_RUN" == true ]]; then
158164
echo "DRY RUN: would have run:"
159-
echo " git push ${PUSH_REMOTE} --delete ${rc_branch}"
165+
echo " git push ${PUSH_REMOTE} --delete ${staging_branch}"
160166
echo " git reset --hard HEAD~1"
161167
exit 0
162168
fi
163169

164-
if git ls-remote --heads --exit-code "$PUSH_REMOTE" "$rc_branch" &>/dev/null; then
165-
git push "$PUSH_REMOTE" --delete "$rc_branch"
170+
if git ls-remote --heads --exit-code "$PUSH_REMOTE" "$staging_branch" &>/dev/null; then
171+
git push "$PUSH_REMOTE" --delete "$staging_branch"
166172
else
167-
echo "(note: ${rc_branch} already absent from ${PUSH_REMOTE})"
173+
echo "(note: ${staging_branch} already absent from ${PUSH_REMOTE})"
168174
fi
169175
git reset --hard HEAD~1
170176
echo "Rolled back. Repo is at pre-release state."
@@ -199,10 +205,10 @@ fi
199205
# Start path: if HEAD is already a release commit, the user probably forgot a flag.
200206
HEAD_MSG=$(git log -1 --format=%s)
201207
if [[ "$HEAD_MSG" =~ ^release\ v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
202-
echo "Error: HEAD is already 'release v${BASH_REMATCH[1]}' — a release candidate is pending."
208+
echo "Error: HEAD is already 'release v${BASH_REMATCH[1]}' — a pending release exists."
203209
echo "Use:"
204-
echo " ./release.sh --finalize # promote (requires the RC workflow to have succeeded)"
205-
echo " ./release.sh --rollback # abandon (delete RC branch, reset local commit)"
210+
echo " ./release.sh --finalize # promote (requires the staging workflow to have succeeded)"
211+
echo " ./release.sh --rollback # abandon (delete staging branch, reset local commit)"
206212
exit 1
207213
fi
208214

@@ -216,22 +222,28 @@ PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3)
216222

217223
echo "Current version: ${CURRENT_VERSION}"
218224

219-
# --- Prompt for bump type ---
225+
# --- Determine new version (prompt, or use --version=X.Y.Z override) ---
220226

221-
echo ""
222-
echo "Release type:"
223-
echo " 1) patch -> $((MAJOR)).$((MINOR)).$((PATCH + 1))"
224-
echo " 2) minor -> $((MAJOR)).$((MINOR + 1)).0"
225-
echo " 3) major -> $((MAJOR + 1)).0.0"
226-
echo ""
227-
read -rp "Select [1/2/3]: " bump_type
227+
if [[ -n "$NEW_VERSION_OVERRIDE" ]]; then
228+
validate_version "$NEW_VERSION_OVERRIDE"
229+
NEW_VERSION="$NEW_VERSION_OVERRIDE"
230+
echo "Using version (from --version): ${NEW_VERSION}"
231+
else
232+
echo ""
233+
echo "Release type:"
234+
echo " 1) patch -> $((MAJOR)).$((MINOR)).$((PATCH + 1))"
235+
echo " 2) minor -> $((MAJOR)).$((MINOR + 1)).0"
236+
echo " 3) major -> $((MAJOR + 1)).0.0"
237+
echo ""
238+
read -rp "Select [1/2/3]: " bump_type
228239

229-
case "$bump_type" in
230-
1|patch) NEW_VERSION="$((MAJOR)).$((MINOR)).$((PATCH + 1))" ;;
231-
2|minor) NEW_VERSION="$((MAJOR)).$((MINOR + 1)).0" ;;
232-
3|major) NEW_VERSION="$((MAJOR + 1)).0.0" ;;
233-
*) echo "Error: invalid selection '${bump_type}'"; exit 1 ;;
234-
esac
240+
case "$bump_type" in
241+
1|patch) NEW_VERSION="$((MAJOR)).$((MINOR)).$((PATCH + 1))" ;;
242+
2|minor) NEW_VERSION="$((MAJOR)).$((MINOR + 1)).0" ;;
243+
3|major) NEW_VERSION="$((MAJOR + 1)).0.0" ;;
244+
*) echo "Error: invalid selection '${bump_type}'"; exit 1 ;;
245+
esac
246+
fi
235247

236248
# --- Check tag doesn't already exist ---
237249

@@ -262,7 +274,7 @@ if [[ "$UNRELEASED_BULLETS" == "0" ]]; then
262274
fi
263275

264276
TODAY=$(date +%Y.%m.%d)
265-
RC_BRANCH="release-candidate-v${NEW_VERSION}"
277+
STAGING_BRANCH="staging-v${NEW_VERSION}"
266278
echo ""
267279
if [[ "$DRY_RUN" == true ]]; then
268280
echo "DRY RUN: This script would:"
@@ -271,13 +283,13 @@ else
271283
fi
272284
echo " 1. Update version ${CURRENT_VERSION} -> ${NEW_VERSION} in _version.py and setup.py"
273285
echo " 2. Update CHANGELOG.md (${NEW_VERSION} - ${TODAY})"
274-
echo " 3. Commit locally, push commit to branch '${RC_BRANCH}' on ${PUSH_REMOTE}"
286+
echo " 3. Commit locally, push commit to branch '${STAGING_BRANCH}' on ${PUSH_REMOTE}"
275287
echo " (master is NOT pushed; no tag is created yet)"
276288
echo " 4. Trigger the build wheels workflow on that branch (test-pypi)"
277289
echo ""
278290
echo "Once the workflow finishes, re-run this script:"
279-
echo " - success -> tag v${NEW_VERSION}, push master, delete RC branch"
280-
echo " - failure -> prompt to roll back (delete RC branch + reset local commit)"
291+
echo " - success -> tag v${NEW_VERSION}, push master, delete staging branch"
292+
echo " - failure -> prompt to roll back (delete staging branch + reset local commit)"
281293
echo ""
282294
if [[ "$DRY_RUN" == false ]]; then
283295
read -rp "Proceed? [Y/n] " confirm
@@ -347,27 +359,27 @@ if [[ "$DRY_RUN" == true ]]; then
347359
echo ""
348360
echo "DRY RUN: would have run:"
349361
echo " git commit -m 'release v${NEW_VERSION}'"
350-
echo " git push ${PUSH_REMOTE} HEAD:refs/heads/${RC_BRANCH}"
351-
echo " gh workflow run build_wheels.yml --repo ${REPO} --ref ${RC_BRANCH} -f upload_to_pypi=true -f test_pypi=true"
362+
echo " git push ${PUSH_REMOTE} HEAD:refs/heads/${STAGING_BRANCH}"
363+
echo " gh workflow run build_wheels.yml --repo ${REPO} --ref ${STAGING_BRANCH} -f upload_to_pypi=true -f test_pypi=true"
352364
exit 0
353365
fi
354366

355-
# --- Commit locally and push RC branch (no tag, no master push yet) ---
367+
# --- Commit locally and push staging branch (no tag, no master push yet) ---
356368

357369
git add "$VERSION_FILE" "$SETUP_FILE" "$CHANGELOG"
358370
git commit -m "release v${NEW_VERSION}"
359-
git push "$PUSH_REMOTE" "HEAD:refs/heads/${RC_BRANCH}"
371+
git push "$PUSH_REMOTE" "HEAD:refs/heads/${STAGING_BRANCH}"
360372

361-
# --- Trigger test-pypi build against the RC branch ---
373+
# --- Trigger test-pypi build against the staging branch ---
362374

363-
gh workflow run build_wheels.yml --repo "$REPO" --ref "$RC_BRANCH" -f upload_to_pypi=true -f test_pypi=true
375+
gh workflow run build_wheels.yml --repo "$REPO" --ref "$STAGING_BRANCH" -f upload_to_pypi=true -f test_pypi=true
364376

365377
echo ""
366378
echo "Release candidate v${NEW_VERSION} started (phase 1 of 2):"
367379
echo " - Local: release commit on ${CURRENT_BRANCH}, NOT pushed"
368-
echo " - Remote: branch '${RC_BRANCH}' has the release commit"
369-
echo " - Workflow: https://github.com/${REPO}/actions?query=branch%3A${RC_BRANCH}"
380+
echo " - Remote: branch '${STAGING_BRANCH}' has the release commit"
381+
echo " - Workflow: https://github.com/${REPO}/actions?query=branch%3A${STAGING_BRANCH}"
370382
echo ""
371383
echo "Re-run this script after the workflow finishes to finalize:"
372-
echo " - success -> tag v${NEW_VERSION}, push ${CURRENT_BRANCH}, delete ${RC_BRANCH}"
384+
echo " - success -> tag v${NEW_VERSION}, push ${CURRENT_BRANCH}, delete ${STAGING_BRANCH}"
373385
echo " - failure -> prompt to roll back"

0 commit comments

Comments
 (0)