-
Notifications
You must be signed in to change notification settings - Fork 281
Add release automation script #1201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Joao-Dionisio
wants to merge
9
commits into
master
Choose a base branch
from
release-automation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+429
−18
Draft
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
5a5189f
Add release automation script
Joao-Dionisio a061adc
Add scipoptsuite-deploy integration to release script
Joao-Dionisio b137b33
Merge branch 'master' into release-automation
Joao-Dionisio 8501bec
Address review: clean dir check, version validation, remote tag check
Joao-Dionisio 73d63cf
Split deploy into upgrade_scip.sh, simplify release.sh
Joao-Dionisio bbb3e22
Harden release scripts: validate sed replacements, fix dispatch race,…
Joao-Dionisio e37574b
Merge branch 'master' into release-automation
Joao-Dionisio 0096ff3
Skip deploy build when release already exists
Joao-Dionisio e43be80
Add PR checklist and update RELEASE.md for automated workflow
Joao-Dionisio File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,36 @@ | ||
| # Release Checklist | ||
| The following are the steps to follow to make a new PySCIPOpt release. They should mostly be done in order. | ||
| - [ ] Check if [scipoptsuite-deploy](https://github.com/scipopt/scipoptsuite-deploy) needs a new release, if a new SCIP version is released for example, or new dependencies (change symmetry dependency, add support for papilo/ parallelization.. etc). And Update release links in `pyproject.toml` | ||
| - [ ] Check if the table in the [documentation](https://pyscipopt.readthedocs.io/en/latest/build.html#building-from-source) needs to be updated. | ||
| - [ ] Update version number according to semantic versioning [rules](https://semver.org/) in `src/pyscipopt/_version.py` and `setup.py` | ||
| - [ ] Update `CHANGELOG.md`; Change the `Unreleased` to the new version number and add an empty unreleased section. | ||
| - [ ] Create a release candidate on test-pypi by running the workflow “Build wheels” in Actions->build wheels, with these parameters `upload:true, test-pypi:true` | ||
| - [ ] If the pipeline passes, test the released pip package on test-pypi by running and checking that it works | ||
| ```bash | ||
| pip install -i https://test.pypi.org/simple/ PySCIPOpt | ||
| ``` | ||
| - [ ] If it works, release on pypi.org with running the same workflow but with `test-pypi:false`. | ||
| - [ ] Then create a tag with the new version (from the master branch) | ||
| ```bash | ||
| git tag vX.X.X | ||
| git push origin vX.X.X | ||
| ``` | ||
| - [ ] Then make a github [release](https://github.com/scipopt/PySCIPOpt/releases/new) from this new tag. | ||
| - [ ] Update the documentation: from readthedocs.io -> Builds -> Build version (latest and stable) | ||
|
|
||
| ## Upgrading SCIP | ||
|
|
||
| Run `./upgrade_scip.sh` from the `master` branch. The script will: | ||
| 1. Prompt for SCIP, SoPlex, GCG, and IPOPT versions | ||
| 2. Build new binaries via [scipoptsuite-deploy](https://github.com/scipopt/scipoptsuite-deploy) (skipped if a matching release already exists) | ||
| 3. Create a branch, update `pyproject.toml`, and open a PR | ||
|
|
||
| On the PR: | ||
| - [ ] Fix any API incompatibilities | ||
| - [ ] Get CI green | ||
| - [ ] Update the [compatibility table](https://pyscipopt.readthedocs.io/en/latest/build.html#building-from-source) if needed | ||
| - [ ] Merge into `master` | ||
|
|
||
| ## Releasing PySCIPOpt | ||
|
|
||
| Run `./release.sh` from the `master` branch. The script will: | ||
| 1. Prompt for the version bump type (patch/minor/major) | ||
| 2. Update `_version.py`, `setup.py`, and `CHANGELOG.md` | ||
| 3. Commit, tag, push, and trigger a test-pypi build | ||
|
|
||
| After the script completes: | ||
| - [ ] Test the package from test-pypi: | ||
| ```bash | ||
| pip install -i https://test.pypi.org/simple/ PySCIPOpt==X.Y.Z | ||
| ``` | ||
| - [ ] Release to production pypi: | ||
| ```bash | ||
| gh workflow run build_wheels.yml --repo scipopt/PySCIPOpt -f upload_to_pypi=true -f test_pypi=false | ||
| ``` | ||
| - [ ] Create a GitHub release: | ||
| ```bash | ||
| gh release create vX.Y.Z --repo scipopt/PySCIPOpt --title vX.Y.Z --generate-notes | ||
| ``` | ||
| - [ ] Update readthedocs: Builds -> Build version (latest and stable) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| VERSION_FILE="src/pyscipopt/_version.py" | ||
| SETUP_FILE="setup.py" | ||
| CHANGELOG="CHANGELOG.md" | ||
| REPO="scipopt/PySCIPOpt" | ||
|
|
||
| # --- Pre-flight checks --- | ||
|
|
||
| if ! command -v gh &>/dev/null; then | ||
| echo "Error: gh CLI is not installed. Install it from https://cli.github.com" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! gh auth status &>/dev/null; then | ||
| echo "Error: gh CLI is not authenticated. Run 'gh auth login' first." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [[ -n "$(git status --porcelain)" ]]; then | ||
| echo "Error: working directory is not clean. Commit, stash, or remove changes first." | ||
| exit 1 | ||
| fi | ||
|
|
||
| CURRENT_BRANCH=$(git branch --show-current) | ||
| if [[ "$CURRENT_BRANCH" != "master" ]]; then | ||
| echo "Error: must be on 'master' branch (currently on '${CURRENT_BRANCH}')." | ||
| exit 1 | ||
| fi | ||
|
|
||
| git pull --ff-only | ||
|
|
||
| # --- Helper functions --- | ||
|
|
||
| validate_version() { | ||
| if [[ ! "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | ||
| echo "Error: '$1' is not a valid version (expected X.Y.Z)" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # --- Read current version --- | ||
|
|
||
| CURRENT_VERSION=$(sed -n "s/^__version__.*'\(.*\)'/\1/p" "$VERSION_FILE") | ||
| validate_version "$CURRENT_VERSION" | ||
| MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1) | ||
| MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2) | ||
| PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3) | ||
|
|
||
| echo "Current version: ${CURRENT_VERSION}" | ||
|
|
||
| # --- Prompt for bump type --- | ||
|
|
||
| echo "" | ||
| echo "Release type:" | ||
| echo " 1) patch -> $((MAJOR)).$((MINOR)).$((PATCH + 1))" | ||
| echo " 2) minor -> $((MAJOR)).$((MINOR + 1)).0" | ||
| echo " 3) major -> $((MAJOR + 1)).0.0" | ||
| echo "" | ||
| read -rp "Select [1/2/3]: " bump_type | ||
|
|
||
| case "$bump_type" in | ||
| 1|patch) NEW_VERSION="$((MAJOR)).$((MINOR)).$((PATCH + 1))" ;; | ||
| 2|minor) NEW_VERSION="$((MAJOR)).$((MINOR + 1)).0" ;; | ||
| 3|major) NEW_VERSION="$((MAJOR + 1)).0.0" ;; | ||
| *) echo "Error: invalid selection '${bump_type}'"; exit 1 ;; | ||
| esac | ||
|
|
||
| # --- Check tag doesn't already exist --- | ||
|
|
||
| if git rev-parse "v${NEW_VERSION}" &>/dev/null; then | ||
| echo "Error: tag 'v${NEW_VERSION}' already exists locally." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if git ls-remote --tags --exit-code origin "refs/tags/v${NEW_VERSION}" &>/dev/null; then | ||
| echo "Error: tag 'v${NEW_VERSION}' already exists on origin." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # --- Show summary and confirm --- | ||
|
|
||
| echo "" | ||
| echo "Unreleased changelog entries:" | ||
| echo "-----------------------------" | ||
| sed -n '/^## Unreleased$/,/^## [0-9]/{/^## [0-9]/!p;}' "$CHANGELOG" | head -30 | ||
| echo "-----------------------------" | ||
|
|
||
| TODAY=$(date +%Y.%m.%d) | ||
| echo "" | ||
| echo "This script will:" | ||
| echo " 1. Update version ${CURRENT_VERSION} -> ${NEW_VERSION} in _version.py and setup.py" | ||
| echo " 2. Update CHANGELOG.md (${NEW_VERSION} - ${TODAY})" | ||
| echo " 3. Commit, tag v${NEW_VERSION}, and push to origin" | ||
| echo " 4. Trigger the build wheels workflow (test-pypi)" | ||
| echo "" | ||
| read -rp "Proceed? [Y/n] " confirm | ||
| [[ "${confirm:-Y}" =~ ^[Nn] ]] && exit 0 | ||
|
|
||
| # ============================================================ | ||
| # From here on, everything runs without further prompts. | ||
| # If a step fails after commit but before push, recover with: | ||
| # git tag -d v${NEW_VERSION} && git reset --soft HEAD~1 | ||
| # ============================================================ | ||
|
|
||
| # --- Update version files --- | ||
|
|
||
| sed -i.bak "s/__version__.*=.*'.*'/__version__: str = '${NEW_VERSION}'/" "$VERSION_FILE" | ||
| if cmp -s "$VERSION_FILE" "${VERSION_FILE}.bak"; then | ||
| echo "Error: failed to update version in $VERSION_FILE (pattern not found)" | ||
| mv "${VERSION_FILE}.bak" "$VERSION_FILE" | ||
| exit 1 | ||
| fi | ||
| rm -f "${VERSION_FILE}.bak" | ||
|
|
||
| sed -i.bak "s/version=\"${CURRENT_VERSION}\"/version=\"${NEW_VERSION}\"/" "$SETUP_FILE" | ||
| if cmp -s "$SETUP_FILE" "${SETUP_FILE}.bak"; then | ||
| echo "Error: failed to update version in $SETUP_FILE (pattern not found)" | ||
| mv "${SETUP_FILE}.bak" "$SETUP_FILE" | ||
| exit 1 | ||
| fi | ||
| rm -f "${SETUP_FILE}.bak" | ||
|
|
||
| echo "Updated version: ${CURRENT_VERSION} -> ${NEW_VERSION}" | ||
|
|
||
| # --- Update changelog --- | ||
|
|
||
| sed -i.bak "s/^## Unreleased$/## ${NEW_VERSION} - ${TODAY}/" "$CHANGELOG" | ||
| if cmp -s "$CHANGELOG" "${CHANGELOG}.bak"; then | ||
| echo "Error: failed to update changelog ('## Unreleased' heading not found)" | ||
| mv "${CHANGELOG}.bak" "$CHANGELOG" | ||
| exit 1 | ||
| fi | ||
| rm -f "${CHANGELOG}.bak" | ||
|
|
||
| sed -i.bak "/^# CHANGELOG$/a\\ | ||
| \\ | ||
| ## Unreleased\\ | ||
| ### Added\\ | ||
| ### Fixed\\ | ||
| ### Changed\\ | ||
| ### Removed\\ | ||
| " "$CHANGELOG" | ||
| rm -f "${CHANGELOG}.bak" | ||
|
|
||
| echo "Updated CHANGELOG.md" | ||
|
|
||
| # --- Commit, tag, and push --- | ||
|
|
||
| git add "$VERSION_FILE" "$SETUP_FILE" "$CHANGELOG" | ||
| git commit -m "release v${NEW_VERSION}" | ||
| git tag "v${NEW_VERSION}" | ||
| git push origin master | ||
| git push origin "v${NEW_VERSION}" | ||
|
|
||
| # --- Trigger test-pypi build --- | ||
|
|
||
| gh workflow run build_wheels.yml --repo "$REPO" -f upload_to_pypi=true -f test_pypi=true | ||
|
|
||
Joao-Dionisio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| echo "" | ||
| echo "Done! v${NEW_VERSION} committed, tagged, pushed, and test-pypi build triggered." | ||
| echo "Monitor at: gh run list --workflow=build_wheels.yml --repo ${REPO}" | ||
| echo "" | ||
| echo "Remaining manual steps:" | ||
| echo " 1. Test the test-pypi package:" | ||
| echo " pip install -i https://test.pypi.org/simple/ PySCIPOpt==${NEW_VERSION}" | ||
| echo " 2. Release to production pypi:" | ||
| echo " gh workflow run build_wheels.yml --repo ${REPO} -f upload_to_pypi=true -f test_pypi=false" | ||
| echo " 3. Create a GitHub release from tag v${NEW_VERSION}:" | ||
| echo " gh release create v${NEW_VERSION} --repo ${REPO} --title v${NEW_VERSION} --generate-notes" | ||
| echo " 4. Update readthedocs: Builds -> Build version (latest and stable)" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.