This project publishes to Maven Central via the Sonatype Central Portal. The release is driven by GitHub Releases: creating a Release triggers .github/workflows/publish.yml, which stages, signs, and auto-finalises the deployment.
We follow Semantic Versioning:
- MAJOR — incompatible public API changes
- MINOR — backwards-compatible additions (new services, new methods, new optional fields)
- PATCH — backwards-compatible bug fixes
The Git tag and GitHub Release name use the v<version> form (e.g. v0.2.0), but the source of truth is VERSION= in gradle.properties — that's what ends up in the published artifact. The tag is convention; the property is law. They must match.
While on 0.x, breaking changes may ship in minor versions. Once we cut 1.0.0, the full semver contract applies.
-
Update the changelog
- Move items from
[Unreleased]into a new version section inCHANGELOG.md - Use the headings
Added/Changed/Deprecated/Removed/Fixed/Security - Update the compare links at the bottom
- Move items from
-
Bump the version — two files:
gradle.properties→VERSION=X.Y.Z(the source of truth)README.md→ the Gradle and Maven install snippets
Do not hand-edit any version constant in Java source.
HttpClientreads the version at runtime fromcom/lettr/version.properties, whichprocessResourcesinbuild.gradleexpands fromproject.version— so theUser-Agentheader followsgradle.propertiesautomatically. -
Verify locally
./gradlew build
-
Commit and push to
maingit add gradle.properties README.md CHANGELOG.md git commit -m "chore(release): 0.2.0" git push origin main -
Create a GitHub Release
- Go to the repo → Releases → Draft a new release
- Choose a tag → type
v0.2.0→ Create new tag: v0.2.0 on publish - Release title:
v0.2.0 - Description: paste the relevant section from
CHANGELOG.md - Click Publish release
Creating the Release creates the tag — no separate
git tag/git push --tagsneeded. -
Watch the workflow
- The
Publish to Maven Centralworkflow starts automatically - It runs
./gradlew publish, then POSTs to the Central Portal to finalise the deployment - Maven Central indexes the artifact within ~15–30 minutes; central.sonatype.com updates first
- The
Only needed when setting up the repo the first time.
-
Register the
com.lettrnamespace at central.sonatype.com (requires DNS TXT verification). -
Generate a Central Portal user token: click your name → View Account → Generate User Token.
-
Generate a GPG key for signing artifacts:
gpg --gen-key gpg --list-keys --keyid-format long gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
-
Add 4 secrets to the GitHub repo (
Settings → Secrets and variables → Actions):Secret Value CENTRAL_PORTAL_TOKEN_USERNAMEToken username from step 2 CENTRAL_PORTAL_TOKEN_PASSWORDToken password from step 2 SIGNING_KEYOutput of gpg --export-secret-keys --armor YOUR_KEY_IDSIGNING_PASSWORDThe passphrase set during gpg --gen-key
Useful for dry-runs against the staging repository.
export CENTRAL_PORTAL_TOKEN_USERNAME=your-token-username
export CENTRAL_PORTAL_TOKEN_PASSWORD=your-token-password
export SIGNING_KEY="$(gpg --export-secret-keys --armor YOUR_KEY_ID)"
export SIGNING_PASSWORD=your-passphrase
./gradlew publishThis stages the artifact on the Central Portal but does not finalise it — log in to the portal to inspect or drop the staged deployment.
- Validation failed on the Central Portal — missing POM metadata, missing Javadoc/sources JARs, or unsigned artifacts. Check the workflow log.
401 Unauthorizedduring publish — theCENTRAL_PORTAL_TOKEN_*secrets are wrong or expired. Regenerate in the portal.- Finalisation step fails but publish succeeded — the artifact is staged but not live. Log in to the Central Portal and publish manually, or re-run the workflow's finalise step.
- Version already exists — Maven Central is immutable. Bump the version and release again; you cannot overwrite.