Skip to content
Open
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
61 changes: 54 additions & 7 deletions .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ name: Gradle Package
on:
release:
types: [ created ]
workflow_dispatch:
inputs:
ref:
description: 'Git ref (branch or tag) to build'
required: false
default: 'main'
type: string
publish_to_remote:
description: 'Publish artifacts to Sonatype OSSRH and the Gradle Plugin Portal'
required: false
default: false
type: boolean

jobs:
build:
Expand All @@ -18,9 +30,18 @@ jobs:
permissions:
contents: read
packages: write
env:
OSSRH_TOKEN_USERNAME: ${{ secrets.OSSRH_TOKEN_USERNAME }}
OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_TOKEN_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
PUBLISH_TO_REMOTE: ${{ github.event_name == 'release' || inputs.publish_to_remote == true }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
Expand All @@ -32,19 +53,45 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

- name: Configure Gradle publishing credentials
run: |
mkdir -p "$HOME/.gradle"

{
if [[ -n "${OSSRH_TOKEN_USERNAME}" ]]; then
echo "ossrhTokenUsername=${OSSRH_TOKEN_USERNAME}"
fi
if [[ -n "${OSSRH_TOKEN_PASSWORD}" ]]; then
echo "ossrhTokenPassword=${OSSRH_TOKEN_PASSWORD}"
fi
if [[ -n "${SIGNING_PASSWORD}" ]]; then
echo "signingPassword=${SIGNING_PASSWORD}"
fi
if [[ -n "${SIGNING_KEY}" ]]; then
echo "signingKey=${SIGNING_KEY}"
fi
# Optionally expose SIGNING_KEY_ID for workflows that delegate signing to gpg.
if [[ -n "${SIGNING_KEY_ID}" ]]; then
echo "signing.keyId=${SIGNING_KEY_ID}"
fi
} > "$HOME/.gradle/gradle.properties"

chmod 600 "$HOME/.gradle/gradle.properties"

- name: Build with Gradle
run: ./gradlew build

# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
# the publishing section of your build.gradle
- name: Publish to GitHub Packages
- name: Publish to Sonatype OSSRH
if: env.PUBLISH_TO_REMOTE == 'true'
run: ./gradlew publish
env:
USERNAME: ${{ github.actor }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish to Gradle Plugin Portal
if: env.PUBLISH_TO_REMOTE == 'true'
run: ./gradlew publishPlugins
env:
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}

- name: Clean up Gradle credentials
if: always()
run: rm -f "$HOME/.gradle/gradle.properties"
145 changes: 145 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,148 @@ tasks.checkLicenses {
policiesFile = file("path/to/your/policies.json")
}
```

## Publishing setup TODO (Sonatype OSSRH)

Store secrets in `~/.gradle/gradle.properties`, your CI secret store, or environment
variables as noted below. The checklist assumes that the project is published through
the Sonatype OSSRH infrastructure with token-based authentication (preferred) and
falls back to legacy username/password credentials if necessary. When these
credentials (and the signing keys described below) are absent, running
`./gradlew publish` will skip the remote Sonatype repository and signing tasks so
that local verification builds continue to succeed.

- [ ] Confirm OSSRH project access
- Sign in at <https://s01.oss.sonatype.org/> with the Sonatype account that owns the
`io.github.eurofunk` groupId. If the namespace has not yet been approved, follow
the steps in the [OSSRH guide](https://central.sonatype.org/publish/publish-guide/)
to request access.
- Ensure you can see the `Staging Repositories` menu entry before attempting to publish.

- [ ] `ossrhTokenUsername` / `OSSRH_TOKEN_USERNAME`
- From the OSSRH web UI, open **Profile → User Token** and click **Access User Token**.
Copy the generated **Token Username** and store it as the Gradle property
`ossrhTokenUsername` or environment variable `OSSRH_TOKEN_USERNAME`.
- Legacy fallback: the build still honors `ossrhUsername` / `OSSRH_USERNAME` if tokens are
not available.

- [ ] `ossrhTokenPassword` / `OSSRH_TOKEN_PASSWORD`
- In the same dialog, copy the **Token Password** and store it as the Gradle property
`ossrhTokenPassword` or environment variable `OSSRH_TOKEN_PASSWORD`.
- Legacy fallback: provide `ossrhPassword` / `OSSRH_PASSWORD` if you must use the classic
credentials.

- [ ] (optional) Override publishing endpoints
- Releases deploy to `https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/` by default.
Override with the Gradle property `ossrhReleasesUrl` only if Sonatype assigns a different
host for your project.
- Snapshot artifacts deploy to `https://s01.oss.sonatype.org/content/repositories/snapshots/`.
Override with `ossrhSnapshotsUrl` if required.

- [ ] `signingKeyId` / `SIGNING_KEY_ID` *(optional)*
- Run `gpg --list-secret-keys --keyid-format=long` and copy the key ID for your publishing key (for example `ABCDEF12`).
- Provide the key ID only if you rely on the local GnuPG executable or need a stable identifier in signing reports. When Gradle loads the ASCII-armored private key directly (`signingKey`/`SIGNING_KEY`), the identifier can be omitted and Gradle derives it from the key material automatically.

- [ ] `signingKey` / `SIGNING_KEY`
- Export the ASCII-armored private key with `gpg --armor --export-secret-keys <KEY_ID>` (replace `<KEY_ID>` with the value above).
- Paste the full output—including the `BEGIN/END PGP PRIVATE KEY BLOCK` markers—into the Gradle property `signingKey` or environment variable `SIGNING_KEY`. A fingerprint alone is not sufficient; the entire private key block must be provided.
- The build accepts either the raw ASCII-armored export or a base64-encoded copy of that same text. Gradle checks that the payload contains a complete PGP secret key block; binary exports or truncated blocks are ignored and signing will be skipped with a warning.

- [ ] `signingPassword` / `SIGNING_PASSWORD`
- Use the passphrase chosen when creating the GPG key (from `gpg --full-generate-key`).
- Store it as the Gradle property `signingPassword` or environment variable `SIGNING_PASSWORD`.

- [ ] `signing.gnupg.keyName` / `SIGNING_GNUPG_KEY_NAME` *(only if using the local GPG executable)*
- If you prefer Gradle to call the local `gpg` binary, set this to the key name returned by `gpg --list-secret-keys` (for example `User Name <[email protected]>`).
- Ensure `signing.gnupg.executable` points to the desired GPG binary and that the key is available in the local keyring or CI agent.

### Using GitHub Actions secrets

When the project builds in GitHub Actions, reference the organization or repository
secrets as environment variables so Gradle can pick them up automatically. Secrets
exposed via the workflow `env` section are available to every step; you can also
scope them to the publish job only. Because the signing key is multi-line, the
workflow writes the values to `~/.gradle/gradle.properties` before invoking Gradle,
which ensures the full key material (including embedded newlines or `\n` escape
sequences) is preserved. Provide the ASCII-armored key directly or a base64-encoded
copy of that text export; the workflow feeds it to Gradle exactly as provided, and
the build validates that the payload contains the full private key before enabling
signing. Invalid or binary private keys are ignored so the publish run continues
without signing.

```yaml
jobs:
publish:
runs-on: ubuntu-latest
env:
OSSRH_TOKEN_USERNAME: ${{ secrets.OSSRH_TOKEN_USERNAME }}
OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_TOKEN_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} # optional; only required for gpg-based signing
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Configure Gradle publishing credentials
run: |
mkdir -p "$HOME/.gradle"

{
if [[ -n "${OSSRH_TOKEN_USERNAME}" ]]; then
echo "ossrhTokenUsername=${OSSRH_TOKEN_USERNAME}"
fi
if [[ -n "${OSSRH_TOKEN_PASSWORD}" ]]; then
echo "ossrhTokenPassword=${OSSRH_TOKEN_PASSWORD}"
fi
if [[ -n "${SIGNING_PASSWORD}" ]]; then
echo "signingPassword=${SIGNING_PASSWORD}"
fi
if [[ -n "${SIGNING_KEY}" ]]; then
echo "signingKey=${SIGNING_KEY}"
fi
# Optionally surface SIGNING_KEY_ID for workflows that delegate signing to gpg.
if [[ -n "${SIGNING_KEY_ID}" ]]; then
echo "signing.keyId=${SIGNING_KEY_ID}"
fi
} > "$HOME/.gradle/gradle.properties"

chmod 600 "$HOME/.gradle/gradle.properties"
- name: Publish artifacts
run: ./gradlew publish
- name: Clean up Gradle credentials
if: always()
run: rm -f "$HOME/.gradle/gradle.properties"
```

Gradle reads the variables listed above (and their legacy fallbacks) directly from the
environment, so no extra configuration is required. If you prefer Gradle properties
instead, write the secrets to `~/.gradle/gradle.properties` in a preceding workflow
step and delete the file afterwards to avoid leaking credentials in later jobs. The
example above performs these steps automatically.

### Running a release test

Use the **Gradle Package** workflow to perform a dry-run of the release
pipeline before cutting an actual GitHub release:

1. Open **Actions → Gradle Package** in the repository UI and click
**Run workflow**.
2. Pick the branch or tag that should be exercised from the **Branch**
drop-down. The release pipeline promotes builds from the `main`
branch, which is selected by default for manual runs. Choose a
different branch only if you need to validate work-in-progress
changes before merging them into `main`.
3. Leave **Publish artifacts to Sonatype OSSRH and the Gradle Plugin Portal**
disabled to run a local verification. The workflow will build the
project, resolve signing, and skip the remote publish steps.
4. When you are satisfied with the dry-run, create a GitHub release from the
branch that should ship (typically `main`) and the workflow will execute
again with publishing enabled.

This mirrors the branch picker shown in the GitHub release dialog and makes it
easy to verify release changes from any branch before publishing.
Loading
Loading