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
+
+
+
+
+
+
+