Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The build is already wired for Central publishing (the `release` Maven profile adds sources
and javadoc jars, GPG signing, and the Central Portal upload). Two one-time setup steps must
be done by a human; after that, releases are a short procedure.
be done by a human; after that, a release is one script run: `./release.sh <version>`.

## One-time setup

Expand Down Expand Up @@ -42,35 +42,36 @@ first signing of a session.

## Release procedure

```bash
# 1. Start from a green main
git checkout main && git pull && mvn verify

# 2. Set the release version (drop -SNAPSHOT) in all three POMs
mvn versions:set -DnewVersion=0.1.0 && mvn versions:commit
Run the release script from a clean, up-to-date `main`:

# 3. Commit and tag (via PR per repo convention, or directly if you prefer for releases)
git commit -am "release: 0.1.0" && git tag v0.1.0
```bash
./release.sh --dry-run 0.1.1 # preflight only: git state, credentials, GPG, mvn verify
./release.sh 0.1.1 # the real thing
```

# 4. Build, sign and upload to the Central Portal
mvn clean deploy -Prelease
The script does, in order:

# 5. Publish: the artifacts land in https://central.sonatype.com/publishing as a
# validated deployment — press "Publish" there. (To skip the manual press, add
# <autoPublish>true</autoPublish> to the central-publishing-maven-plugin config.)
1. **Preflight** — verifies you're on a clean `main` in sync with origin, the `v0.1.1` tag
doesn't exist, `~/.m2/settings.xml` has real `central` credentials, GPG can actually
sign (fails early on the pinentry/`GPG_TTY` problem), and `mvn verify` is green.
2. Sets the version in all three POMs, commits `release: 0.1.1`, tags `v0.1.1`.
3. `mvn clean deploy -Prelease` — builds, signs, and uploads to the Central Portal.
4. Bumps to the next snapshot (defaults to the next minor, e.g. `0.2.0-SNAPSHOT`; pass a
second argument to override), commits, and pushes `main` plus the tag.
5. Prints the last manual step: press **Publish** on the validated deployment at
<https://central.sonatype.com/publishing>. (To skip the manual press, add
`<autoPublish>true</autoPublish>` to the central-publishing-maven-plugin config.)

# 6. Bump back to the next snapshot and push
mvn versions:set -DnewVersion=0.2.0-SNAPSHOT && mvn versions:commit
git commit -am "chore: bump to 0.2.0-SNAPSHOT" && git push && git push --tags
```
If anything fails before the push, nothing has left your machine; the script prints the
two-line local rollback (`git tag -d`, `git reset --hard origin/main`).

Artifacts appear on Maven Central (search.maven.org) within an hour of publishing:

```xml
<dependency>
<groupId>io.github.marcofanti</groupId>
<artifactId>aauth</artifactId> <!-- or aauth-signing for the signing layer only -->
<version>0.1.0</version>
<version>0.1.1</version>
</dependency>
```

Expand Down
127 changes: 127 additions & 0 deletions release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
# Release this library to Maven Central.
#
# Usage:
# ./release.sh [--dry-run] <release-version> [next-snapshot-version]
#
# Examples:
# ./release.sh 0.1.1 # releases 0.1.1, bumps back to 0.2.0-SNAPSHOT
# ./release.sh 0.2.0 0.3.0-SNAPSHOT # explicit next development version
# ./release.sh --dry-run 0.1.1 # preflight checks only, no changes
#
# One-time setup (Central Portal token, GPG key) is described in RELEASING.md.
set -euo pipefail

usage() {
grep '^#' "$0" | grep -v '^#!' | sed 's/^# \{0,1\}//' | head -10
exit 1
}

DRY_RUN=0
if [[ ${1:-} == "--dry-run" ]]; then
DRY_RUN=1
shift
fi

VERSION=${1:-}
[[ -n $VERSION ]] || usage
[[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
echo "ERROR: release version must be X.Y.Z (got: $VERSION)" >&2
exit 1
}

if [[ $# -ge 2 ]]; then
NEXT=$2
else
IFS=. read -r major minor _ <<<"$VERSION"
NEXT="$major.$((minor + 1)).0-SNAPSHOT"
fi
[[ $NEXT == *-SNAPSHOT ]] || {
echo "ERROR: next development version must end in -SNAPSHOT (got: $NEXT)" >&2
exit 1
}

say() { printf '\n==> %s\n' "$*"; }

# --- Preflight checks --------------------------------------------------------

say "Preflight: git state"
[[ $(git branch --show-current) == main ]] || {
echo "ERROR: releases must run from main (currently on $(git branch --show-current))" >&2
exit 1
}
git diff-index --quiet HEAD || {
echo "ERROR: working tree is not clean" >&2
exit 1
}
git fetch -q origin
[[ $(git rev-parse HEAD) == $(git rev-parse origin/main) ]] || {
echo "ERROR: local main is not in sync with origin/main" >&2
exit 1
}
if git rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null; then
echo "ERROR: tag v$VERSION already exists" >&2
exit 1
fi

say "Preflight: Central Portal credentials"
grep -q '<id>central</id>' ~/.m2/settings.xml || {
echo "ERROR: no <server><id>central</id> entry in ~/.m2/settings.xml (see RELEASING.md)" >&2
exit 1
}
if grep -q 'PASTE_TOKEN' ~/.m2/settings.xml; then
echo "ERROR: ~/.m2/settings.xml still has placeholder token values" >&2
exit 1
fi

say "Preflight: GPG signing"
if [[ -z ${GPG_TTY:-} ]] && tty -s; then
GPG_TTY=$(tty)
export GPG_TTY
fi
echo release-test | gpg --clearsign >/dev/null || {
echo "ERROR: gpg cannot sign (check pinentry-mac or GPG_TTY; see RELEASING.md)" >&2
exit 1
}

say "Preflight: build is green (mvn verify)"
mvn -q clean verify

if [[ $DRY_RUN -eq 1 ]]; then
say "Dry run OK. Would release $VERSION, tag v$VERSION, then bump to $NEXT."
exit 0
fi

# --- Release -----------------------------------------------------------------

rollback_hint() {
cat >&2 <<EOF

Release FAILED before completion. To roll back local state:
git tag -d v$VERSION 2>/dev/null || true
git reset --hard origin/main
Nothing has been pushed; re-run after fixing the problem.
EOF
}
trap rollback_hint ERR

say "Setting version $VERSION"
mvn -q versions:set -DnewVersion="$VERSION" && mvn -q versions:commit
git commit -aqm "release: $VERSION"
git tag "v$VERSION"

say "Building, signing and uploading to the Central Portal"
mvn clean deploy -Prelease

say "Bumping to $NEXT"
mvn -q versions:set -DnewVersion="$NEXT" && mvn -q versions:commit
git commit -aqm "chore: bump to $NEXT"

say "Pushing main and tags"
git push origin main
git push origin "v$VERSION"

trap - ERR
say "Done. Now press 'Publish' on the validated deployment:"
echo " https://central.sonatype.com/publishing"
echo "Artifacts sync to Maven Central within about an hour of publishing."