Gradle Publishing, Maven Publish, Ivy Publish, Signing, Metadata, and Repository Promotion: Concepts, Architecture, and Mental Model
Model publication as an external contract: choose a component, bind stable coordinates, generate interoperable metadata, publish immutable bytes to a repository boundary, and treat credentials and signatures as security-sensitive inputs.
Learning objectives
- Separate build outputs, software components, publications, repositories, coordinates, metadata, signatures, and promotion state.
- Explain why POM/Ivy metadata and Gradle Module Metadata coexist and where interoperability can become lossy.
- Distinguish a checksum from an OpenPGP signature and distinguish publishing from repository promotion.
- Inspect publication-related model and tasks before mutating a repository.
- Identify credential, signing-key, repository-write, and immutable-version trust boundaries.
1. Why “the JAR built” is not enough
Chapter 25 ended with a build whose performance could be measured without changing its output. A release pipeline now has a different problem: the output must cross the build boundary and become a contract that other builds can resolve later. A lone JAR file does not tell a Maven consumer its coordinates, dependencies, source/Javadoc companions, or release policy. It also does not prove who signed it or whether the same version was overwritten yesterday.
Publishing is therefore not “copy the JAR somewhere.” It is a controlled state transition from a project-local build model to a repository-visible module identity. The exact bytes matter, but so do metadata, repository layout, credentials, signatures, and promotion rules.
Trust boundary: A Gradle publishing task can send arbitrary project files and metadata to a repository while running with CI credentials and signing material. Review the build logic that selects publications and repository URLs with the same care as deployment code.
2. The publication model
| Object / state | Meaning | Typical evidence | Trust question |
|---|---|---|---|
| Software component |
Gradle model of consumable variants produced by plugins such
as java-library.
|
./gradlew outgoingVariants;
components["java"].
|
Are the variants and dependencies intended to become a public contract? |
| Publication | A named external representation assembled from a component plus coordinates, artifacts, and metadata. |
MavenPublication or
IvyPublication; generated POM/ivy.xml/GMM.
|
Does the publication preserve the semantics consumers actually need? |
| Repository | Destination/layout and transport used to store immutable published state. | Disposable file tree, Maven/Ivy layout, repository URL, authentication type. | Who can write, overwrite, or promote? |
| Coordinates |
Stable external identity: Maven
groupId:artifactId:version; Ivy
organisation/module/revision.
|
Generated descriptor path and consumer declaration. | Can the same release identity ever point to different bytes? |
| POM / ivy.xml | Interoperability metadata understood by Maven/Ivy ecosystems. | Generated descriptor and downstream Maven/Gradle resolution. | Did Gradle-only semantics degrade or disappear during mapping? |
| Gradle Module Metadata | Variant-aware metadata published alongside POM/Ivy descriptors. |
.module file with variants, attributes,
capabilities and checksums.
|
Will non-Gradle consumers see an equivalent enough contract? |
| Detached signature | OpenPGP evidence over a published file; distinct from a checksum. | .asc files and independent verification. |
Is the signing key protected and is verification policy defined? |
| Promotion | Repository-management act that advances already-built bytes from one trust stage to another. | Same SHA-256 before/after; repository audit trail. | Did promotion reuse exact bytes, or silently rebuild/overwrite? |
The most important separation is
component → publication → repository. The Java
plugin creates the java software component. The
Maven/Ivy publishing plugins do not automatically mean “publish
everything”; you create named publications and explicitly say what
component/artifacts belong to them. Repository declarations then
create destination-specific publish tasks.
3. From project model to external consumers
flowchart TD A[Java sources + build.gradle.kts] --> B[Java software component] B --> C[MavenPublication / IvyPublication] C --> D[POM or ivy.xml] C --> E[Gradle Module Metadata] C --> F[JAR + sources + Javadoc] D --> G[Disposable repository] E --> G F --> G G --> H[Gradle consumer] G --> I[Maven or Ivy consumer] G --> J[Promotion gate] J --> K[Release repository]
The first arrow is build configuration: the Java plugin knows which outgoing variants and artifacts exist. The second arrow selects which component becomes a publication. The publication generates repository-format metadata plus Gradle Module Metadata. The repository stores those files under an external coordinate. Promotion is shown separately because a repository manager should normally move or expose already-built bytes; it should not trigger another compilation.
4. Maven Publish and Ivy Publish are related but not interchangeable
maven-publish works with
MavenPublication and Maven-compatible repositories.
ivy-publish works with IvyPublication and
Ivy repositories. Both can publish the same Java component, but they
serialize identity and dependency metadata according to different
repository models.
| Question | Maven publication | Ivy publication |
|---|---|---|
| Primary identity | groupId:artifactId:version |
organisation / module / revision |
| Native descriptor | POM XML | ivy.xml |
| Gradle-rich descriptor | .module alongside POM |
.module alongside ivy.xml |
| Typical interoperability | Maven, Gradle, many JVM tools | Gradle/Ivy-aware consumers and custom layouts |
| Course rule | Default compatibility target for the checkpoint | Teach and inspect; do not force Maven consumers to understand Ivy |
5. Why Gradle Module Metadata exists beside POM/Ivy metadata
A POM is excellent for Maven’s dependency model, but Gradle can express richer variant state: attributes, capabilities, dependency constraints, and variant-specific artifacts. Gradle Module Metadata (GMM) serializes that richer component model. Gradle publishes GMM alongside POM or Ivy metadata so Gradle consumers can recover the richer model while Maven/Ivy consumers still have their native descriptor.
This creates an interoperability obligation: if a Gradle-only concept cannot be represented faithfully in POM/Ivy metadata, Gradle can emit publication warnings. Suppressing a warning does not make the semantic mismatch disappear. The production response is to decide whether the Maven/Ivy view remains acceptable, redesign the public variant model, or consciously limit the consumer ecosystem.
6. Coordinates are an immutable public identity
For the lab, the Maven identity is
dev.academy.publish:ledger-api:1.0.0. Once consumers
can resolve that identity from an authoritative release repository,
the safest production rule is that the coordinate is immutable:
later code changes require a new version.
A file repository used in a lab may happily overwrite files. That behavior is exactly why it is not an authoritative production release policy. Repository-manager immutability, staging, retention, and promotion are separate operational controls that the Gradle build should respect rather than try to replace.
7. Checksums, signatures, and provenance are different claims
A checksum answers: “Are these bytes the same bytes I measured?” A detached OpenPGP signature additionally binds bytes to a signing key under a verification policy. A signature is not a vulnerability scan, SBOM, reproducible-build proof, or authorization to release. Those are separate evidence streams.
Gradle’s Signing Plugin signs artifacts/publications and can publish
detached .asc signatures. Its OpenPGP private key is
therefore a high-value secret. This chapter never embeds a real key
or passphrase in a build script. The optional lab key is generated
in a disposable directory, has no production trust, and is deleted
with the lab.
8. Repository credentials belong outside build logic
File repositories need no credentials. Real HTTPS repositories
commonly do. Gradle supports typed credentials and can derive
property names from a repository name. For a repository named
releases, a
PasswordCredentials declaration can resolve
releasesUsername and releasesPassword from
Gradle property sources.
That still does not make the values safe to print. Do not log credentials, put them in Git, bake them into a Docker image, or pass them into arbitrary untrusted build logic. In CI, supply them through the platform’s secret facility and constrain which branches/jobs are allowed to publish.
9. Read-only inspection before publication
# Run in a trusted Wrapper-enabled project.
java -version
./gradlew --version
./gradlew projects
./gradlew outgoingVariants
./gradlew tasks --group publishing
./gradlew properties | grep -E '^(group|version|name):' || true
outgoingVariants lets you inspect the component
contract before a publication object serializes it.
tasks --group publishing shows which generation/publish
tasks exist after the plugins are applied. Neither command should
upload anything. Keep this evidence in CI logs before write-capable
tasks are allowed to run.
10. Publish, stage, promote, and release are different verbs
Publish writes a publication to a repository. Stage writes to a location that is not yet the authoritative release channel. Promote advances already-built artifacts/metadata from staging to release according to repository policy. Release is the broader organizational decision that those immutable coordinates are supported and discoverable.
Gradle can publish directly to a Maven/Ivy repository, but a production repository manager usually owns promotion, immutability, retention, quarantine, and permission boundaries. The local checkpoint simulates promotion by copying an already-published coordinate tree only after comparing hashes; it explicitly refuses to overwrite an existing release identity.
11. DevOps operating rule
A production build-engineering team should be able to answer five questions from evidence: what component was published, under which exact coordinate, what metadata did each consumer ecosystem receive, what bytes were signed/promoted, and which principal was authorized to write them? If those answers depend on a developer’s Maven Local cache or a mutable URL, the release process is not yet auditable.
Knowledge check
Does applying maven-publish automatically publish
every file in build/?
No. A publication selects a component and/or explicit artifacts; repository definitions then create tasks for publishing that publication.
Why publish GMM next to a POM instead of replacing the POM?
GMM preserves Gradle variant semantics, while the POM preserves Maven ecosystem interoperability. Consumers need their native metadata format.
If a JAR SHA-256 matches, is an OpenPGP signature redundant?
No. A checksum proves byte equality relative to a known digest; a verified signature adds signer/key evidence under a trust policy.
What should promotion do to the artifact bytes?
Nothing. Promotion should advance the same already-built bytes; rebuilding during promotion breaks artifact-identity evidence.
Why is Maven Local unsuitable as the authoritative release repository?
It is user-local mutable cache state, can contain incomplete/overwritten modules, and does not provide shared release governance or trustworthy provenance.
Official references and version notes
- Gradle 9.7.1 release notes — pinned Gradle baseline for this chapter.
- Publishing a project as a module — publication = artifacts + metadata, repository target, publish task, checksums, and signatures when configured.
-
Maven Publish Plugin
—
MavenPublication, generated POM, repository tasks, Maven Local, version mapping, snapshot/release routing. -
Ivy Publish Plugin
—
IvyPublication, generatedivy.xml, Ivy repository layout and tasks. - Gradle Module Metadata — variant-aware metadata, POM/Ivy mapping, publication warnings, validation, reproducibility.
- Signing Plugin — OpenPGP signatures, in-memory keys, GPG command integration, signing publications.
- Supported repository protocols and credentials — file/HTTP(S) transports, externalized credentials, repository-name-derived properties.
- Metadata formats — Gradle Module Metadata, Maven POM, Ivy descriptors and consumer behavior.
- Apache Maven releases history — Maven 3.9.16 is the current GA baseline used for the clean Maven consumer lane.
- Maven Compiler Plugin — pinned 3.15.0 compiler plugin for the Maven consumer fixture.
Version-sensitive behavior was rechecked against current Gradle and
Apache Maven primary documentation on 2026-08-24. Mandatory
publication uses only repository-owned files plus disposable
file: repositories under the lab workspace. Maven
Local, real credentials, real release repositories, hosted signing,
Maven Central, Nexus/Artifactory administration, and CI-specific
secret stores are not mandatory.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.