skip to content

Repositories and Credentials

Where publications are sent: declared repositories, credentials sourced from outside the build, mavenLocal, and Maven Central's requirements. Interviewers ask because credentials hardcoded in a build script are an instant red flag.

on this pageshow

explore

questions

25

What artifacts must a Maven Central release bundle contain beyond the main JAR, and why does Central reject a publication that is missing them?

level: juniorimportance: must knowfreq 62%

answer

  1. main + sources + javadoc + POM
  2. .asc signature per file
  3. withSourcesJar / withJavadocJar
  4. POM needs license/scm/developers
  5. validation is server-side

basics

~10 s

Each release must include the main JAR plus a sources JAR, a javadoc JAR, a POM, and PGP/GPG signatures (.asc) for every file. Central validation rejects bundles missing any of these.

solid answer

~40 s

Maven Central enforces a minimum bundle for every released coordinate: the primary artifact (usually the JAR), a `-sources.jar`, a `-javadoc.jar`, a complete POM (with name, description, URL, license, developers, SCM), and a detached PGP signature (`.asc`) for **each** of those files plus the POM. The sources/javadoc requirement exists so consumers can read code and docs in their IDE; signatures let anyone verify provenance against the publisher's public key. In Gradle you produce the extra JARs with `java { withSourcesJar(); withJavadocJar() }` and sign everything with the `signing` plugin applied to the `maven-publish` publication. Central's validation (Portal or legacy OSSRH) runs these checks server-side and fails the staging deployment if anything is absent or a signature does not verify.

code

kotlin · 8 lines
kotlin
java {
    withSourcesJar()
    withJavadocJar()
}

signing {
    sign(publishing.publications["maven"])
}

go deeper

for a junior

List the four artifact kinds plus signatures and note Central rejects incomplete bundles.

for a middle

Show the exact Gradle wiring (withSourcesJar/withJavadocJar + signing) and name the POM fields validated.

for a senior

Explain why immutability drives the contract and how Kotlin projects satisfy javadoc via Dokka.

for a principal

Frame it as a supply-chain/provenance guarantee and discuss enforcing the bundle contract across many modules via convention plugins.

## Why Maven Central has a bundle contract Maven Central is the default public repository for the JVM ecosystem. Because anything published there is **immutable and globally consumable forever**, Sonatype enforces a strict quality and provenance contract before a coordinate (`group:artifact:version`) goes live. A 'bundle' is the complete set of files uploaded for one version. ## The required files For a typical Java/Kotlin library version Central requires: - **Main artifact** — the `.jar` (or `.aar`, `.war`, etc.). - **Sources JAR** — `artifact-version-sources.jar`, so consumers can step into your code. - **Javadoc JAR** — `artifact-version-javadoc.jar`. (For Kotlin you can publish a stub/Dokka-generated jar; an empty javadoc jar with a placeholder is also commonly accepted, but a real one is better.) - **POM** — must carry `name`, `description`, `url`, at least one `license`, `developers`, and `scm` blocks. Central validates these fields. - **PGP signatures** — a detached ASCII-armored `.asc` for **every** file above, including the POM and the checksums. The public key must be discoverable on a public keyserver. ## Producing them in Gradle With `maven-publish`, `signing`, and the `java` plugin: ```kotlin plugins { `java-library` `maven-publish` signing } java { withSourcesJar() withJavadocJar() } publishing { publications { create<MavenPublication>("maven") { from(components["java"]) pom { name.set("my-lib") description.set("A useful library") url.set("https://github.com/me/my-lib") licenses { license { name.set("Apache-2.0") } } developers { developer { id.set("me"); name.set("Me") } } scm { url.set("https://github.com/me/my-lib") } } } } } signing { sign(publishing.publications["maven"]) } ``` `withSourcesJar()`/`withJavadocJar()` register `sourcesJar`/`javadocJar` tasks and wire them into the `java` component, so `from(components["java"])` automatically attaches them to the publication. `sign(...)` produces the `.asc` files at publish time. ## What validation checks When the bundle reaches Central (via the Central Portal upload or the legacy OSSRH staging repository), server-side validation verifies: presence of sources + javadoc, POM completeness, a valid signature on each file, and that the signing public key resolves on a keyserver. Any failure leaves the deployment in a failed/dropped state and it never goes public.

  • Kotlin libraries have no real Javadoc — how do people satisfy the javadoc-JAR requirement?
    Generate one with the Dokka plugin's javadoc-format task and attach it as the javadoc jar, or attach a minimal placeholder javadoc jar. Central requires the artifact to exist; it doesn't deeply inspect its contents.
  • Which POM fields does Central specifically validate?
    name, description, url, at least one license, developers, and scm. Missing any of these fails validation.

