Install, Deploy & Distribution Management
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Maven 3.10.0 with the Apache Maven Install Plugin 3.2.0, the Apache Maven Deploy Plugin 3.2.0,
the Source Plugin 3.4.0, the Javadoc Plugin 3.12.0 and the Site Plugin 3.22.0. Every example marked as run was executed on JDK 26 on
2026-10-16 in scratch copies of the tidepool project (the parent io.tidepool:tidepool-parent with tidepool-model,
tidepool-engine and tidepool-cli). Maven 3.9.16 and Maven 4.0.0-rc-7 (a release candidate) were run wherever the page says so.
In the output, <lab> is the scratch directory, <proj> is the directory of the project being built, the local repository was
<lab>/m2 (set with -Dmaven.repo.local, so the real ~/.m2/repository was not used), lines were filtered with grep (said each
time), transfer sizes and speeds were cut, and the timestamp of a snapshot upload is written TS because it changes on every run.
Nothing was published to a real server. The repositories were directories reached through file: URLs, and, for credentials, a
small Python HTTP server on localhost that is described below. Publishing to Maven Central is on
Publishing to Maven Central and is "not run here".
Building an artifact is only half of a build’s job; the other half is making it available to somebody else. Maven splits that
into two steps that are easy to confuse. Install copies the result into the local repository of the machine, so that other
projects built on the same machine can use it. Deploy uploads it to a remote repository, so that the rest of the world
can. The address of that remote repository is not a property of the plugin but of the project, and lives in the POM element
distributionManagement. This page covers the two plugins, that element, the credentials a remote repository asks for, what exactly
gets written, and what changed in Maven 3.10 and Maven 4. The repositories themselves (layout, mirrors, managers) are on
Repositories, Mirrors & Repository Managers;
settings.xml and encrypted passwords on
settings.xml & Password Encryption; the release
process that ends in a deploy on
Releasing. The scenario is on the
section landing page.
Where install and deploy sit
install and deploy are the last two phases of the default lifecycle (the others are on
Build Lifecycle). Each is a phase with one default binding,
and both are bound to the goal of the same name for every packaging that produces an artifact:
| Phase | Default goal | What it does |
|---|---|---|
|
|
Builds the artifact in |
|
none for most packagings |
Where checks on the package (integration tests, quality gates) belong. |
|
|
Copies the project’s artifacts into the local repository. |
|
|
Uploads the project’s artifacts to the remote repository named in |
A phase runs every phase before it, so mvn deploy also installs; there is no way to deploy without installing except by calling the
goal directly (mvn deploy:deploy, which needs an already built package) or skipping the install (below).
The diagram shows the two uploads that deploy:deploy chooses between by the version of the project, and the site, which is not
part of the default lifecycle and has its own goal.
The lab: tidepool with a distributionManagement
The parent POM of the lab pins the plugins in pluginManagement (which is why the versions above are known, see
Maven Plugins & Their Configuration) and declares
two repositories. Their URLs are built from a property, so that a command line can point the same POM somewhere else, and the base
is a directory inside the project:
<properties>
<repo.url>file://${maven.multiModuleProjectDirectory}/build/repo</repo.url>
</properties>
<distributionManagement>
<repository>
<id>tidepool-releases</id>
<url>${repo.url}/releases</url>
</repository>
<snapshotRepository>
<id>tidepool-snapshots</id>
<url>${repo.url}/snapshots</url>
</snapshotRepository>
</distributionManagement>
${maven.multiModuleProjectDirectory} is the directory that contains the .mvn directory (or the directory Maven was started
in, when there is none), and it exists on 3.9.16, 3.10.0 and 4.0.0-rc-7. Maven 3.10 and 4 also have ${project.rootDirectory},
but 3.9.16 leaves that expression unresolved in a URL, so the older name is the portable one (see
Inheritance, Aggregation & the Effective POM).
The id values, not the URLs, are what links the repository to the credentials in settings.xml.
install: the local repository
What it writes
The Install Plugin has three goals: install:install (bound to the install phase), install:install-file (for a file that no
project built, see below) and install:help. Running the phase for the whole tidepool reactor
(mvn -B install, lines filtered with grep -E "install:|Installing|BUILD") printed:
[INFO] --- install:3.2.0:install (default-install) @ tidepool-parent ---
[INFO] Installing pom.xml to <lab>/m2/io/tidepool/tidepool-parent/1.0.0-SNAPSHOT/tidepool-parent-1.0.0-SNAPSHOT.pom
[INFO] --- install:3.2.0:install (default-install) @ tidepool-model ---
[INFO] Installing tidepool-model/pom.xml to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.pom
[INFO] Installing tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] --- install:3.2.0:install (default-install) @ tidepool-engine ---
[INFO] Installing tidepool-engine/pom.xml to <lab>/m2/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-SNAPSHOT.pom
[INFO] Installing tidepool-engine/target/tidepool-engine-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-SNAPSHOT.jar
[INFO] --- install:3.2.0:install (default-install) @ tidepool-cli ---
[INFO] Installing tidepool-cli/pom.xml to <lab>/m2/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-SNAPSHOT.pom
[INFO] Installing tidepool-cli/target/tidepool-cli-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS
The parent installs only its POM, because a pom packaging has no JAR (the packaging is in
POM Fundamentals). The modules install their POM and their JAR; the
POM matters as much as the JAR, because it is what a consumer reads to find the transitive dependencies. In the local repository a
module looks like this (find tidepool-model -type f | sort):
tidepool-model/1.0.0-SNAPSHOT/_remote.repositories
tidepool-model/1.0.0-SNAPSHOT/maven-metadata-local.xml
tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.pom
tidepool-model/maven-metadata-local.xml
There are no checksum files: the Install Plugin 3.2.0 writes none (a deploy does, below). _remote.repositories is a record of where each
file came from, kept by Maven Resolver:
#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.
tidepool-model-1.0.0-SNAPSHOT.jar>=
tidepool-model-1.0.0-SNAPSHOT.pom>=
The >= with nothing after it means the file was put there by an install, from no repository; a file downloaded from the repository
with the id central reads >central=, and Resolver then uses it only for builds that list that repository (the effect is on
Repositories, Mirrors & Repository Managers).
maven-metadata-local.xml is the local version of the metadata that a remote repository keeps, here for a snapshot:
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>io.tidepool</groupId>
<artifactId>tidepool-model</artifactId>
<versioning>
<lastUpdated>TIMESTAMP</lastUpdated>
<snapshot>
<localCopy>true</localCopy>
</snapshot>
<snapshotVersions>
<snapshotVersion>
<extension>pom</extension>
<value>1.0.0-SNAPSHOT</value>
<updated>TIMESTAMP</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar</extension>
<value>1.0.0-SNAPSHOT</value>
<updated>TIMESTAMP</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
<version>1.0.0-SNAPSHOT</version>
</metadata>
(TIMESTAMP replaces the install time, which changes on every run.) localCopy tells the resolver that this snapshot is a copy that was installed locally, not downloaded from a timestamped build in a remote repository.
The parameters
The parameters below come from help:describe -Dplugin=org.apache.maven.plugins:maven-install-plugin:3.2.0 -Ddetail.
| Goal and parameter | User property | Meaning |
|---|---|---|
|
|
Do not install. For artifacts that must not reach the local repository. |
|
|
Install every project of the reactor at the end of the build instead of during each project’s own |
|
|
A project with a packaging other than |
|
|
The file to install (required). |
|
same names |
Coordinates; taken from |
|
same names |
A classifier for the file, or the bundled sources and Javadoc to install beside it. |
|
|
An existing POM, or generate a minimal one when no POM is given (the default when the local repository has none yet). |
|
|
Install into a specific directory instead of the configured local repository. |
installAtEnd and skipping
With -DinstallAtEnd=true each module only registers itself, and the installs happen after the last project (the deferred messages come
first, then the installs; the filter was grep -E "install:|Deferring|Installing .*\.jar |BUILD", so the POM installs are not shown):
[INFO] --- install:3.2.0:install (default-install) @ tidepool-parent ---
[INFO] Deferring install for io.tidepool:tidepool-parent:1.0.0-SNAPSHOT at end
[INFO] --- install:3.2.0:install (default-install) @ tidepool-model ---
[INFO] Deferring install for io.tidepool:tidepool-model:1.0.0-SNAPSHOT at end
[INFO] --- install:3.2.0:install (default-install) @ tidepool-engine ---
[INFO] Deferring install for io.tidepool:tidepool-engine:1.0.0-SNAPSHOT at end
[INFO] --- install:3.2.0:install (default-install) @ tidepool-cli ---
[INFO] Deferring install for io.tidepool:tidepool-cli:1.0.0-SNAPSHOT at end
[INFO] Installing tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] Installing tidepool-engine/target/tidepool-engine-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-SNAPSHOT.jar
[INFO] Installing tidepool-cli/target/tidepool-cli-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS
-Dmaven.install.skip=true turns the goal into a no-op for every project:
[INFO] --- install:3.2.0:install (default-install) @ tidepool-parent ---
[INFO] Skipping artifact installation
[INFO] --- install:3.2.0:install (default-install) @ tidepool-model ---
[INFO] Skipping artifact installation
[INFO] --- install:3.2.0:install (default-install) @ tidepool-engine ---
[INFO] Skipping artifact installation
[INFO] --- install:3.2.0:install (default-install) @ tidepool-cli ---
[INFO] Skipping artifact installation
[INFO] BUILD SUCCESS
install-file is for JARs that no Maven project built, such as a vendor library; its use, and the relocation that is the
POM’s way of renaming an artifact, are on
Installing Third-Party JARs & Relocation.
distributionManagement
distributionManagement says where the outputs of this project go. It is not to be confused with <repositories>, which says
where the project’s inputs come from. The element has these children:
| Element | Meaning |
|---|---|
|
Where release versions are uploaded. Children: |
|
Where |
|
Where |
|
Tells consumers that the artifact moved to other coordinates. |
|
A page where users can download the artifact, for people; no tool reads it. |
|
The status of the artifact in the remote repository ( |
The layout (default or legacy) and uniqueVersion children date from Maven 2. Maven 3 supports only the default layout, and
uniqueVersion is ignored: a run with <uniqueVersion>false</uniqueVersion> in the repository, on a project without a
snapshotRepository, still wrote a timestamped file with a build number (see the next section).
The id is the link to the credentials
The <id> of a repository, snapshotRepository or site is the name that Maven looks up in the <servers> of settings.xml for the
user name and password (below). The id is arbitrary, but it identifies the repository among all the others of the build, so it must be
unique. A typo in the id does not fail the build: the upload is simply attempted without credentials, and a protected repository
answers 401.
snapshotRepository is optional
Without a snapshotRepository, a snapshot version is sent to repository. Removing it from the lab and deploying tidepool-model
(filtered with grep -E "Uploaded to.*(jar|pom) |BUILD") printed:
[INFO] Uploaded to tidepool-releases: file://<proj>/build/repo/releases/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-releases: file://<proj>/build/repo/releases/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] BUILD SUCCESS
The files of a snapshot landed under releases, which is a trap: a repository manager that keeps release and snapshot
repositories apart may reject the upload. Keep both elements.
No distributionManagement at all
A project without the element cannot be deployed by the lifecycle. Deploying the same module from a POM without it failed in the Deploy Plugin, after the build had succeeded:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy (default-deploy) on project tidepool-model: Deployment failed: repository element was not specified in the POM inside distributionManagement element or in -DaltDeploymentRepository=id::url parameter -> [Help 1]
The message names the two ways out, the POM element and the altDeploymentRepository parameter described below.
deploy: what is uploaded
A snapshot
mvn -B -pl tidepool-model deploy (filtered with grep -E "deploy:|Uploaded|BUILD", the preceding phases are the usual ones) printed:
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/maven-metadata.xml
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/maven-metadata.xml
[INFO] BUILD SUCCESS
The version 1.0.0-SNAPSHOT was not uploaded under that name. The file names carry a timestamp and a build number (TS-1:
the date, the time in UTC and the sequence number), and the directory 1.0.0-SNAPSHOT still carries the literal version. Everything the
resolver needs to find the newest one is in maven-metadata.xml files. After the build, with the checksum files included, the repository contained (find build/repo -type f | sort):
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/maven-metadata.xml
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/maven-metadata.xml.md5
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/maven-metadata.xml.sha1
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar.md5
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar.sha1
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom.md5
./snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom.sha1
./snapshots/io/tidepool/tidepool-model/maven-metadata.xml
./snapshots/io/tidepool/tidepool-model/maven-metadata.xml.md5
./snapshots/io/tidepool/tidepool-model/maven-metadata.xml.sha1
Deploying again, a few seconds later, did not replace anything: it added the files for build number 2 and rewrote the metadata of the version directory to point at it. The directory listing, and the metadata of the version directory after the second deploy:
maven-metadata.xml
maven-metadata.xml.md5
maven-metadata.xml.sha1
tidepool-model-1.0.0-TS-1.jar
tidepool-model-1.0.0-TS-1.jar.md5
tidepool-model-1.0.0-TS-1.jar.sha1
tidepool-model-1.0.0-TS-1.pom
tidepool-model-1.0.0-TS-1.pom.md5
tidepool-model-1.0.0-TS-1.pom.sha1
tidepool-model-1.0.0-TS-2.jar
tidepool-model-1.0.0-TS-2.jar.md5
tidepool-model-1.0.0-TS-2.jar.sha1
tidepool-model-1.0.0-TS-2.pom
tidepool-model-1.0.0-TS-2.pom.md5
tidepool-model-1.0.0-TS-2.pom.sha1
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>io.tidepool</groupId>
<artifactId>tidepool-model</artifactId>
<versioning>
<lastUpdated>DATETIME</lastUpdated>
<snapshot>
<timestamp>DATE.TIME</timestamp>
<buildNumber>2</buildNumber>
</snapshot>
<snapshotVersions>
<snapshotVersion>
<extension>pom</extension>
<value>1.0.0-DATE.TIME-2</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar</extension>
<value>1.0.0-DATE.TIME-2</value>
<updated>DATETIME</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
<version>1.0.0-SNAPSHOT</version>
</metadata>
timestamp and buildNumber name the newest snapshot, and each snapshotVersion entry maps an extension (and a classifier) to the
value to download. The other file, in the artifact’s own directory, lists the versions the repository knows about:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>io.tidepool</groupId>
<artifactId>tidepool-model</artifactId>
<versioning>
<versions>
<version>1.0.0-SNAPSHOT</version>
</versions>
<lastUpdated>DATETIME</lastUpdated>
</versioning>
</metadata>
A consumer that depends on 1.0.0-SNAPSHOT reads these files, and downloads the newest timestamped build; the update policy (daily
by default) decides how often it looks (see
Versions, Snapshots & Ranges). To check that the
repository is usable, a new project that declares the directory as a repository and depends on tidepool-cli:1.0.0-SNAPSHOT, with an
empty local repository, resolved the whole graph (dependency:list, filtered):
[INFO] The following files have been resolved:
[INFO] io.tidepool:tidepool-cli:jar:1.0.0-SNAPSHOT:compile -- module tidepool.cli (auto)
[INFO] io.tidepool:tidepool-engine:jar:1.0.0-SNAPSHOT:compile -- module tidepool.engine (auto)
[INFO] io.tidepool:tidepool-model:jar:1.0.0-SNAPSHOT:compile -- module tidepool.model (auto)
[INFO] org.apache.commons:commons-csv:jar:1.14.1:compile -- module org.apache.commons.csv
[INFO] commons-io:commons-io:jar:2.20.0:compile -- module org.apache.commons.io
[INFO] commons-codec:commons-codec:jar:1.19.0:compile -- module org.apache.commons.codec
[INFO] info.picocli:picocli:jar:4.7.7:compile -- module info.picocli
[INFO] BUILD SUCCESS
The same POM also gives a release: a version without -SNAPSHOT is uploaded under its own name, in the repository URL, and a
repository manager normally refuses a second upload of the same release file (see "Credentials" below).
Checksums
For every file Maven also uploads a checksum, by default .sha1 and .md5. They are the hash of the bytes sent, so a consumer
(or a repository manager) can detect corruption:
$ cat tidepool-model-1.0.0-TS-1.jar.sha1
4323ad73b2363318d68170288e32a1c41bd8d42b
$ shasum -a 1 tidepool-model-1.0.0-TS-1.jar
4323ad73b2363318d68170288e32a1c41bd8d42b tidepool-model-1.0.0-TS-1.jar
The algorithms are a Maven Resolver setting, aether.checksums.algorithms, a comma-separated list that defaults to SHA-1,MD5. A
deploy with -Daether.checksums.algorithms=SHA-1,SHA-256,SHA-512,MD5 on 3.10.0 listed (ls | grep jar):
tidepool-model-1.0.0-TS-1.jar
tidepool-model-1.0.0-TS-1.jar.md5
tidepool-model-1.0.0-TS-1.jar.sha1
tidepool-model-1.0.0-TS-1.jar.sha256
tidepool-model-1.0.0-TS-1.jar.sha512
The same property with SHA-1,SHA-256 also worked on Maven 3.9.16 and 4.0.0-rc-7. Central requires .md5 and .sha1 files and accepts the
stronger ones in addition (see
Publishing to Maven Central); the verification a consumer
can do with them is on
Supply Chain Security.
The whole reactor, in order
A deploy of the whole reactor uploaded each module in the order of the reactor, as its own deploy phase, parent first (filtered
with grep -E "deploy:|Uploaded to.*(\.jar|-1\.pom) |BUILD", so the POM and JAR uploads show, and the metadata uploads do not):
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-parent/1.0.0-SNAPSHOT/tidepool-parent-1.0.0-TS-1.pom
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-TS-1.jar
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-TS-1.jar
[INFO] BUILD SUCCESS
That order has a consequence: if the build fails in the third module, the first two are already deployed. deployAtEnd changes it.
deployAtEnd
-DdeployAtEnd=true (the user property has the same name as the parameter) makes each project only register what it would deploy;
the uploads happen after the last project has built successfully:
[INFO] Deferring deploy for io.tidepool:tidepool-parent:1.0.0-SNAPSHOT at end
[INFO] Deferring deploy for io.tidepool:tidepool-model:1.0.0-SNAPSHOT at end
[INFO] Deferring deploy for io.tidepool:tidepool-engine:1.0.0-SNAPSHOT at end
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-engine/1.0.0-SNAPSHOT/tidepool-engine-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-cli/1.0.0-SNAPSHOT/tidepool-cli-1.0.0-TS-1.jar
[INFO] BUILD SUCCESS
(The last project, tidepool-cli, triggers the uploads, so its own Deferring line is not printed. The POM uploads and the parent are left out by the filter. The uploads are not in reactor order: this run uploaded the engine, the model
and then the command line module.) The point of deployAtEnd is to avoid publishing three of four modules when the fourth one fails its tests. It
is not atomic: if the upload of the third module fails, the first two are already in the repository, and Maven cannot take them back. The
plugin’s description of the parameter says so explicitly. For something that must be all-or-nothing, deploy to a staging area and
promote it (what Central does, see
Publishing to Maven Central). The Install Plugin has the
equivalent installAtEnd, above.
Skipping, and the typo that fails
maven.deploy.skip (parameter skip) skips the upload. A module that is a test harness or an example, never meant to be published,
sets it in its own POM:
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
and -Dmaven.deploy.skip=true on the command line does it for the whole build:
[INFO] --- deploy:3.2.0:deploy (default-deploy) @ tidepool-parent ---
[INFO] Skipping artifact deployment
[INFO] --- deploy:3.2.0:deploy (default-deploy) @ tidepool-model ---
[INFO] Skipping artifact deployment
[INFO] --- deploy:3.2.0:deploy (default-deploy) @ tidepool-engine ---
[INFO] Skipping artifact deployment
[INFO] --- deploy:3.2.0:deploy (default-deploy) @ tidepool-cli ---
[INFO] Skipping artifact deployment
[INFO] BUILD SUCCESS
Since Deploy Plugin 3.0.0-M2 the value is not only a boolean: releases skips when the version is a release and snapshots skips
when it is a snapshot (-Dmaven.deploy.skip=snapshots on the lab, whose version is a snapshot, printed Skipping artifact deployment
again); any other value fails the build, which protects against a typo in a control that exists to prevent a publication:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy (default-deploy) on project tidepool-model: Unrecognized value 'snapshto' for maven.deploy.skip. Accepted values: true, false, releases, snapshots. -> [Help 1]
The parameters of the Deploy Plugin
These come from help:describe -Dplugin=org.apache.maven.plugins:maven-deploy-plugin:3.2.0 -Ddetail.
| Goal and parameter | User property | Meaning |
|---|---|---|
|
|
|
|
the same |
Like the above, but only for release or only for snapshot versions. |
|
|
Deploy at the end of the reactor. |
|
|
How many times a failed upload is retried; values outside 1 to 10 are moved into the range. Default 1. Not run here. |
|
|
|
|
|
Allow a non- |
|
|
The file, the repository URL and the server id (default |
|
same names |
Coordinates; read from |
|
|
An existing POM to upload beside the file; or generate a default one ( |
|
|
Bundled sources and Javadoc JARs to upload with the main file. |
|
the same |
Comma-separated lists of extra files, with a classifier and type for each; the three lists must have the same length. |
|
|
Like |
altDeploymentRepository
-DaltDeploymentRepository=id::url overrides the repository of the POM for one run, which is how a CI job publishes a branch build to a
scratch location without touching the POM. Deploying tidepool-model from the POM without distributionManagement, with
-DaltDeploymentRepository=lab::file://<proj>/build/alt, worked (filter: grep -E "deploy:|Uploaded to|BUILD"):
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to lab: file://<proj>/build/alt/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to lab: file://<proj>/build/alt/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to lab: file://<proj>/build/alt/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/maven-metadata.xml
[INFO] Uploaded to lab: file://<proj>/build/alt/io/tidepool/tidepool-model/maven-metadata.xml
[INFO] BUILD SUCCESS
The old form id::layout::url is still accepted by 3.2.0 but warns (this is -DaltDeploymentRepository=lab::default::file://<proj>/build/alt2):
[WARNING] Using legacy syntax for alternative repository. Use "lab::file://<proj>/build/alt2" instead.
[INFO] BUILD SUCCESS
A book or a blog written for the Deploy Plugin 2.x will use id::default::url; drop the middle part. The repository chosen this way
is used for releases and snapshots alike, unless you use the altReleaseDeploymentRepository and altSnapshotDeploymentRepository
variants. The id is also the server id, so credentials work the same way.
Credentials
A real repository does not accept anonymous uploads. Maven finds the user name and password in settings.xml, in the <server> whose
<id> equals the id of the repository (a settings.xml is per user and never goes into the project; the elements are on
settings.xml & Password Encryption).
The runs below use a Python file server on localhost:18090 that accepts a PUT only with HTTP basic authentication for the user
deployer with the placeholder password changeit-example, logs what it receives, and, to imitate a rule of repository managers, answers
400 to a second upload of a release file. That rule is the lab’s imitation, not the behaviour of any particular product. The lab uses
the project as version 1.0.0, and -Drepo.url=http://localhost:18090 sends both repositories there.
First with no <server> (settings-none.xml, an empty <settings/>), the build fails at the first PUT; the log of the server
shows both files denied:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy (default-deploy) on project tidepool-model: Failed to deploy artifacts: Could not transfer artifact io.tidepool:tidepool-model:pom:1.0.0 from/to tidepool-releases (http://localhost:18090/releases): HTTP Status: 401 -> [Help 1]
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom user=- DENIED
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar user=- DENIED
HTTP Status: 401 is what a missing entry, a wrong id, a wrong password and an undecryptable password all look like; the log of the
server (or -X) tells them apart. With a <server> for each id:
<settings xmlns="http://maven.apache.org/SETTINGS/1.3.0">
<servers>
<server>
<id>tidepool-releases</id>
<username>deployer</username>
<password>changeit-example</password>
</server>
<server>
<id>tidepool-snapshots</id>
<username>deployer</username>
<password>changeit-example</password>
</server>
</servers>
</settings>
mvn -B -s settings-deploy.xml -pl tidepool-model deploy -Drepo.url=http://localhost:18090
The output (Maven 3.10.0, grep -E "WARN|Uploaded|BUILD"; the two long warnings are cut where marked):
[WARNING] Using credentials of server 'tidepool-releases' for repository http://localhost:18090/releases, although no repository or mirror with this id is declared in settings or on the command line and the server declares no <repositoryOrigins>. [...]
[WARNING] Using credentials of server 'tidepool-snapshots' for repository http://localhost:18090/snapshots, although no repository or mirror with this id is declared in settings or on the command line and the server declares no <repositoryOrigins>. [...]
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/maven-metadata.xml
[INFO] BUILD SUCCESS
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom.sha1 user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom.md5 user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar.sha1 user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar.md5 user=deployer ok
PUT /releases/io/tidepool/tidepool-model/maven-metadata.xml user=deployer ok
PUT /releases/io/tidepool/tidepool-model/maven-metadata.xml.sha1 user=deployer ok
PUT /releases/io/tidepool/tidepool-model/maven-metadata.xml.md5 user=deployer ok
The upload worked, and the log shows the PUT of each file followed by those of its checksums. The warnings are new in Maven 3.10.
Maven 3.10: credentials are scoped to an origin
According to the release notes of 3.10.0, the credentials of a server are sent only to the origin (scheme, host and port) of the
repositories and mirrors that have that id, and the match on the id is exact. Maven finds those repositories among the ones declared in
settings.xml and on the command line; a distributionManagement repository of the POM is not among them in these runs, because the
warning appeared for the tidepool-releases id that the POM itself declares. To bind the credentials explicitly, name the origin in the
server, which the same release notes document as repositoryOrigins (the full description is on the settings page):
<server>
<id>tidepool-releases</id>
<username>deployer</username>
<password>changeit-example</password>
<repositoryOrigins>
<repositoryOrigin>http://localhost:18090</repositoryOrigin>
</repositoryOrigins>
</server>
The property maven.repository.credentialScope=strict makes Maven refuse credentials that are not bound this way. A deploy with
settings-origins.xml and -Dmaven.repository.credentialScope=strict on a fresh server, per Maven version
(grep -E "WARN|Uploaded|BUILD|ERROR\] Failed"; the Failed to execute line is cut after the goal):
--- Maven 3.10.0
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/maven-metadata.xml
[INFO] BUILD SUCCESS
--- Maven 3.9.16
[WARNING] Some problems were encountered while building the effective settings
[WARNING] Unrecognised tag: 'repositoryOrigins'
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar
[INFO] Uploaded to tidepool-releases: http://localhost:18090/releases/io/tidepool/tidepool-model/maven-metadata.xml
[INFO] BUILD SUCCESS
--- Maven 4.0.0-rc-7
[WARNING] Not using credentials of server 'tidepool-releases' for repository http://localhost:18090/releases: no repository or mirror with this id is declared in settings or on the command line, and maven.repository.credentialScope=strict is in effect.
[WARNING] Not using credentials of server 'tidepool-snapshots' for repository http://localhost:18090/snapshots: no repository or mirror with this id is declared in settings or on the command line, and maven.repository.credentialScope=strict is in effect.
[ERROR] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy [...]
So the explicit origin works on 3.10.0. Maven 3.9.16 does not know the element and says so (Unrecognised tag), but it sends the
credentials anyway because it has no scoping. Maven 4.0.0-rc-7 printed the warning with the short text and refused, in this run, to use the
credentials even though the server named the origin, which is the behaviour the settings page found too. Without strict, each version
sent the credentials, with these warnings (the long lines cut where marked):
--- Maven 3.10.0
[WARNING] Using credentials of server 'tidepool-releases' for repository http://localhost:18090/releases, although no repository or mirror with this id is declared in settings or on the command line and the server declares no <repositoryOrigins>. [...]
[WARNING] Using credentials of server 'tidepool-snapshots' for repository http://localhost:18090/snapshots, although no repository or mirror with this id is declared in settings or on the command line and the server declares no <repositoryOrigins>. [...]
[INFO] BUILD SUCCESS
--- Maven 3.9.16
[INFO] BUILD SUCCESS
--- Maven 4.0.0-rc-7
[WARNING] Using credentials of server 'tidepool-releases' for repository http://localhost:18090/releases, although no repository or mirror with this id is declared in settings or on the command line. Set maven.repository.credentialScope=strict to refuse such credential use.
[WARNING] Using credentials of server 'tidepool-snapshots' for repository http://localhost:18090/snapshots, although no repository or mirror with this id is declared in settings or on the command line. Set maven.repository.credentialScope=strict to refuse such credential use.
[INFO] BUILD SUCCESS
The practical advice: for a repository that the build declares in the POM, give the server repositoryOrigins on Maven 3.10, expect
a warning without it, and do not turn on strict on Maven 4 release candidates until your repository is declared where Maven looks.
Release versions are immutable
Deploying the same release a second time, with the same credentials, was refused by the lab server:
[ERROR] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy (default-deploy) on project tidepool-model: Failed to deploy artifacts: Could not transfer artifact io.tidepool:tidepool-model:pom:1.0.0 from/to tidepool-releases (http://localhost:18090/releases): HTTP Status: 400 -> [Help 1]
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.pom user=deployer ok
PUT /releases/io/tidepool/tidepool-model/1.0.0/tidepool-model-1.0.0.jar user=deployer ok
HTTP Status: 400 here is the lab’s imitation of a repository manager that does not allow redeploying a release. A plain file repository
(file:) has no such rule and overwrites silently, so a local test does not catch a pipeline that redeploys a release. The rule is
right: a consumer that cached 1.0.0 must never see different bytes under that name. The way out is a new version, not a deploy of the
same one (see
Releasing).
Other transports and how the password is protected
file: and http:/https: work with the native HTTP transport of Maven 3.9 and later. scp:, sftp: and ftp: need a Wagon
provider added as a build extension, which the repositories page describes as "Transports: native and Wagon"; they were not run here, and
their privateKey and passphrase settings are in the server table of the settings page. The password in the file should be
encrypted with the master-password mechanism of Maven 3 or mvnenc of Maven 4 (the settings page runs both), or come from an
environment variable in the CI job.
Attached artifacts: sources and Javadoc
A repository consumer, and Central, wants more than the JAR: a -sources.jar for the IDE and a -javadoc.jar for the documentation.
The JAR Plugin attaches nothing but the main artifact; other plugins attach extra files to the project, and install and deploy
publish every attached artifact. The Source Plugin’s jar-no-fork goal (the plain jar goal first runs the generate-sources phase again, which jar-no-fork does not)
and the Javadoc Plugin’s jar goal are the usual ones, bound to the package phase with an execution each:
<plugins>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals><goal>jar-no-fork</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals><goal>jar</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
(The versions come from pluginManagement; the executions are named so that -X and the logs show where each JAR comes from, and
because ids must be unique within a plugin, see
Build Lifecycle.) Deploying tidepool-model
(grep -E "source:|javadoc:|Building jar|Uploaded to.*(jar|pom) |BUILD"):
[INFO] Building jar: <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] --- source:3.4.0:jar-no-fork
[INFO] Building jar: <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT-sources.jar
[INFO] --- javadoc:3.12.0:jar
[INFO] Building jar: <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT-javadoc.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1-sources.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1-javadoc.jar
[INFO] BUILD SUCCESS
The JAR’s Javadoc run printed twelve no @param for ... warnings for the records of tidepool, which are not shown. The extra files are named
<artifactId>-<version>-<classifier>.<extension>, here with the same timestamp and build number as the main artifact:
maven-metadata.xml
tidepool-model-1.0.0-TS-1-javadoc.jar
tidepool-model-1.0.0-TS-1-sources.jar
tidepool-model-1.0.0-TS-1.jar
tidepool-model-1.0.0-TS-1.pom
and the metadata lists a snapshotVersion per classifier:
<snapshotVersions>
<snapshotVersion>
<extension>pom</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<classifier>sources</classifier>
<extension>jar</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<classifier>javadoc</classifier>
<extension>jar</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
</snapshotVersions>
install copies the same set, with the literal version, to the local repository (the four Installing lines):
[INFO] Installing <proj>/tidepool-model/pom.xml to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.pom
[INFO] Installing <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] Installing <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT-sources.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT-sources.jar
[INFO] Installing <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT-javadoc.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT-javadoc.jar
The classifier is the way to publish several artifacts from one project: a test JAR (jar:test-jar, type test-jar, classifier
tests; see
Packaging JARs, WARs & Manifests) or an assembly (see
Uber JARs with Shade & Assembly). A consumer asks for
one with <classifier> in its dependency. The Javadoc Plugin runs the JDK’s javadoc tool, so its usage is also affected by the JDK
(see Compiling & Toolchains).
deploy-file: uploading a file that is not a project
deploy:deploy-file uploads files that no project of the current build produced: a vendor JAR for the company repository, or the output
of a non-Maven build. It needs no POM at all. With a JAR compiled with javac and packed with jar (so with no pom.xml inside), and
the sources and Javadoc JARs of the earlier run, from a directory without a POM
(grep -E "deploy-file|Uploaded|BUILD"; <df> is that directory):
mvn -B org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy-file -Dfile=tidepool-extra.jar \
-DgroupId=io.tidepool -DartifactId=tidepool-extra -Dversion=1.0.0 -Dpackaging=jar \
-Durl=file://<df>/repo -DrepositoryId=tidepool-releases \
-Dsources=tidepool-extra-sources.jar -Djavadoc=tidepool-extra-javadoc.jar
[INFO] --- deploy:3.2.0:deploy-file
[INFO] Uploaded to tidepool-releases: file://<df>/repo/io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0.jar
[INFO] Uploaded to tidepool-releases: file://<df>/repo/io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0.pom
[INFO] Uploaded to tidepool-releases: file://<df>/repo/io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0-sources.jar
[INFO] Uploaded to tidepool-releases: file://<df>/repo/io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0-javadoc.jar
[INFO] Uploaded to tidepool-releases: file://<df>/repo/io/tidepool/tidepool-extra/maven-metadata.xml
[INFO] BUILD SUCCESS
The tree was the JAR, the POM, the two classified JARs and one maven-metadata.xml:
./io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0-javadoc.jar
./io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0-sources.jar
./io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0.jar
./io/tidepool/tidepool-extra/1.0.0/tidepool-extra-1.0.0.pom
./io/tidepool/tidepool-extra/maven-metadata.xml
and the generated POM and the release’s metadata were:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>io.tidepool</groupId>
<artifactId>tidepool-extra</artifactId>
<version>1.0.0</version>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>io.tidepool</groupId>
<artifactId>tidepool-extra</artifactId>
<versioning>
<release>1.0.0</release>
<versions>
<version>1.0.0</version>
</versions>
<lastUpdated>DATETIME</lastUpdated>
</versioning>
</metadata>
For a release, the metadata of the artifact has a <release> entry, which a snapshot’s metadata does not.
The embedded POM that wins
A JAR built by Maven contains its own POM in META-INF/maven/<groupId>/<artifactId>/pom.xml. deploy-file takes the POM
from there when no pomFile is given, even if the coordinates on the command line are different. Uploading the JAR of tidepool-model as
tidepool-extra (the JAR holds):
META-INF/maven/io.tidepool/tidepool-model/pom.xml
META-INF/maven/io.tidepool/tidepool-model/pom.properties
succeeded, and the POM it wrote for tidepool-extra was the one of tidepool-model, with its parent and its dependency (the
grep -E "BUILD|artifactId|<version>" of the run):
[INFO] BUILD SUCCESS
<artifactId>tidepool-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<artifactId>tidepool-model</artifactId>
<artifactId>junit-jupiter</artifactId>
-DgeneratePom=true did not change it. The artifact was published with the wrong coordinates inside its own POM, and consumers
would resolve the dependencies of another artifact. Pass an explicit POM; the file is the minimum one:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>io.tidepool</groupId>
<artifactId>tidepool-extra3</artifactId>
<version>1.0.0</version>
</project>
With -DpomFile=extra-pom.xml and the coordinates taken from it:
[INFO] Uploaded to tidepool-releases: file://<df>/repo2/io/tidepool/tidepool-extra3/1.0.0/tidepool-extra3-1.0.0.jar
[INFO] Uploaded to tidepool-releases: file://<df>/repo2/io/tidepool/tidepool-extra3/1.0.0/tidepool-extra3-1.0.0.pom
[INFO] BUILD SUCCESS
Deploying a site
The site child of distributionManagement is the target of site:deploy, which uploads target/site using the same transports and the
same server lookup. Adding
<site>
<id>tidepool-site</id>
<url>${repo.url}/site/${project.artifactId}</url>
</site>
to the parent and running mvn -B -pl tidepool-model site site:deploy (the site content and the reports are on
Site & Reporting; grep -E "site:|BUILD") printed:
[INFO] --- site:3.22.0:site (default-site) @ tidepool-model ---
[INFO] --- site:3.22.0:deploy (default-cli) @ tidepool-model ---
[INFO] BUILD SUCCESS
The pages were written below the site URL. Note the path: site/tidepool-model/tidepool-model, with the artifact id twice. The url
of a site is inherited by the children with the child’s artifact id appended, which is the intention for a multi-module site (one
directory per module) and a surprise when the parent URL already contains ${project.artifactId}. help:evaluate for the child:
file://<proj>/build/repo/site/tidepool-model/tidepool-model
file://<proj>/build/repo/releases
and the directory listing of build/repo/site (first lines of find . -maxdepth 3 | sort) was:
.
./tidepool-model
./tidepool-model/tidepool-model
./tidepool-model/tidepool-model/css
./tidepool-model/tidepool-model/dependencies.html
./tidepool-model/tidepool-model/dependency-info.html
./tidepool-model/tidepool-model/dependency-management.html
./tidepool-model/tidepool-model/distribution-management.html
The second line of the first listing is the repository URL of the same child, for comparison: unlike the site URL it was not extended
with the artifact id. The files of the first listing’s directory follow. A site published to GitHub Pages is usually pushed by a CI workflow
from target/site rather than with site:deploy; see the site page.
Source: Maven Site Plugin: site:deploy
Maven 3.9, 3.10 and 4: what differs
| Topic | Maven 3.9.16 and 3.10.0 | Maven 4.0.0-rc-7 (release candidate) |
|---|---|---|
What is installed or deployed for a module |
The POM as written ( |
Two POMs: the consumer POM as |
Metadata |
One |
An extra |
Credentials |
3.10 warns and scopes; 3.9.16 does neither |
A short warning; |
Install and Deploy Plugin 4.0.0-beta-3 |
Fail: unmet prerequisites |
Work; the plugins of the 4.0 line |
On Maven 4, mvn -B -pl tidepool-model deploy (grep -E "install:|deploy:|Installing|Uploaded to.*(pom|jar) |BUILD") printed:
[INFO] --- install:3.2.0:install
[INFO] Installing <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] Installing <proj>/tidepool-model/pom.xml to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT-build.pom
[INFO] Installing <proj>/tidepool-model/target/consumer-11803575979158663052.pom to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.pom
[INFO] --- deploy:3.2.0:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1-build.pom
[INFO] BUILD SUCCESS
and the repository listing (without checksums) and the metadata were:
maven-metadata.xml
tidepool-model-1.0.0-TS-1-build.pom
tidepool-model-1.0.0-TS-1.jar
tidepool-model-1.0.0-TS-1.pom
<snapshotVersions>
<snapshotVersion>
<classifier></classifier>
<extension>jar</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<classifier>build</classifier>
<extension>pom</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
<snapshotVersion>
<classifier></classifier>
<extension>pom</extension>
<value>1.0.0-DATE.TIME-1</value>
<updated>DATETIME</updated>
</snapshotVersion>
</snapshotVersions>
The consumer POM is generated into target/consumer-<number>.pom and published as the POM of the artifact; the build POM keeps
everything that only a build needs, such as <modules> of an aggregator. Its content, and the new model version 4.1.0, are on
Maven 4: POM Model & Consumer POM. With the plugin
versions of the 4.0 line pinned, the same build on Maven 4 printed:
[INFO] --- install:4.0.0-beta-3:install
[INFO] Installing <proj>/tidepool-model/target/tidepool-model-1.0.0-SNAPSHOT.jar to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.jar
[INFO] Installing <proj>/tidepool-model/pom.xml to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT-build.pom
[INFO] Installing <proj>/tidepool-model/target/consumer-NNN.pom to <lab>/m2/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-SNAPSHOT.pom
[INFO] --- deploy:4.0.0-beta-3:deploy
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.jar
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1-build.pom
[INFO] Uploaded to tidepool-snapshots: file://<proj>/build/repo/snapshots/io/tidepool/tidepool-model/1.0.0-SNAPSHOT/tidepool-model-1.0.0-TS-1.pom
[INFO] BUILD SUCCESS
The 4.0.0-beta-3 plugins need Maven 4. On Maven 3.10.0 and 3.9.16 the build fails when it reaches them, with the message of the prerequisite check:
[ERROR] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-install-plugin:4.0.0-beta-3:install (default-install) on project tidepool-model:
[ERROR] The plugin org.apache.maven.plugins:maven-install-plugin:4.0.0-beta-3 has unmet prerequisites:
[ERROR] Required Maven version 4.0.0-rc-6 is not met by current version 3.10.0
[ERROR] For more information about the errors and possible solutions, please read the following articles:
(Maven 3.9.16 printed the same text with "3.9.16" in the last line.) Do not put the 4.0 line of a plugin in a POM that is also built with Maven 3. The migration is on Migrating to Maven 4, and the release candidate status means anything above may change before 4.0.0.
The same job in Ant and Gradle
| Tool | Local install | Remote publishing |
|---|---|---|
Maven |
|
|
Ant |
the Resolver Ant Tasks |
the Resolver Ant Tasks |
Gradle |
the |
|
The three tools write the same repository layout, which is why a project deployed by one can be consumed by another; the comparison, with
the same tidepool modules published to build/repo by all of them, is on
Repositories & Publishing Across Tools.
Classic problems
-
HTTP Status: 401on deploy. No<server>with the right<id>, a wrong password, or a password encrypted with a master password the build cannot read. Check the id ofdistributionManagementagainstsettings.xml, and the log of the server. -
HTTP Status: 400or409on the second deploy of a release. The repository does not allow redeploying a release. Release a new version. -
repository element was not specified in the POM. NodistributionManagementand noaltDeploymentRepository. -
The snapshots end up in the releases repository, or are rejected: no
snapshotRepository. -
deploy-fileuploads a POM with other coordinates. The JAR has an embeddedpom.xml; pass-DpomFile. -
Some modules were deployed and the build failed. The default deploys per module; use
deployAtEnd, and know it is not atomic. -
Unrecognized value ... for maven.deploy.skip. The value must betrue,false,releasesorsnapshots. -
A deploy from a CI job overwrites a developer’s snapshot. Snapshots are shared by design; use CI-friendly versions that put the commit in the version (see CI-Friendly Versions).
-
A POM with
${revision}is published unresolved on Maven 3. See the same page. -
A plugin 4.0 line on Maven 3.
has unmet prerequisites, above.
Where the books are outdated
-
Older books describe
deploy:deploywith the layout inaltDeploymentRepository(id::default::url), thelayoutanduniqueVersionchildren ofdistributionManagement, and SCP or WebDAV as the normal transports. Maven 3 has one layout, ignoresuniqueVersion, uses the native HTTP transport and needs a Wagon extension for SCP and FTP. -
Plain
http:repository URLs for downloads are blocked by default since Maven 3.8.1 (the settings and repositories pages); uploads tolocalhostover HTTP, as here, are for tests. -
Publishing to Central through OSSRH (
oss.sonatype.org) ended on 2025-06-30; see Publishing to Maven Central. -
The
release-profileof the super POM, which added sources and Javadoc JARs to a release, is removed in Maven 3.10 (see Releasing); attach them with the executions shown above.
What was not run here
-
Any real repository manager, Central, or an authenticated remote server other than the localhost lab server; the
scp,sftpandftptransports with Wagon extensions;privateKeyauthentication. -
retryFailedDeploymentCount,allowIncompleteProjects,installAtEndcombined with failing builds, and thestatusanddownloadUrlelements ofdistributionManagement. -
The test JAR (
test-jar) and assembly attachments (shown on the pages named above).