skip to content

Walk through the Gradle tasks the publish-plugin contributes and the order you'd invoke them to ship a release.

level: middleimportance: must knowfreq 50%

answer

  1. initialize → publishToSonatype → close → release
  2. task names embed repo name
  3. closeAndRelease chains both
  4. staging id in build state — run same invocation
  5. close/release poll the API

basics

~10 s

Run publishToSonatype to create the staging repo and upload artifacts, then closeAndReleaseSonatypeStagingRepository to validate and promote to Central. Often combined: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository.

solid answer

~40 s

The plugin derives task names from the repository name you configure (`sonatype` → `...Sonatype...`). The pipeline is: 1. `initializeSonatypeStagingRepository` — opens a fresh staging repo and remembers its server id. 2. `publishToSonatype` — runs your `maven-publish` `publishAllPublicationsToSonatypeRepository`, uploading every artifact + signature into that staging repo. 3. `closeSonatypeStagingRepository` — closes it, triggering Sonatype's validation. 4. `releaseSonatypeStagingRepository` — promotes the closed repo to Central. The convenience task `closeAndReleaseSonatypeStagingRepository` runs 3 and 4 together. A typical CI invocation is `./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository`. Because the staging-repo id is shared via build state, you must run these in the **same Gradle invocation** (or persist the id) so close/release act on the repo you just published to.

code

bash · 3 lines
bash
# One invocation so the staging-repo id is shared across tasks
./gradlew clean publishToSonatype \
         closeAndReleaseSonatypeStagingRepository --no-configuration-cache

go deeper

for a junior

Recall the happy-path command: publishToSonatype then closeAndReleaseSonatypeStagingRepository.

for a middle

Name each task, explain the order, and that task names mirror the configured repo name.

for a senior

Explain the shared-staging-id build-state constraint and the polling/timeout behaviour for async close/release.

for a principal

Design the CI topology (single job vs. split with persisted id), failure recovery (drop on failed close), and timeout governance for flaky Central days.

## Task naming Every task name embeds the repository name you set inside `nexusPublishing { repositories { sonatype { } } }`. Configure a repo named `sonatype` and you get `publishToSonatype`, `closeSonatypeStagingRepository`, etc. Name it `myNexus` and the tasks become `publishToMyNexus`, `closeMyNexusStagingRepository`. This matters because you can configure multiple Nexus targets. ## The full ordered flow 1. **initialize** — `initializeSonatypeStagingRepository` calls the staging API to **open** a new repository and captures its id (e.g. `comexample-1042`). This id is held in build state for the rest of the invocation. 2. **upload** — `publishToSonatype` is an alias that depends on `publishAllPublicationsToSonatypeRepository` from `maven-publish`; the plugin has injected the staging URL so artifacts land in the just-opened repo. This is where signing matters: each artifact needs a `.asc`. 3. **close** — `closeSonatypeStagingRepository` closes the repo; Sonatype validates. The task **polls** the API until the transition finishes or times out. 4. **release** — `releaseSonatypeStagingRepository` promotes; it also polls. 5. **closeAndRelease** — `closeAndReleaseSonatypeStagingRepository` chains close then release. ## Why same-invocation matters The opened staging-repo id lives in **in-memory build state**. If you run `publishToSonatype` in one `gradle` call and `closeAndReleaseSonatypeStagingRepository` in a separate call, the second call has no id and will fail (or scan for repos). Either run them together, or pass `-Pio.github.gradle-nexus...stagingRepositoryId=...` to target a specific repo explicitly. ## Practical CI line ```bash ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository \ -PsonatypeUsername=$SONATYPE_USER -PsonatypePassword=$SONATYPE_PW ``` ## Timeouts Close and release are asynchronous on Sonatype's side, so the plugin polls. Slow Central days may need a longer `clientTimeout`/`numberOfRetries` in `nexusPublishing { }`.

  • Why can splitting publish and close into two Gradle invocations fail?
    The opened staging-repo id is kept in in-memory build state; a second invocation has lost it, so close/release have no target unless you pass the id explicitly.
  • How does the plugin know when a close has finished, given it's asynchronous?
    It polls the Nexus staging API on an interval up to a configurable timeout/retry count before succeeding or failing.
  • Where do task names like 'publishToSonatype' come from?
    From the repository name you declare inside nexusPublishing; rename it and every task name changes accordingly.

saying these in an interview costs you the question

  • Claiming you can publish in one CI job and close in a totally separate job without persisting the staging-repo id.
  • Forgetting that artifacts must be signed before close, or close validation fails.

context