Release to Maven Central #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release to Maven Central | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Release version (e.g., 0.1.0)' | |
| required: true | |
| permissions: read-all | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 | |
| - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '25' | |
| cache: 'maven' | |
| server-id: central | |
| server-username: MAVEN_USERNAME | |
| server-password: MAVEN_PASSWORD | |
| gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} | |
| gpg-passphrase: MAVEN_GPG_PASSPHRASE | |
| - name: Configure git identity and non-interactive GPG for signed commit/tag | |
| run: | | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git config user.name "github-actions[bot]" | |
| # Use the GPG key imported by setup-java (MAVEN_GPG_PRIVATE_KEY) for | |
| # both commit and tag signing — same trust path as the artifact. | |
| KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}') | |
| if [ -z "$KEYID" ]; then | |
| echo "no GPG secret key in agent — release-java.yml needs MAVEN_GPG_PRIVATE_KEY" >&2 | |
| exit 1 | |
| fi | |
| git config user.signingkey "$KEYID" | |
| git config gpg.format openpgp | |
| git config commit.gpgsign true | |
| git config tag.gpgsign true | |
| # Reviewer finding cf64b44d (RAN-47, R5-1): | |
| # `git commit -S` / `git tag -s` invoke gpg interactively by default | |
| # and fail in non-interactive Actions shells when the imported key | |
| # has a passphrase. setup-java only wires the passphrase for Maven | |
| # signing (via MAVEN_GPG_PASSPHRASE in settings.xml); git itself | |
| # has no equivalent autoconfig. Configure the gpg-agent for loopback | |
| # pinentry, point gpg.program at a thin wrapper that injects | |
| # --batch / --pinentry-mode loopback / --passphrase from | |
| # MAVEN_GPG_PASSPHRASE, so signing succeeds non-interactively. | |
| mkdir -p "$HOME/.gnupg" | |
| chmod 700 "$HOME/.gnupg" | |
| printf '%s\n' 'use-agent' 'pinentry-mode loopback' > "$HOME/.gnupg/gpg.conf" | |
| printf '%s\n' 'allow-loopback-pinentry' > "$HOME/.gnupg/gpg-agent.conf" | |
| gpgconf --kill gpg-agent || true | |
| # Wrapper script: git invokes this with the same flags as gpg. | |
| # We exec into gpg with --batch + loopback + the passphrase from | |
| # the env (MAVEN_GPG_PASSPHRASE is set on each step that signs). | |
| cat > "$HOME/.gnupg/gpg-loopback.sh" <<'WRAPPER' | |
| #!/usr/bin/env bash | |
| # Non-interactive gpg wrapper for `git commit -S` / `git tag -s`. | |
| # MAVEN_GPG_PASSPHRASE is set on the workflow step that signs. | |
| exec gpg --batch --yes --pinentry-mode loopback \ | |
| --passphrase "${MAVEN_GPG_PASSPHRASE:-}" "$@" | |
| WRAPPER | |
| chmod +x "$HOME/.gnupg/gpg-loopback.sh" | |
| git config gpg.program "$HOME/.gnupg/gpg-loopback.sh" | |
| - name: Set release version and create signed release commit | |
| env: | |
| RELEASE_VERSION: ${{ inputs.version }} | |
| # Picked up by the gpg-loopback wrapper script for `git commit -S`. | |
| MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| # Commit the version bump on a detached HEAD off the workflow's | |
| # checkout. The commit is reachable only via the tag created below — | |
| # no push to `main`, so this works under branch protection. | |
| # The commit captures the exact source tree that the deploy step | |
| # will build from, fixing the prior "tag diverges from released | |
| # artifact" gap (Reviewer finding 47b718b9). | |
| run: | | |
| mvn -B -ntp versions:set -DnewVersion="$RELEASE_VERSION" -DgenerateBackupPoms=false | |
| git add pom.xml | |
| git commit -S -m "chore(release): ${RELEASE_VERSION}" | |
| - name: Deploy to Maven Central | |
| env: | |
| MAVEN_USERNAME: ${{ secrets.OSS_NEXUS_USER }} | |
| MAVEN_PASSWORD: ${{ secrets.OSS_NEXUS_PASS }} | |
| MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| run: mvn -B -ntp -P release clean deploy | |
| - name: Create signed annotated tag and push | |
| env: | |
| RELEASE_VERSION: ${{ inputs.version }} | |
| MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| # Annotated + GPG-signed tag pointing at the release commit (the | |
| # current HEAD after the commit step above). Push only the tag — | |
| # the release commit lives only as a tag-reachable object so we | |
| # never need to update `main`, and branch protection stays clean. | |
| run: | | |
| git tag -s "v${RELEASE_VERSION}" -m "codeiq ${RELEASE_VERSION}" | |
| git push origin "refs/tags/v${RELEASE_VERSION}" | |
| - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 | |
| with: | |
| tag_name: v${{ inputs.version }} | |
| generate_release_notes: true | |
| files: | | |
| target/code-iq-*-cli.jar |