Skip to content

Commit 091dd7a

Browse files
cd: create GH Actions workflow to prepare release on dispatch (#169)
* cd: create GH Actions workflow to prepare release on dispatch * chore: use suggested regex from semver.org * fix: bump PATCH version if no prerelease string is appended
1 parent a39ed83 commit 091dd7a

File tree

3 files changed

+186
-7
lines changed

3 files changed

+186
-7
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/python3
2+
import re
3+
import subprocess
4+
import sys
5+
6+
7+
def get_arg(arg_idx) -> str:
8+
return sys.argv[arg_idx]
9+
10+
11+
def get_current_version() -> str:
12+
current_version_cmd = subprocess.run(
13+
"mvn help:evaluate -Dexpression=project.version -q -DforceStdout",
14+
shell=True, capture_output=True, text=True)
15+
return current_version_cmd.stdout
16+
17+
18+
def get_release_version(release_type: str, current_version: str) -> str:
19+
major, minor, patch = determine_new_version(current_version, release_type)
20+
return str(major) + "." + str(minor) + "." + str(patch)
21+
22+
23+
def get_snapshot_version(release_type: str, current_version: str) -> str:
24+
major, minor, patch = determine_new_version(current_version, release_type)
25+
patch += 1
26+
return str(major) + "." + str(minor) + "." + str(patch) + "-SNAPSHOT"
27+
28+
29+
def get_version_tag(release_type: str, current_version: str) -> str:
30+
major, minor, patch = determine_new_version(current_version, release_type)
31+
return "v" + str(major) + "." + str(minor) + "." + str(patch)
32+
33+
34+
def determine_new_version(current_version, release_type):
35+
major, minor, patch, is_prerelease = dissect_version(current_version)
36+
37+
match release_type:
38+
case "MAJOR":
39+
major, minor, patch = get_major_release_version(major)
40+
case "MINOR":
41+
major, minor, patch = get_minor_release_version(major, minor)
42+
case "PATCH":
43+
major, minor, patch = get_patch_release_version(major, minor, patch,
44+
is_prerelease)
45+
case _:
46+
print("Second arg has to be `MAJOR`, `MINOR` or `PATCH`")
47+
sys.exit()
48+
49+
return major, minor, patch
50+
51+
52+
def dissect_version(current_version) -> (int, int, int):
53+
version_regex = get_regex()
54+
regex_match = version_regex.search(current_version)
55+
major: int = int(regex_match.groupdict().get("major"))
56+
minor: int = int(regex_match.groupdict().get("minor"))
57+
patch: int = int(regex_match.groupdict().get("patch"))
58+
is_prerelease: bool = True if regex_match.groupdict().get(
59+
"prerelease") else False
60+
return major, minor, patch, is_prerelease
61+
62+
63+
def get_regex():
64+
# Following REGEX is suggested on semver.org
65+
# https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
66+
return re.compile(
67+
r'^'
68+
r'(?P<major>0|[1-9]\d*)'
69+
r'\.'
70+
r'(?P<minor>0|[1-9]\d*)'
71+
r'\.'
72+
r'(?P<patch>0|[1-9]\d*)'
73+
r'(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?'
74+
r'(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$')
75+
76+
77+
def get_major_release_version(major):
78+
major += 1
79+
minor = 0
80+
patch = 0
81+
return major, minor, patch
82+
83+
84+
def get_minor_release_version(major, minor):
85+
minor += 1
86+
patch = 0
87+
return major, minor, patch
88+
89+
90+
def get_patch_release_version(major, minor, patch, is_prerelease):
91+
if is_prerelease:
92+
# Leave values as is because current version without `prerelease` is new patch version
93+
return major, minor, patch
94+
return major, minor, patch + 1
95+
96+
97+
if __name__ == "__main__":
98+
99+
version_type = get_arg(1)
100+
release_type = get_arg(2)
101+
102+
current_version = get_current_version()
103+
104+
match version_type:
105+
case "release-version":
106+
new_version = get_release_version(release_type, current_version)
107+
case "version-tag":
108+
new_version = get_version_tag(release_type, current_version)
109+
case "snapshot-version":
110+
new_version = get_snapshot_version(release_type, current_version)
111+
case _:
112+
print(
113+
"First arg has to be `release-version`, `version-tag` or `snapshot-version`.")
114+
sys.exit()
115+
116+
print(new_version)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: prepare-release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
release:
7+
description: Type of release
8+
required: true
9+
type: choice
10+
options:
11+
- PATCH
12+
- MINOR
13+
- MAJOR
14+
default: PATCH
15+
16+
jobs:
17+
prepare:
18+
runs-on: ubuntu-latest
19+
steps:
20+
21+
- uses: actions/checkout@v4
22+
23+
# SSH connection with keys is necessary to allow `git push`
24+
- name: Ensure correct SSH connection
25+
uses: webfactory/[email protected]
26+
with:
27+
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
28+
29+
- uses: actions/setup-java@v3
30+
with:
31+
java-version: 11
32+
distribution: corretto
33+
cache: maven
34+
35+
- name: Determine new versions
36+
run: |
37+
echo "release_version=$(./.github/workflows/maven-version-determiner.py release-version $release_type)" >> "$GITHUB_ENV"
38+
echo "snapshot_version=$(./.github/workflows/maven-version-determiner.py snapshot-version $release_type)" >> "$GITHUB_ENV"
39+
echo "version_tag=$(./.github/workflows/maven-version-determiner.py version-tag $release_type)" >> "$GITHUB_ENV"
40+
env:
41+
release_type: ${{ inputs.release }}
42+
43+
- name: Configure Git user
44+
run: |
45+
git config user.email "[email protected]"
46+
git config user.name "GitHub Actions"
47+
48+
- name: Prepare with Maven release plugin
49+
run: >
50+
mvn
51+
--batch-mode
52+
-Dresume=false
53+
-Drelease-version=$release_version
54+
-Dtag=$version_tag
55+
-DdevelopmentVersion=$snapshot_version
56+
release:prepare

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,20 @@ mvn clean verify
106106

107107
If you are a maintainer, you can release a new version by doing the following:
108108

109-
- Merge the changes that need to be released into the `master` branch
110-
- Checkout on to master locally and pull the latest changes
111-
- Run `mvn release:prepare`, this will generate 2 commits that will bump the version of the github-java-client
112-
- Push these changes to master
113-
- Once the [release pipeline](https://github.com/spotify/github-java-client/actions/workflows/release.yml) has completed, the changes will have been tagged
114-
- [Navigate to the tag](https://github.com/spotify/github-java-client/tags) associated with the changes and generate a manual release
115-
- Once the release is generated, select the "Set as the latest release" checkbox and publish the release
109+
- Trigger the workflow [prepare-release](./.github/workflows/prepare-release.yml) through the
110+
[web UI](https://github.com/spotify/github-java-client/actions/workflows/prepare-release.yml)
111+
- Select whether the new release should be a `major`, `minor` or `patch` release
112+
- Trigger the release preparation on the `master` branch
113+
- Pushes of this workflow will trigger runs of the workflow
114+
[release](https://github.com/spotify/github-java-client/actions/workflows/release.yml)
115+
- Once the
116+
[release pipeline](https://github.com/spotify/github-java-client/actions/workflows/release.yml)
117+
has completed for the release tag, the new release will be available on Maven Central and the
118+
changes can be released on GitHub
119+
- [Navigate to the tag](https://github.com/spotify/github-java-client/tags) associated with the
120+
changes and generate a manual release
121+
- Once the release is generated, select the "Set as the latest release" checkbox and publish the
122+
release
116123

117124
## Notes about maturity
118125

0 commit comments

Comments
 (0)