Releasing¶
Where the version lives¶
One place: converterVersion in gradle.properties. It drives
- the distribution tarball name —
linked-archi-converters-<version>.tar Implementation-Versionin every JAR manifest- what each CLI prints for
--version, read from that manifest at runtime bycore/.../cli/ManifestVersionProvider.kt - the Package Registry version path on default-branch builds
There are no hardcoded version strings in the CLIs, so --version cannot drift
away from the artifact it came from.
On a tagged build, CI passes -PconverterVersion=<tag without leading v>, so
the git tag is authoritative even if gradle.properties has drifted. A tag
v1.2.0 produces:
| Artifact | Value |
|---|---|
| Distribution | linked-archi-converters-1.2.0.tar |
JAR manifest / --version |
1.2.0 |
| Package Registry path | 1.2.0 |
| Container image | :1.2.0 and :v1.2.0 (plus :latest, :<short-sha>) |
| GitLab Release | links the registry assets for 1.2.0 |
Cutting a release with the script¶
# Prepare everything locally, print the push commands
scripts/release.sh 1.2.0
# Same, then push the branch and the tag
scripts/release.sh 1.2.0 --push
# Show what would happen, change nothing
scripts/release.sh 1.2.0 --dry-run
The script refuses to continue unless the release is actually ready. It checks
that the working tree is clean, that you are on main, that the version is
MAJOR.MINOR.PATCH and not a SNAPSHOT, that the tag is unused locally and on
origin, that CHANGELOG.md has a ## <version> — <date> heading, and that
docs/downloads.md advertises this version.
Under --dry-run those same problems are downgraded to warnings so you still see
the full sequence on a work-in-progress tree; the run ends with either
No blockers: a real run would go all the way through. or a count of what would
stop it. The version argument is required in every mode — without it the script
prints the help and an error rather than silently doing nothing.
Then it sets converterVersion, runs clean build distTar distZip and asserts
the tarball name and every JAR manifest report the expected version, commits
release: <version>, tags v<version> on that commit, and finally commits
a bump to the next -SNAPSHOT.
| Option | Effect |
|---|---|
--next <version> |
Development version after the release. Default: next minor as -SNAPSHOT |
--push |
Push the branch and the tag to origin |
--dry-run |
Print the whole plan, change nothing. Readiness problems are reported as warnings instead of stopping the run, and the exit code is 1 if any were found |
--skip-build |
Skip build verification — faster, but the artifacts go unchecked |
--allow-branch |
Cut from a branch other than main |
--rename-unreleased |
Promote a ## Unreleased heading to ## <version> — <today> |
--manual |
Print the equivalent commands to run by hand, then exit |
Nothing is pushed without --push, and no command rewrites history. Without
--push the script prints both the push commands and the undo commands.
It is POSIX sh (verified against dash), depending only on git, awk,
grep, cut, date and unzip. The two file edits it makes — converterVersion
in gradle.properties and the optional CHANGELOG heading rename — go through a
read-and-rewrite helper rather than sed -i, whose syntax differs between GNU and
BSD/macOS. Each edit touches exactly the matching line and fails loudly if the
line isn't there.
Why the trailing SNAPSHOT bump matters
Default-branch builds publish to the Package Registry under whatever
converterVersion says. If it stays at the released version after tagging,
every subsequent merge to main republishes over 1.2.0, and a version
people pinned stops being immutable. The bump is the last step for that
reason, and it is the step most easily forgotten by hand.
Doing it manually¶
To get the exact commands for a given version, with the file edits called out and the next development version already worked out, ask the script:
That prints and exits — it touches nothing. The output is generated from the same values the automated path uses, so it cannot drift from what the script does. The outline:
- Add a
## <version> — <date>heading inCHANGELOG.mdabove the entries this release contains. That file is published as the Release Notes page, so it is what users read. - Set
converterVersion=<version>ingradle.properties. - Update
docs/downloads.md: the current-release block, the tarball name, the registry example and the Docker tag table. - Verify:
./gradlew clean build distTar— confirmbuild/distributions/linked-archi-converters-<version>.tarandjava -jar converter-bpmn/build/libs/bpmn2linkedarchi.jar --version. - Commit, then
git tag -a v<version> -m "linked-archi-converters <version>". git push origin main && git push origin v<version>.- Bump
converterVersionto the next-SNAPSHOTand commit.
What the tag pipeline does¶
| Job | On tags |
|---|---|
build |
Builds with the version taken from the tag |
publish |
Uploads the tarball and the five fat JARs to the generic Package Registry under the plain version |
release |
Creates a GitLab Release linking those assets, noting the Java 25 requirement and the changelog |
docker |
Pushes :<version>, :v<version>, :latest and :<short-sha> |
pages |
Default branch only — refreshes the static downloads and the docs site |
Releases are immutable: the Package Registry path for a version is written once. If a release is wrong, cut the next patch version rather than retagging.