skip to content

How do you supply Sonatype credentials and configure signing for an automated Central release with this plugin in CI?

level: middleimportance: must knowfreq 48%

answer

  1. Sonatype user token, not password
  2. ORG_GRADLE_PROJECT_ env mapping
  3. useInMemoryPgpKeys for CI
  4. every artifact needs a .asc
  5. secrets in CI store, gate on release branch

basics

~10 s

Pass 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 s

Two 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 lines
kotlin
signing {
    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

for a junior

Know that you need Sonatype credentials plus a PGP key, and that secrets shouldn't be hardcoded.

for a middle

Show the ORG_GRADLE_PROJECT_ env mapping and useInMemoryPgpKeys, and that every artifact needs an .asc.

for a senior

Discuss token vs password, branch-gating the release workflow, and how missing signatures fail the close validation.

for a principal

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.

context