diff --git a/.github/workflows/maven-version-determiner.py b/.github/workflows/maven-version-determiner.py new file mode 100755 index 00000000..8e46562c --- /dev/null +++ b/.github/workflows/maven-version-determiner.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 +import re +import subprocess +import sys + + +def get_arg(arg_idx) -> str: + return sys.argv[arg_idx] + + +def get_current_version() -> str: + current_version_cmd = subprocess.run( + "mvn help:evaluate -Dexpression=project.version -q -DforceStdout", + shell=True, capture_output=True, text=True) + return current_version_cmd.stdout + + +def get_release_version(release_type: str, current_version: str) -> str: + major, minor, patch = determine_new_version(current_version, release_type) + return str(major) + "." + str(minor) + "." + str(patch) + + +def get_snapshot_version(release_type: str, current_version: str) -> str: + major, minor, patch = determine_new_version(current_version, release_type) + patch += 1 + return str(major) + "." + str(minor) + "." + str(patch) + "-SNAPSHOT" + + +def get_version_tag(release_type: str, current_version: str) -> str: + major, minor, patch = determine_new_version(current_version, release_type) + return "v" + str(major) + "." + str(minor) + "." + str(patch) + + +def determine_new_version(current_version, release_type): + major, minor, patch, is_prerelease = dissect_version(current_version) + + match release_type: + case "MAJOR": + major, minor, patch = get_major_release_version(major) + case "MINOR": + major, minor, patch = get_minor_release_version(major, minor) + case "PATCH": + major, minor, patch = get_patch_release_version(major, minor, patch, + is_prerelease) + case _: + print("Second arg has to be `MAJOR`, `MINOR` or `PATCH`") + sys.exit() + + return major, minor, patch + + +def dissect_version(current_version) -> (int, int, int): + version_regex = get_regex() + regex_match = version_regex.search(current_version) + major: int = int(regex_match.groupdict().get("major")) + minor: int = int(regex_match.groupdict().get("minor")) + patch: int = int(regex_match.groupdict().get("patch")) + is_prerelease: bool = True if regex_match.groupdict().get( + "prerelease") else False + return major, minor, patch, is_prerelease + + +def get_regex(): + # Following REGEX is suggested on semver.org + # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + return re.compile( + r'^' + r'(?P0|[1-9]\d*)' + r'\.' + r'(?P0|[1-9]\d*)' + r'\.' + r'(?P0|[1-9]\d*)' + r'(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?' + r'(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$') + + +def get_major_release_version(major): + major += 1 + minor = 0 + patch = 0 + return major, minor, patch + + +def get_minor_release_version(major, minor): + minor += 1 + patch = 0 + return major, minor, patch + + +def get_patch_release_version(major, minor, patch, is_prerelease): + if is_prerelease: + # Leave values as is because current version without `prerelease` is new patch version + return major, minor, patch + return major, minor, patch + 1 + + +if __name__ == "__main__": + + version_type = get_arg(1) + release_type = get_arg(2) + + current_version = get_current_version() + + match version_type: + case "release-version": + new_version = get_release_version(release_type, current_version) + case "version-tag": + new_version = get_version_tag(release_type, current_version) + case "snapshot-version": + new_version = get_snapshot_version(release_type, current_version) + case _: + print( + "First arg has to be `release-version`, `version-tag` or `snapshot-version`.") + sys.exit() + + print(new_version) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 00000000..285de369 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,56 @@ +name: prepare-release + +on: + workflow_dispatch: + inputs: + release: + description: Type of release + required: true + type: choice + options: + - PATCH + - MINOR + - MAJOR + default: PATCH + +jobs: + prepare: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v4 + + # SSH connection with keys is necessary to allow `git push` + - name: Ensure correct SSH connection + uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: corretto + cache: maven + + - name: Determine new versions + run: | + echo "release_version=$(./.github/workflows/maven-version-determiner.py release-version $release_type)" >> "$GITHUB_ENV" + echo "snapshot_version=$(./.github/workflows/maven-version-determiner.py snapshot-version $release_type)" >> "$GITHUB_ENV" + echo "version_tag=$(./.github/workflows/maven-version-determiner.py version-tag $release_type)" >> "$GITHUB_ENV" + env: + release_type: ${{ inputs.release }} + + - name: Configure Git user + run: | + git config user.email "actions@users.noreply.github.com" + git config user.name "GitHub Actions" + + - name: Prepare with Maven release plugin + run: > + mvn + --batch-mode + -Dresume=false + -Drelease-version=$release_version + -Dtag=$version_tag + -DdevelopmentVersion=$snapshot_version + release:prepare diff --git a/README.md b/README.md index ca413edc..dce5ed98 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,20 @@ mvn clean verify If you are a maintainer, you can release a new version by doing the following: -- Merge the changes that need to be released into the `master` branch -- Checkout on to master locally and pull the latest changes -- Run `mvn release:prepare`, this will generate 2 commits that will bump the version of the github-java-client -- Push these changes to master -- Once the [release pipeline](https://github.com/spotify/github-java-client/actions/workflows/release.yml) has completed, the changes will have been tagged -- [Navigate to the tag](https://github.com/spotify/github-java-client/tags) associated with the changes and generate a manual release -- Once the release is generated, select the "Set as the latest release" checkbox and publish the release +- Trigger the workflow [prepare-release](./.github/workflows/prepare-release.yml) through the + [web UI](https://github.com/spotify/github-java-client/actions/workflows/prepare-release.yml) + - Select whether the new release should be a `major`, `minor` or `patch` release + - Trigger the release preparation on the `master` branch + - Pushes of this workflow will trigger runs of the workflow + [release](https://github.com/spotify/github-java-client/actions/workflows/release.yml) +- Once the + [release pipeline](https://github.com/spotify/github-java-client/actions/workflows/release.yml) + has completed for the release tag, the new release will be available on Maven Central and the + changes can be released on GitHub +- [Navigate to the tag](https://github.com/spotify/github-java-client/tags) associated with the + changes and generate a manual release +- Once the release is generated, select the "Set as the latest release" checkbox and publish the + release ## Notes about maturity