How do you supply Sonatype credentials and configure signing for an automated Central release with this plugin in CI?
answer
- Sonatype user token, not password
- ORG_GRADLE_PROJECT_ env mapping
- useInMemoryPgpKeys for CI
- every artifact needs a .asc
- secrets in CI store, gate on release branch
basics
~10 sPass the Sonatype user-token as sonatypeUsername/sonatypePassword (Gradle properties or env vars), and configure the signing plugin with an in-memory PGP key from CI secrets via useInMemoryPgpKeys. Never hardcode secrets.
solid answer
~40 sTwo secrets are in play: **Sonatype credentials** and a **PGP signing key**. The plugin reads credentials from the `sonatype { }` block, but by convention you supply them via Gradle properties `sonatypeUsername`/`sonatypePassword` (or `OSSRH_USERNAME`/`OSSRH_PASSWORD`-style env vars mapped through `ORG_GRADLE_PROJECT_sonatypeUsername`). Use a **Central Portal user token**, not your account password. For signing, apply the `signing` plugin and, in CI where there's no GPG keyring, use an **in-memory key**: ```kotlin signing { val key = System.getenv("SIGNING_KEY") val pwd = System.getenv("SIGNING_PASSWORD") useInMemoryPgpKeys(key, pwd) sign(publishing.publications) } ``` Every artifact must carry a `.asc` signature or Sonatype's **close** validation rejects the repo. Store the ASCII-armored key and passphrase as CI secrets, and gate the publish/release on a real release branch so PRs never attempt it.
code
kotlin · 15 linessigning {
val key = System.getenv("SIGNING_KEY") // armored private key
val pwd = System.getenv("SIGNING_PASSWORD")
if (key != null) useInMemoryPgpKeys(key, pwd)
sign(publishing.publications)
}
nexusPublishing {
repositories {
sonatype {
username.set(providers.gradleProperty("sonatypeUsername"))
password.set(providers.gradleProperty("sonatypePassword"))
}
}
}go deeper
Know that you need Sonatype credentials plus a PGP key, and that secrets shouldn't be hardcoded.
Show the ORG_GRADLE_PROJECT_ env mapping and useInMemoryPgpKeys, and that every artifact needs an .asc.
Discuss token vs password, branch-gating the release workflow, and how missing signatures fail the close validation.
Own secret governance: rotation policy, scoping tokens, restricting which branches/identities may release, and avoiding key material on disk.
## Two distinct secrets Automated Central publishing needs **both**: 1. **Sonatype credentials** — to authenticate against the staging REST API (open/close/release) and to upload. 2. **A PGP key** — to sign every artifact; Central requires valid detached `.asc` signatures. ## Credentials The `gradle-nexus.publish-plugin` resolves credentials from the `sonatype { }` block. The idiomatic approach is **not** to inline them but to rely on Gradle properties: ```kotlin nexusPublishing { repositories { sonatype { username.set(providers.gradleProperty("sonatypeUsername")) password.set(providers.gradleProperty("sonatypePassword")) } } } ``` In CI you inject them as environment variables using Gradle's `ORG_GRADLE_PROJECT_` prefix: `ORG_GRADLE_PROJECT_sonatypeUsername` and `..._sonatypePassword` become the `sonatypeUsername`/`sonatypePassword` properties automatically. Always use a **user token** from the Central Portal account page, never the raw password — tokens are revocable and scoped. ## Signing in CI with an in-memory key Locally you might have a GPG keyring, but CI runners are ephemeral and have none. The `signing` plugin supports `useInMemoryPgpKeys(secretKey, password)` (or the subkey-id overload) so you can feed an ASCII-armored private key straight from a secret: ```kotlin plugins { signing } signing { useInMemoryPgpKeys( System.getenv("SIGNING_KEY"), // ASCII-armored private key System.getenv("SIGNING_PASSWORD") // passphrase ) sign(publishing.publications["maven"]) } ``` Export the key with `gpg --armor --export-secret-keys KEYID` and store the whole block (including the BEGIN/END lines) as a single CI secret. ## Why close fails without signatures Sonatype's close validation requires a `.asc` for **each** published file. Forgetting `sign(...)`, or signing only some publications, makes close fail with a missing-signature rule violation — a very common first-release error. ## Security hygiene - Never commit secrets to `gradle.properties` in VCS; keep them in CI secret stores or `~/.gradle/gradle.properties` locally. - Restrict the release workflow to protected branches/tags so forks and PRs can't trigger a publish. - Prefer revocable user tokens; rotate on leak.
- Why use a Sonatype user token instead of your account password?Tokens are revocable, scoped, and can be rotated without changing your login; a leaked token is far easier to contain than a leaked password.
- Why is useInMemoryPgpKeys preferred over a keyring file in CI?CI runners are ephemeral with no GPG keyring; an in-memory key fed from a secret avoids writing key material to disk and keeps the runner stateless.
- What error appears at close time if signing is misconfigured?A validation failure citing missing detached PGP signatures (.asc) for one or more artifacts.
saying these in an interview costs you the question
- Hardcoding credentials or the PGP key in checked-in gradle.properties.
- Signing only the main jar and forgetting sources/javadoc/POM signatures.
- Using the account password instead of a revocable user token.