diff --git a/docs/RELEASE.md b/docs/RELEASE.md index f04ad72..5998ee7 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -92,6 +92,8 @@ The `perform` goal is simpler than the `prepare` goal. Its documentation can be ## Summary +`scripts/create-release.py patch|minor|major` bumps the latest `v*` tag accordingly, aborts if `api-keys.properties` exists, runs `release:prepare release:perform` in one go and then offers to open Sonatype's [publishing page](https://central.sonatype.com/publishing), where the validated deployment must be published manually. The equivalent manual steps are: + ```sh # stay safe even if the examples module is not published rm examples/src/main/resources/api-keys.properties diff --git a/scripts/create-release.py b/scripts/create-release.py new file mode 100755 index 0000000..e6ab0c3 --- /dev/null +++ b/scripts/create-release.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import subprocess +import sys +import webbrowser +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +API_KEYS = ROOT / "examples/src/main/resources/api-keys.properties" +PUBLISHING_URL = "https://central.sonatype.com/publishing" +BUMPS = ("patch", "minor", "major") + + +def latest_version(): + subprocess.run(["git", "fetch", "--tags", "origin"], cwd=ROOT, check=True) + tags = subprocess.run( + ["git", "tag", "--list", "v[0-9]*", "--sort=-v:refname"], + cwd=ROOT, check=True, capture_output=True, text=True, + ).stdout.split() + return tags[0].removeprefix("v") + + +def bump(version, part): + major, minor, patch = map(int, version.split(".")) + if part == "major": + return f"{major + 1}.0.0" + if part == "minor": + return f"{major}.{minor + 1}.0" + return f"{major}.{minor}.{patch + 1}" + + +def main(): + if len(sys.argv) != 2 or sys.argv[1] not in BUMPS: + sys.exit(f"usage: {Path(sys.argv[0]).name} {'|'.join(BUMPS)}") + + if API_KEYS.exists(): + sys.exit(f"abort: remove {API_KEYS} before releasing") + + current = latest_version() + release = bump(current, sys.argv[1]) + development = f"{bump(release, 'patch')}-SNAPSHOT" + + if input(f"Release {current} -> {release}, next development version {development}? [y/N] ").lower() != "y": + sys.exit("aborted") + + maven = subprocess.run([ + "mvnd", "--serial", + f"-Dtag=v{release}", + f"-DreleaseVersion={release}", + f"-DdevelopmentVersion={development}", + "release:prepare", "release:perform", + ], cwd=ROOT) + if maven.returncode: + sys.exit("release failed") + + if input(f"Open {PUBLISHING_URL} to publish {release}? [Y/n] ").lower() != "n": + webbrowser.open(PUBLISHING_URL) + + +if __name__ == "__main__": + main() diff --git a/scripts/create-release.sh b/scripts/create-release.sh deleted file mode 100755 index 1442b05..0000000 --- a/scripts/create-release.sh +++ /dev/null @@ -1,2 +0,0 @@ -mvn -Dtag=v1.0.0 -DreleaseVersion=1.0.0 -DdevelopmentVersion=1.0.1-SNAPSHOT release:prepare release:perform -git fetch