Skip to main content

From Latest to Locked: Pinning jPOS SNAPSHOTs

· 4 min read
Alejandro Revilla
jPOS project founder
AR Agent
AI assistant

In Latest and Greatest, we recommended a simple rule: use the latest -SNAPSHOT while developing, then pin the exact timestamped build that passed QA before deploying it.

The rule is simple. The manual version was not.

Someone had to find Maven metadata, copy the timestamped version, edit gradle/libs.versions.toml, make sure every related library was considered, and later remember which value used to be a moving snapshot. That is precisely the kind of release step that should be boring and repeatable.

jPOS Gradle Plugin 0.0.19 adds three tasks for that job: pins, pin, and unpin.

See what is moving

Start with pins:

./gradlew :pins

On MGL, that reports the snapshot-managed entries in the version catalog and the newest timestamped build available from the project's declared Maven repositories:

> Task :pins
jpos 3.0.2-SNAPSHOT snapshot latest build: 3.0.2-20260720.022713-26
jposee 3.0.2-SNAPSHOT snapshot latest build: 3.0.2-20260714.003334-21

The leading : matters in a multi-project build: it runs the root task once, rather than asking every subproject to print the same catalog.

pins does not modify anything. It is the release conversation in one command: these are the versions still following the development line, and these are the concrete builds available to lock down.

Pin the build you tested

To pin every snapshot-managed version in the catalog to its latest available build:

./gradlew :pin

For a small, explicit change, pin just jPOS:

./gradlew :pin --ref jpos --to 3.0.2-20260720.022713-26

--to is useful when QA has already selected a particular candidate. Without it, pin resolves the latest timestamped snapshot from the configured repositories.

The task changes only the relevant catalog line and records where it came from:

-jpos = "3.0.2-SNAPSHOT"
+jpos = "3.0.2-20260720.022713-26" # pinned-from 3.0.2-SNAPSHOT

That diff is the important artifact. Commit it on the certification or release branch, run the required tests against it, and deploy that commit. The version is no longer a moving target; the build used tomorrow is the one QA approved today.

For a release, the default :pin is usually the right choice. It pins every eligible snapshot entry, not only jpos. A build is reproducible only when the related jPOS-EE and in-house snapshot dependencies are just as intentional.

Return to the development line

The marker is not decoration. It lets the plugin restore the original value without another manual edit:

./gradlew :unpin --ref jpos

or, for every pinned entry:

./gradlew :unpin

The result is the original catalog line again:

jpos = "3.0.2-SNAPSHOT"

Quote style and surrounding comments are preserved. Re-pinning an already pinned entry also keeps the original -SNAPSHOT marker, so the round trip remains clear.

In practice, a development branch will normally remain on -SNAPSHOT while a QA or release branch carries the pin commit. unpin is still valuable for an experiment, a short-lived certification branch, or any workflow where the same branch must deliberately rejoin the moving line.

A small release workflow

The workflow now looks like this:

  1. Develop against the latest snapshots.
  2. Run ./gradlew :pins when preparing QA.
  3. Pin all snapshots, or choose the exact approved candidate with --ref and --to.
  4. Commit the catalog change, test it, and promote that commit.
  5. Keep development on -SNAPSHOT, or use unpin when it is time to resume following the active line.

This is not a replacement for Gradle dependency locking or a general-purpose supply-chain policy. It solves one focused problem: moving Maven snapshots in a jPOS version catalog. It makes the transition from latest while building to exact while deploying explicit, reviewable, and reversible.

That is the balance we want: stay close to the current work without making a release depend on whatever happened to be published next.