skip to content

Publication Models

Declaring what gets published: maven-publish and ivy-publish, POM metadata, coordinates, and the tasks Gradle generates from them. Interviewers ask because the publication is the contract your consumers actually see.

on this pageshow

explore

questions

25

How do you set the groupId, artifactId, and version on a MavenPublication, and how does that relate to project.group and project.version?

level: juniorimportance: must knowfreq 70%

answer

  1. groupId / artifactId / version
  2. defaults: project.group, project.name, project.version
  3. override per-publication
  4. multiple artifacts from one project
  5. override != mutating project.version

basics

~10 s

Inside a MavenPublication you set groupId, artifactId, and version directly. If you omit them, Gradle defaults groupId to project.group, version to project.version, and artifactId to project.name.

solid answer

~30 s

A `MavenPublication` carries three coordinate properties: `groupId`, `artifactId`, and `version`. When you don't set them, Gradle derives defaults — `groupId` from `project.group`, `version` from `project.version`, and `artifactId` from `project.name`. You can override any of them inside the publication block, which is common when one project produces several artifacts (e.g. a `-core` and a `-bom`) or when the published name should differ from the Gradle project name. Overriding here does **not** change `project.group`/`project.version`; it only affects this publication's published coordinates and the generated POM's `<groupId>/<artifactId>/<version>`. Setting `project.group`/`project.version` at the top level is the idiomatic way when all publications share one GAV.

code

kotlin · 10 lines
kotlin
publishing {
    publications {
        create<MavenPublication>("library") {
            from(components["java"])
            groupId = "com.acme"
            artifactId = "payments-core"
            version = "1.4.0"
        }
    }
}

go deeper

for a junior

Name the three coordinates and the three project defaults they fall back to.

for a middle

Explain when to override per-publication (multiple artifacts, BOMs, renamed modules) and that artifactId defaults to project.name.

for a senior

Discuss keeping GAV centralized on the project vs. per-publication, and lazy evaluation gotchas when reading project.version early.

for a principal

Frame GAV as the public API contract of a module and how org conventions (reverse-DNS groups, naming) are governed across many publishing builds.

## What "GAV" means Every artifact in a Maven-style repository is addressed by three coordinates, collectively called **GAV**: - **G**roup id — a namespace, usually reverse-DNS (`com.acme`). - **A**rtifact id — the module name (`payments-core`). - **V**ersion — the release identity (`1.4.0`, `2.0.0-SNAPSHOT`). A consumer declares `com.acme:payments-core:1.4.0`, so these three strings are the public contract of what you publish. ## Where coordinates come from in Gradle The `maven-publish` plugin contributes a `publishing { publications { ... } }` block. Inside a `MavenPublication` you can assign coordinates explicitly: - `groupId`, `artifactId`, `version` are mutable `String` properties on the publication. - If you leave them unset, Gradle fills in **defaults at publish time**: `groupId = project.group.toString()`, `version = project.version.toString()`, `artifactId = project.name`. So for the common case you set nothing on the publication and instead set `group` and `version` once on the project — every publication inherits them. ## When you override on the publication You override directly on the `MavenPublication` when a single Gradle project emits **more than one** set of coordinates, or when the published name must diverge from the project's folder name. Classic cases: - A BOM published as `acme-bom` from a project whose `project.name` is `bom`. - A renamed artifact for backward compatibility. - Multiple publications in one project, each with its own artifactId. Overriding the publication's `version` does **not** mutate `project.version`; the two are independent once you set the publication value explicitly. ## Example ```kotlin publishing { publications { create<MavenPublication>("library") { from(components["java"]) // override only what differs from project defaults: groupId = "com.acme" artifactId = "payments-core" version = "1.4.0" } } } ``` ## Pitfalls - Coordinates are resolved lazily; if you read `project.version` too early in configuration you may capture a stale value. - `artifactId` defaults to `project.name`, which is the **directory name** unless overridden in `settings.gradle(.kts)` — a frequent surprise.

  • If you set project.group and project.version but nothing on the publication, what coordinates get published?
    groupId = project.group, version = project.version, artifactId = project.name (the directory name unless overridden in settings).
  • Why might artifactId differ from what you expect even with no override?
    Because it defaults to project.name, which is the project directory name, not necessarily the human-friendly module name; it must be set in settings.gradle if you want a different one.

