From 69479e126ab8ebeebe75483ded59f67e2ce93dfe Mon Sep 17 00:00:00 2001 From: Oleksandr Mandryk <2678920+omandryk@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:13:41 -0400 Subject: [PATCH] build: add gated Maven Central release pipeline --- .github/workflows/release.yml | 71 +++++++++++++++++++++++++++++++++++ README.md | 2 + RELEASING.md | 54 ++++++++++++++++++++++++++ pom.xml | 56 ++++++++++++++++++--------- 4 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 RELEASING.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ddb48b2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: Release to Maven Central + +on: + workflow_dispatch: + inputs: + mode: + description: Verify the release build or upload a signed bundle for manual publishing + required: true + default: verify + type: choice + options: + - verify + - upload + +permissions: + contents: read + +jobs: + verify: + if: inputs.mode == 'verify' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '8' + cache: maven + - run: mvn --batch-mode --no-transfer-progress clean verify + + upload: + if: inputs.mode == 'upload' + runs-on: ubuntu-latest + environment: maven-central + steps: + - name: Require a release tag + shell: bash + run: | + if [[ "${GITHUB_REF_TYPE}" != "tag" || "${GITHUB_REF_NAME}" != v* ]]; then + echo "Upload mode must be dispatched from a v* release tag." >&2 + exit 1 + fi + - uses: actions/checkout@v7 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '8' + cache: maven + server-id: central + server-username: CENTRAL_USERNAME + server-password: CENTRAL_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + - name: Confirm a non-SNAPSHOT project version + shell: bash + run: | + project_version="$(mvn --quiet help:evaluate -Dexpression=project.version -DforceStdout)" + if [[ "${project_version}" == *-SNAPSHOT ]]; then + echo "Refusing to upload SNAPSHOT version ${project_version}." >&2 + exit 1 + fi + if [[ "${GITHUB_REF_NAME}" != "v${project_version}" ]]; then + echo "Tag ${GITHUB_REF_NAME} does not match project version ${project_version}." >&2 + exit 1 + fi + - name: Upload signed bundle for Central validation + run: mvn --batch-mode --no-transfer-progress clean deploy -Prelease + env: + CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/README.md b/README.md index 2bb05b9..bf43a32 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ The 1.x client does not implement WooCommerce HTTPS Basic Authentication. Use it mvn verify -Dgpg.skip=true ``` +Maintainers preparing a Central release should follow [RELEASING.md](RELEASING.md). + See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before opening an issue or pull request. ## License diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..a3f77c2 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,54 @@ +# Releasing + +Releases use the Central Publisher Portal and preserve the coordinates +`com.icoderman:wc-api-java`. The release profile uses +`org.sonatype.central:central-publishing-maven-plugin` with automatic publishing +disabled. Uploading a bundle therefore does not make it public; a maintainer must +review and publish the validated deployment in the Central Portal. + +## Safety gates + +- Normal pushes and pull requests never invoke the release workflow. +- The workflow is manual and defaults to `verify` mode. +- `upload` mode uses the protected `maven-central` GitHub environment and only + accepts a `v*` tag matching the non-SNAPSHOT POM version. +- The Central plugin has `false`. +- Publishing the validated deployment in Central is a separate, explicit, + irreversible action. + +Configure the `maven-central` GitHub environment with required reviewers before +adding secrets. Store these environment secrets there: + +- `CENTRAL_USERNAME`: username from a newly generated Central Portal user token. +- `CENTRAL_PASSWORD`: password from that token. +- `GPG_PRIVATE_KEY`: ASCII-armored export of the signing private key. +- `GPG_PASSPHRASE`: passphrase for the signing key. + +Never commit credentials, private keys, passphrases, Maven `settings.xml`, or a +recovered GnuPG home. The historical signing key fingerprint is +`161A 7A08 F89C 1657 FC4F 0B75 DE35 65D0 3185 1B72`. + +## Release sequence + +1. Verify `master` and prepare release notes. +2. Change the POM from `1.5-SNAPSHOT` to `1.5.0` in a reviewed release PR. +3. Merge with the repository's noreply commit identity and wait for CI. +4. Create and push the signed/approved `v1.5.0` tag. +5. Dispatch this workflow from `v1.5.0` in `upload` mode. +6. Inspect the validated deployment in the Central Portal. +7. Publish it only after explicit owner approval. +8. Resolve the artifact from a clean Maven consumer before creating the GitHub + Release and announcing availability. + +For a credential-free build check, dispatch `verify` mode or run: + +```bash +mvn clean verify +``` + +For a local signed-bundle check without deploying, use an isolated GnuPG home and +run: + +```bash +GNUPGHOME=/path/to/isolated/.gnupg mvn clean verify -Prelease +``` diff --git a/pom.xml b/pom.xml index 7b7551b..99fde66 100644 --- a/pom.xml +++ b/pom.xml @@ -107,24 +107,6 @@ - - org.apache.maven.plugins - maven-gpg-plugin - 3.2.8 - - true - - - - sign-artifacts - verify - - sign - - - - - org.apache.maven.plugins maven-release-plugin @@ -134,4 +116,42 @@ + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.8 + + true + + + + sign-artifacts + verify + + sign + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.11.0 + true + + central + false + + + + + + +