saying these in an interview costs you the question

  • Saying only the main JAR is needed for Central.
  • Claiming signatures are optional for public releases.
  • Confusing the javadoc/sources requirement with snapshot publishing (snapshots are more lenient — but that's a sibling topic).

context

open as a page

How do you supply a username and password to a Maven repository in a Gradle publishing block, and where does Gradle get those values from?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Inside the repository, call credentials(PasswordCredentials::class). Gradle then reads <repoName>Username and <repoName>Password from gradle.properties or environment variables, where <repoName> is the repository's name.

open as a page

What does the publishToMavenLocal task do, and where does it put your artifacts?

level: juniorimportance: must knowfreq 60%

basics

~10 s

publishToMavenLocal installs your built artifacts (jar, POM, metadata) into the local Maven cache at ~/.m2/repository, so other local builds on the same machine can resolve them via mavenLocal().

open as a page

How do you declare a target Maven repository to publish your artifacts to in the maven-publish plugin, and what is the minimum you must specify?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Inside the publishing { repositories { } } block add a maven { } repository and set its url. Optionally give it a name. That tells Gradle where to upload the published artifacts.

open as a page

What problem does the io.github.gradle-nexus.publish-plugin solve, and what would you have to do manually without it?

level: juniorimportance: must knowfreq 55%

basics

~10 s

It automates Sonatype staging: it creates a staging repository, publishes your artifacts into it, then closes and releases it from the build — so you don't click through the Nexus/Central Portal UI by hand.

open as a page

How does Sonatype namespace (groupId) verification work for Maven Central, and what must you do before you can publish under a given group?

level: middleimportance: must knowfreq 55%

basics

~20 s

Your groupId is a namespace you must prove you control. For a domain-based group you add a TXT DNS record; for a code-host group (io.github.user) you verify via the matching account. Central won't accept artifacts until the namespace is verified.

open as a page

How do you inject repository credentials from environment variables in CI without writing any gradle.properties file, and what exactly must the variables be named?

level: middleimportance: must knowfreq 60%

basics

~10 s

Set environment variables ORG_GRADLE_PROJECT_<repoName>Username and ORG_GRADLE_PROJECT_<repoName>Password. Gradle maps any ORG_GRADLE_PROJECT_<x> env var to project property <x>, so credentials(PasswordCredentials::class) picks them up automatically.

open as a page

How would you route a publication to a snapshots URL versus a releases URL based on whether the version ends with -SNAPSHOT?

level: middleimportance: must knowfreq 55%

basics

~10 s

Check whether version ends with -SNAPSHOT; if it does, point the repository url at the snapshots repo, otherwise at the releases repo. Typically a conditional expression on the repository's url.

open as a page

What publish tasks does Gradle auto-generate per declared Maven repository, and how does the repository name shape them?

level: middleimportance: must knowfreq 60%

basics

~10 s

For each repository named e.g. myCompany, Gradle generates publishAllPublicationsToMyCompanyRepository plus a publish<Pub>PublicationToMyCompanyRepository task per publication. The repository name is capitalised into the task name.

open as a page

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

level: middleimportance: must knowfreq 48%

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.

open as a page

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%

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.

open as a page

Why does Maven Central require PGP/GPG signatures on published artifacts, and how do you wire the Gradle signing plugin to satisfy it (including on CI)?

level: seniorimportance: must knowfreq 45%

basics

~20 s

Signatures let anyone verify an artifact came from the real publisher and wasn't tampered with. In Gradle you apply the signing plugin and sign(publication); the public key must be on a keyserver. On CI you provide the key/passphrase via the in-memory signing keys.

open as a page

Compare the Central Portal and the legacy OSSRH staging endpoints. What URLs/targets does each use and how does a Gradle build point at them?

level: middleimportance: should knowfreq 40%

basics

~10 s

Legacy OSSRH used per-account Nexus staging URLs like s01.oss.sonatype.org/service/local/staging/deploy/maven2. The new Central Portal uploads a bundle to central.sonatype.com via its Publisher API. New accounts use the Portal; OSSRH is being retired.

open as a page

Some artifact registries authenticate with a bearer/header token rather than basic username+password. How do you configure that in a Gradle repository?

level: middleimportance: should knowfreq 40%

basics

~10 s

Use credentials(HttpHeaderCredentials::class) and set a name (the header, e.g. Authorization or Private-Token) and value (the token). You must also add authentication { create<HttpHeaderAuthentication>("header") } so Gradle sends it as an HTTP header.

open as a page

What practices keep publishing credentials out of source control and logs, and what are common mistakes teams make here?

level: middleimportance: should knowfreq 45%

basics

~10 s

Never hardcode credentials in build scripts. Put them in ~/.gradle/gradle.properties (per-user, never committed) or inject via ORG_GRADLE_PROJECT_* env vars in CI. Use the credentials(PasswordCredentials::class) convention so values stay external.

open as a page

What problems can arise from consuming artifacts via mavenLocal, and how do you mitigate them?

level: middleimportance: should knowfreq 40%

basics

~10 s

mavenLocal() can serve stale or shadowing artifacts: a leftover ~/.m2 build can override the intended remote one, and results differ between machines. Mitigate by re-publishing, clearing the local entry, or avoiding mavenLocal in CI.

open as a page

How would you route a publish to a release repository for a release version and a snapshot repository for a -SNAPSHOT version using the repository url?

level: middleimportance: should knowfreq 45%

basics

~10 s

Set the repository url conditionally on the version: if version ends with -SNAPSHOT use the snapshot URL, otherwise the releases URL. Often two layout paths under one base host.

open as a page

How can you declare a local file-system Maven repository as a publish target, and when is that useful?

level: middleimportance: should knowfreq 35%

basics

~10 s

Point the repository url at a local directory via uri(layout.buildDirectory.dir("repo")). Gradle writes the full Maven layout there. Useful for testing publication output without hitting a real server.

open as a page

Walk through what happens to a release after Gradle uploads it: the staging lifecycle on Sonatype before it appears on Maven Central.

level: seniorimportance: should knowfreq 35%

basics

~20 s

After upload, the bundle lands in a staging area where Sonatype validates the artifacts (signatures, sources/javadoc, POM). If valid you release/publish it; it then syncs to the public Central index and mirrors within a short time.

open as a page

When exactly does Gradle require repository credentials to be present, and why does that timing matter for builds that don't publish?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Credentials resolved via credentials(PasswordCredentials::class) are lazy: Gradle only requires them when a task that uses the repository (like publish) is in the execution graph. So ./gradlew build works without them; only publishing fails if they're missing.

open as a page

If you declare both mavenLocal() and mavenCentral(), how does Gradle decide which one supplies a dependency, and why does order matter?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Gradle tries declared repositories in order until one provides the requested version. With mavenLocal() listed first, a matching local artifact is used before Central is ever queried — which can shadow the remote one.

open as a page

Why are -SNAPSHOT versions treated specially, and what does that mean for caching and for publishing the same version repeatedly?

level: seniorimportance: should knowfreq 38%

basics

~10 s

A -SNAPSHOT version is a mutable, 'changing' module: re-publishing the same coordinate is allowed and expected, and Gradle periodically re-checks it instead of caching forever. Release versions are immutable and cannot be overwritten.

open as a page

You need internal libraries published to a private repository but releases also mirrored to a public one. How do you model multiple publish repositories and control which build targets which?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Declare multiple named maven { } repositories in publishing.repositories. Each gets its own publishAllPublicationsTo<Name>Repository task, so CI invokes the one(s) appropriate for that build instead of the catch-all publish.

open as a page

In a multi-module build, how do you ensure all modules land in a single staging repository, and why does that matter?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Apply the plugin once at the root project. It opens one staging repo for the whole build and injects its URL into every subproject's maven-publish, so all modules are closed and released together as one atomic release.

open as a page

OSSRH is being sunset in favor of the Central Portal. How does that migration affect a build using the gradle-nexus publish-plugin, and what are your options?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The classic plugin targets the legacy OSSRH Nexus staging API. New Central Portal namespaces don't expose that API, so you either use OSSRH's Portal compatibility endpoint or switch to a Central-Portal-native plugin/publisher for newer setups.

open as a page