saying these in an interview costs you the question

  • Claiming you must always set coordinates explicitly on every publication — defaults from project.group/name/version usually suffice.
  • Thinking overriding the publication's version also changes project.version.

context

open as a page

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

level: juniorimportance: must knowfreq 70%

basics

~10 s

publishToMavenLocal installs every publication into your local Maven repository on disk (by default ~/.m2/repository), so other local builds can resolve it via mavenLocal().

open as a page

How do you apply the ivy-publish plugin and declare a basic Ivy publication in a Gradle build?

level: juniorimportance: must knowfreq 35%

basics

~10 s

Apply the ivy-publish plugin, then inside publishing { publications { } } create a publication of type IvyPublication and add a component such as from components.java.

open as a page

How do you apply the maven-publish plugin and declare a basic Maven publication that publishes your project's Java library?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Apply the maven-publish plugin, then in a publishing { publications { } } block create a publication of type MavenPublication and call from(components["java"]) so it picks up your library's jar and dependencies.

open as a page

What is the pom { } block on a MavenPublication, and why might you need to populate it before publishing a library?

level: juniorimportance: must knowfreq 60%

basics

~10 s

The pom { } block lets you set metadata (name, description, url, licenses, developers, scm) written into the generated pom.xml. You populate it because repositories like Maven Central reject artifacts missing this metadata.

open as a page

How does Gradle decide whether to publish to a snapshot or a release repository, and what role does the -SNAPSHOT suffix play?

level: middleimportance: must knowfreq 60%

basics

~20 s

A version ending in -SNAPSHOT is a snapshot; anything else is a release. Gradle itself doesn't pick the repo — you point publishing at a snapshot or release URL, typically choosing based on whether version ends with -SNAPSHOT.

open as a page

Explain the naming pattern of the tasks maven-publish generates for a publication and a repository.

level: middleimportance: must knowfreq 65%

basics

~10 s

For each publication and each repository the plugin generates publish<Pub>PublicationTo<Repo>Repository. It also generates publish<Pub>PublicationToMavenLocal and generatePomFileFor<Pub>Publication, plus aggregate publish and publishToMavenLocal tasks.

open as a page

How do you set the organisation, module, and revision on an IvyPublication, and how do they map to other coordinate systems?

level: middleimportance: must knowfreq 28%

basics

~10 s

Inside the create<IvyPublication> block set organisation, module, and revision. They map to Maven's groupId, artifactId, and version respectively. If omitted, Gradle defaults them from project.group, project.name, and project.version.

open as a page

A teammate applied maven-publish but their published module has no dependencies and consumers can't resolve transitive libraries. What likely went wrong and how do you fix it?

level: middleimportance: must knowfreq 45%

basics

~10 s

They almost certainly attached the jar with artifact(jar) instead of calling from(components["java"]). Switch to from(components["java"]) so the publication carries dependency metadata and the POM lists transitive dependencies.

open as a page

What metadata must a POM contain to pass Maven Central validation, and how do you express each field in a Gradle MavenPublication?

level: middleimportance: must knowfreq 55%

basics

~10 s

Central needs name, description, url, at least one license (name + url), at least one developer, and scm connection/developerConnection/url. You set each via the pom { } block on the MavenPublication.

open as a page

You produce two artifacts (a core library and a BOM) from one Gradle project. How do you give each its own artifactId while sharing one group and version?

level: middleimportance: should knowfreq 35%

basics

~10 s

Create two MavenPublications in the same project. Let both inherit groupId and version from project.group/version, but set a distinct artifactId on each so they publish as separate coordinates.

open as a page

How would you inspect the POM Gradle will publish without actually pushing anything to a repository?

level: middleimportance: should knowfreq 50%

basics

~10 s

Run generatePomFileFor<Pub>Publication. It writes the pom.xml into build/publications/<pub>/pom-default.xml so you can read it without publishing to any repository.

open as a page

How do you customize the Ivy descriptor metadata — author, description, and license — on an IvyPublication?

level: middleimportance: should knowfreq 22%

basics

~20 s

Use the descriptor { } block inside the publication and call author { name = ... }, description { text = ... }, and license { name = ... } to add metadata that is written into the generated ivy.xml.

open as a page

Show how to declare the same MavenPublication in both Kotlin DSL and Groovy DSL, and explain the syntactic differences.

level: middleimportance: should knowfreq 50%

