Skip to content

Releasing

Where the version lives

One place: converterVersion in gradle.properties. It drives

  • the distribution tarball name — linked-archi-converters-<version>.tar
  • Implementation-Version in every JAR manifest
  • what each CLI prints for --version, read from that manifest at runtime by core/.../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:

scripts/release.sh 1.2.0 --manual

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:

  1. Add a ## <version> — <date> heading in CHANGELOG.md above the entries this release contains. That file is published as the Release Notes page, so it is what users read.
  2. Set converterVersion=<version> in gradle.properties.
  3. Update docs/downloads.md: the current-release block, the tarball name, the registry example and the Docker tag table.
  4. Verify: ./gradlew clean build distTar — confirm build/distributions/linked-archi-converters-<version>.tar and java -jar converter-bpmn/build/libs/bpmn2linkedarchi.jar --version.
  5. Commit, then git tag -a v<version> -m "linked-archi-converters <version>".
  6. git push origin main && git push origin v<version>.
  7. Bump converterVersion to the next -SNAPSHOT and 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.