basics

~10 s

In Kotlin you call create<MavenPublication>("maven") { from(components["java"]) }; in Groovy you write maven(MavenPublication) { from components.java }. Both apply maven-publish and configure the same publishing block.

open as a page

How do you generate and inspect the POM Gradle will publish, before actually publishing, and why is that useful?

level: middleimportance: should knowfreq 30%

basics

~10 s

Run the generatePomFileFor<PubName>Publication task; it writes the POM to build/publications/<name>/pom-default.xml. Inspecting it lets you confirm metadata and dependency mapping before a real publish.

open as a page

A teammate published a library that declares a dependency as 'com.acme:lib:[1.0,2.0)'. A Maven consumer complains the POM is non-deterministic. What went wrong and how do you fix it at the coordinate/versioning layer?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The declared dynamic version range was copied verbatim into the published POM, so Maven re-resolves it and may pick a different version each build. Add versionMapping{} so the POM records the concrete resolved version instead.

open as a page

What problem does versionMapping{} solve in a MavenPublication, and how does it change the generated POM?

level: seniorimportance: should knowfreq 40%

basics

~10 s

versionMapping{} writes the actual resolved dependency versions into the published POM instead of the declared (possibly dynamic or constraint-driven) versions, so consumers using plain Maven see concrete, reproducible versions.

open as a page

What is the difference between running the aggregate `publish` task and a specific publish<Pub>PublicationTo<Repo>Repository task?

level: seniorimportance: should knowfreq 45%

basics

~10 s

publish is a lifecycle task that fans out to every publication-to-remote-repository task, pushing to all configured remote repos. A specific publish<Pub>PublicationTo<Repo>Repository task targets exactly one publication and one repository.

open as a page

Why might you register an IvyPublication lazily and declare several publications, and how does that interact with the descriptor?

level: seniorimportance: should knowfreq 14%

basics

~10 s

You can register more than one IvyPublication (e.g. main + a variant) in the publications container; each gets its own coordinates and descriptor. Lazy registration avoids configuring publications that no task needs.

open as a page

A team applies ivy-publish but the published ivy.xml has no dependencies or the publish task does nothing useful. What are the likely causes?

level: seniorimportance: should knowfreq 12%

basics

~10 s

Most often the publication has no component attached — from(components["java"]) was omitted — so no artifacts or dependency metadata are included. Also check that a java/java-library plugin exists to provide the component.

open as a page

What is a SoftwareComponent in Gradle, and why does a MavenPublication require from(components[...]) rather than just attaching a jar?

level: seniorimportance: should knowfreq 40%

basics

~10 s

A SoftwareComponent is Gradle's variant-aware description of a project's consumable outputs — its artifacts plus dependency metadata. from(components["java"]) derives both the jar and the correctly-scoped POM dependencies, which a raw artifact(jar) cannot.

open as a page

How would you ensure every module in a large multi-project Gradle build emits a consistent, Central-compliant POM without copy-pasting the pom { } block?

level: seniorimportance: should knowfreq 25%

basics

~10 s

Extract the pom { } configuration into a shared convention plugin (buildSrc or build-logic) applied by every module, parameterizing per-module bits like name/description. Each subproject then gets identical license/developer/scm metadata for free.

open as a page

When would you reach for pom.withXml { } instead of the typed pom { } properties, and what are the trade-offs?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Use pom.withXml { } to edit raw XML the typed DSL can't express — e.g. injecting nodes, tweaking a dependency's scope/exclusions, or adding custom elements. The trade-off is brittle, low-level DOM manipulation that bypasses Gradle's model.

open as a page

A teammate added a new repository but their CI publish step now uploads to the wrong place. How do you discover the available publish tasks and pick the right one?

level: seniorimportance: nice to knowfreq 35%

basics

~10 s

List tasks with ./gradlew tasks --group publishing (or --all) to see every generated publish<Pub>PublicationTo<Repo>Repository task, then point CI at the specific task for the intended repo instead of the aggregate publish.

open as a page

When and how would you declare more than one MavenPublication in a single project, and what governs the choice of software component for each?

level: seniorimportance: nice to knowfreq 25%

basics

~10 s

Add several entries to publications {}, each create<MavenPublication>(name) { from(components[...]) } with a distinct name and (usually) a different component — for example a java library plus a platform/BOM component, or differently-coordinated variants.

open as a page