What is required to use maven for artifact deployment? - maven

I am setting up a CI system with repository archive, based on apache Archiva. Among various techniques for deploying file there, the most promising one seems to be using maven (as opposed to REST api that would require too much curl calls and Web interface that is not for automation).
It seems that for deploying artifact, such as zip archives of build artifacts, in maven there is a following plugin: deploy:deploy-file. However an attempt to simply invoke that command gave me no results.
I did not work with maven before; currently our builds are done by invoking cmake on source directory, then make from shell script. What do i need to add and have to be able to use maven for deploying the resulting artefact?
Is it necessary to create a pom file? If so, what steps do i need to add?

You can use deploy:deploy-file to upload these artifacts, and by default it will generate a POM for you. What you will need is to:
install Maven
create a settings.xml file, with a <server> element that contains the credentials for deploying to Archiva over HTTP
There's a bit more information here: http://maven.apache.org/plugins/maven-deploy-plugin/usage.html. If you are generating the POM, then you will need to supply the groupId (distinct grouping of artifacts), artifactId (filename without version), version (artifact version), and packaging (typically extension, such as zip) parameters along with the required ones of repositoryId, url and file.
However, it's not required to use Maven, or the REST API - you can also just use a simple HTTP PUT call:
curl -T artifact.zip http://localhost:8080/archiva/repository/my-releases/group/artifact/version/artifact-version.zip
You may also use scp, ftp, etc. to place the file directly in the Archiva filesystem. Note that in that case, you'll have to wait for Archiva's scheduled scan to pick it up.

Maven needs a WebDAV like repository (see also Nexus, Artifactory) to deploy its artifacts (mostly jar, war, ear or assemblies.
Look here for a how to on setting up Maven with Archiva.

Yes, it is necessary to have pom.xml in maven and also it is obvious that you need maven to be installed on your machine. Bare minimum, you need to provide artifact ID(like name of jar), group id(like package to identify the location of artifact in repository) and version. By default package structure will be jar unless you mention it as war/ear/pom. If you want to use any dependencies you can mention them in dependencies section.
Following is the minimum pom that is required.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
</project>
Here is the quick tutorial to get acquainted with maven.

Related

How to upload jars with different classifiers and same pom.xml on Nexus 2 from REST API with curl?

I've been struggling with this for quite some while.
Background: We have some Jenkins jobs that produce jars and other jobs that upload the jars to nexus. In our case we are looking for myJar-1.0.jar and myJar-1.0-myClassifier.jar. Obviously they are both produced with the same pom.xml file.
What I am trying to achieve is to upload both of them on Nexus (We use Nexus2) through the REST API with a curl command using the pom.xml.
The pom.xml file looks something like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.group.id</groupId>
<artifactId>myJar</artifactId>
<version>1.0</version>
<name>My Name</name>
<packaging>pom</packaging>
<properties>
<otherJar.version>1.5</otherJar.version>
...
</properties>
<dependencies>
<dependency>
<groupId>x.y</groupId>
<artifactId>art</artifactId>
<version>{$otherJar.version}</version>
</dependency>
...
</dependencies>
</project>
The curl command that I'm using to upload (the jar without the classifier) is:
curl -v -F r=releases -F hasPom=true -F e=jar -F file=#pom.xml -F file=#myJar-1.0.jar -u user:pass http://link/to/nexus/service/local/artifact/maven/content
and this works as expected. The jar is uploaded to nexus with the groupid, artifactid and version mentioned in the pom.xml file.
My question is - after I upload this jar, how can I upload the jar with the classifier provided we have the same pom.xml?
Should I alter the pom.xml file? If so, how?
Should I modify the curl command? I tried adding -F c=myClassifier but that didn't work. That resulted in an error produced by Nexus: <html><body><error>Repository with ID='releases' does not allow updating artifacts.</error></body></html> (because there is already an artifact with same groupid, artifactid and version - the one that I just uploaded; it seems that the classifier is ignored)
So I tried multiple solutions and could not come up with one that satisfies my needs (uploads multiple jars with the same pom and different classifiers through the REST API) with one or more calls.
I ended up installing maven on the machine that was running my scripts and using the maven deploy:deploy-file as follows:
maven deploy:deploy-file
-DpomFile=pom.xml
-Dfile=myJar-1.0.jar
-Dpackaging=jar // this doesn't have to be specified, it will be taken from the pom file, however in my case the pom was specifying <packaging>pom</packaging> and the jar ended up with .pom extention
-Dclassifiers=myClassifier // here you can define multiple classifiers, comma-separated
-Dtypes=jar // same as above
-Dfiles=myJar-1.0-myClassifier.jar // same as above
-DrepositoryId=nexus
-Durl=http://host:port/nexus/content/repositories/releases
You can find the full documentation here.
Also, please note that you have to define your nexus repository credentials in the maven's configuration file (maven_home/conf/settings.xml).
Find the node servers in that file and add something similar to this:
<server>
<id>nexus</id>
<username>yourUserName</username>
<password>yourPass</password>
</server>
In the end I want to stress the fact that I think this is a missing feature/bug of Nexus2's REST API. As you can see from the snippet above, this can be achieved by uploading the jars with maven and this can also be achieved manually from the Nexus interface. However, I did not manage to do it through the REST API. It could be possible because I did not find the right way to call it, but I couldn't find (almost) any documentation either.. a bummer.
Also, final note for this question - I will leave this unsolved since this does not answer my original question (How can this be achieved through the REST API?).

Generate the maven artifact path

Maven install knows all the artifacts generated by a build, and will push them locally.
Installs the project's main artifact, and any other artifacts attached by other plugins in the lifecycle, to the local repository.
the help plugin probably supports this, but not sure of right expression
# has all the pieces (artifact, version, type) but is it fair to assume filename will always be that combo?
mvn help:evaluate -Dexpression=project.artifact
Is there any way to get that list of paths from a maven command?
I want to generate a list of specific artifacts to persist as artifact results in a build process, without publishing to a maven repo.
Artifact paths in Maven repository will follow the following formula by default:
groupId is broken into folders using the full stops as delimiter, then artifactId and version form the last two folders
filename of the artifact consists of artifactId and version, type is defined by packaging
So, let's say you have a multi-module project with main pom.xml:
<groupId>com.foobar.my.business</group>
<artifactId>myApp</artifactId>
<version>1.0-SNAPSHOT</version>
And it has two submodules, first is web module that creates a REST endpoint:
<parent>
<groupId>com.foobar.my.business</group>
<artifactId>myApp</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>myApp-web</artifactId>
<packaging>war</packaging>
Second one is persistence layer:
<parent>
<groupId>com.foobar.my.business</group>
<artifactId>myApp</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>myApp-persistence</artifactId>
<packaging>jar</packaging>
Let's say your local repository is found from ~/.m2/repository. Then your artifacts will be saved in local repository at:
~/.m2/repository/com/foobar/my/business/myapp/1.0-SNAPSHOT/myapp-1.0-SNAPSHOT.pom
~/.m2/repository/com/foobar/my/business/myapp-web/1.0-SNAPSHOT/myapp-web-1.0-SNAPSHOT.pom
~/.m2/repository/com/foobar/my/business/myapp-web/1.0-SNAPSHOT/myapp-web-1.0-SNAPSHOT.war
~/.m2/repository/com/foobar/my/business/myapp-persistence/1.0-SNAPSHOT/myapp-persistence-1.0-SNAPSHOT.pom
~/.m2/repository/com/foobar/my/business/myapp-persistence/1.0-SNAPSHOT/myapp-persistence-1.0-SNAPSHOT.jar
An artifact's final build name and the local repository location can be tinkered with. But you can use the following expressions to check those:
${settings.localRepository} will return path to local repository.
${project.build.finalName} will return final build artifact name.
To get this list in almost the correct format, you can run:
On Windows mvn -q exec:exec -Dexec.executable="cmd" -Dexec.args="/C echo ${settings.localRepository}\${project.groupId}\${project.artifactId}\${project.version}\${project.build.finalName}.${project.packaging}"
On POSIX mvn -q exec:exec -Dexec.executable='echo' -Dexec.args='${settings.localRepository}/${project.groupId}/${project.artifactId}/${project.version}/${project.build.finalName}.${project.packaging}'
Then you just have to fix the full stops in groupId.
There is also a mvn dependency:build-classpath command which will show the location of each dependency on the file system that can come in handy sometimes.

What are .pom files in Maven build artifacts?

You can see from browsing any repository that Maven build artifacts contain .pom files. The contents of these files look a whole lot like pom.xml files. Where do these files come from? What are they used for? Additionally, build artifacts have maven-metadata.xml files, at least on search.maven.org, and these files have substantially the same content as the .pom files. What's the deal with that?
The files are the pom files from within the project. Those are deployed to the maven repository during the release build or by other build tools as well (gradle, ivy, etc.).
Those files are needed to describe the dependencies of the appropriate artifact otherwise you have no other opportunity to store such kind of information.
In your particular example (really old 2005) this is a pom file which is created at a time of times where maven was not such distributed. In this case the file does not contain any dependencies.
If you take a look here:
http://search.maven.org/#browse%7C-77609479
you see a number of versions of a single artifact. If you now take a look into the maven-metadata.xml you will see list of available versions.
The best answer I have found so far is in this SO thread. Here is an exact quote, highlighting the crux of the explanation:
Every jar needs to have a pom file describing it, you can just add something simple like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>aGroup</groupId>
<artifactId>aArtifactId</artifactId>
<version>aVersion</version>
<packaging>jar</packaging>
<name>a Name</name>
</project>
Another good explanation:
POM that is installed to Nexus will describe the jar. Used to pull the
dependencies that are associated to corresponding jar. When we add the
jar as dependency to our project, all the jars required for the
included jar will be identified through the corresponding pom.

Maven: Selecting Parent Project Based On Profile

I have a maven project - it is a plugin for jenkins. It's parent should be a:
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.414</version>
</parent>
But at the same time this plugin can be also used for hudson, without changing any line of code. But the parent project for it should be:
<parent>
<groupId>org.jvnet.hudson.plugins</groupId>
<artifactId>hudson-plugin-parent</artifactId>
<version>2.0.1</version>
</parent>
Can I specify 2 different profiles for that and use them to build plugin for jenkins or hudson accordingly? So that I call something like that:
mvn package -P jenkins
or
mvn package -P hudson
I have tried to specify properties in profiles, but those are not replaced by their values inside the <parent> tag. So is there any other possibility to build plugin for both, but with as much as possible common code and files?
Added: So, if I cannot do that, what should I do then? How to refactor? What the new structure should be?
As already mentioned, this is not possible.
Also, it is not possible to set a property for the parent's version as the interpolation for that happens a lot earlier than the handling of the profiles.
I would suggest that you create a masterbuild project as follows:
master
|-plugin-jenkins
|-plugin-hudson
|-plugin-assembly
The master should build all three as usual. However, in the assembly, you could add each of the two plugins as dependencies in separate profiles. And... each of these plugins can have the parent you like.
This is obviously somewhat a deviation from the Maven convention, but I believe it is a solution to your problem.
It's not possible because the tag "parent" is not available in the profiles section of the pom.
Currently we decided to stick with 1 repository and 2 separate pom.xml files, giving maven key which pom.xml use to build the project.
mvn package -f pom-jenkins.xml
mvn package -f pom-hudson.xml
No you cannot do that. you will have to refactor somehow to avoid the necessity.
As mentioned already not possible. I would suggest to make separate projects for jenkins plugin and hudson plugin. I assume that in not that far future that will not work anymore cause Hudons and Jenkins will diverge.
In general, you should be able to set the {group,artifact}Id and version of the parent POM via Java System Properties or Environment Variables, but it seems there is a Bug in Maven which will only be fixed in 4.x:
https://issues.apache.org/jira/browse/MNG-624
Another solution is to delegate the inclusion of the parent POM to your own parent POMs which you reference in the relativePath element, and change the content of the target e.g. via a symlink or cp command.
So in the main POM you would write:
<parent>
<groupId>org.mycompany.project</groupId>
<artifactId>foo-artifact</artifactId>
<version>1.0.0</version>
<relativePath>./my-parent.pom</relativePath>
</parent>
And in my-parent-jenkins you would just put:
<groupId>org.mycompany.project</groupId>
<artifactId>foo-artifact</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.414</version>
</parent>
The same project information with the block for hudson you put in my-parent-hudson.pom.
No you can either use
ln -s my-parent-jenkins.pom my-parent.pom
or
ln -s my-parent-hudson.pom my-parent.pom
to include the respective parent POM without the need to maintain two different main POM files for your project.
In case POM does not exist at the place referenced in relativePath, Maven will look up the POM in the remote repository[1], which is also an easy way to overwrite a parent POM locally.
[1] http://maven.apache.org/components/ref/3.3.9/maven-model/maven.html#class_parent

How can I deploy only the pom file to my snapshot repository in Maven?

I would like to be able to deploy only the POM artifact (file) without the main artifact (JAR, WAR, etc), when running mvn deploy and version is a SNAPSHOT version.
Why?
We several developers working on multiple Maven projects. We have a Hudson server with a job per Maven project and version (e.g. foo-1.2, foo-1.3). Each job builds the project and deploys it to a Nexus server (upon success). Maven projects under development are marked as such by using -SNAPSHOT postfix in the version. For example: 1.2-SNAPSHOT, 1.3-SNAPSHOT.
Here's a sample scenario how a developer work is damaged due to this architecture.
Assume two Maven projects: foo-core and foo-webapp, both at version 1.2-SNAPSHOT.
Developer A is working on foo-core, made several changes and compiled it.
Developer A continues to work, but on foo-webapp.
Developer B started working and changing foo-core. It commits his work and pushes it to the SCM.
Hudson is triggered by SCM; Builds foo-core and deploys it to the snapshot repository in Nexus.
Developer A is running mvn install on foo-webapp. Maven is checking with Nexus, and finds that there is a newer version of foo-core in Nexus. It downloads it (filled with developer B changes) and than it fails compilation, since the changes made by developer A are not in the jar located in the local repository. The download overrides the file installed there by developer A.
Existing Solutions
I looked into maven-deploy-plugin, but this plugin deploys all artifacts attached to the project. If they had a way to configure which artifacts to deploy, it would have been great.
Question: Is there any way to solve this without resorting to writing my own deploy plugin, based on maven-deploy-plugin?
Basically to the -Dfile parameter, instead of the artifact, pass the pom.xml. Run the command and yay! mvn deploy won't give you any issues now. Here's a sample deploy command :
$ mvn deploy:deploy-file -DpomFile=pom.xml -Dfile=./pom.xml -DgroupId=my.group.id -DartifactId=artifact-id -DrepositoryId=bigdata-upload-snapshots -Durl=http://maven.mymaven.com/content/repositories/snapshots/
A prerequisite for this is that the repository be added in your settings.xml
[Edit]: I have supplied the parameters -DgroupId and -DartifactId of the project in the sample deploy command but they're not required (refer to Zac's comment below)
I never heard of such a possibility and also would be very astonished if that would be possible. As the pom and the resulting artifact are some kind of unit it would make no scence (to me) to deploy only parts of them.
Nevertheless you should consider to make a separate pom project which specified dependencies and plugins you might want to use on your JAR/WAR projects like this:
<groupId>foo.bar</groupId>
<artifactId>my-pom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
and then inherit that pom project by your JAR/WAR projects like this:
<parent>
<groupId>foo.bar</groupId>
<artifactId>my-pom</artifactId>
<version>1.0.0</version>
</parent>
This is called project inheritance. You can change and deploy your pom project independent of the "child" artifacts.
EDIT after reading motivation:
As I understand you want to prevent maven to resolve SNAPSHOT artifacts from a repository (so that local version won't be overwritten). Have you ever tried to use the mvn -nsu option (see mvn -help)?
-nsu,--no-snapshot-updates Suppress SNAPSHOT updates
I never tried it but found this reported issue. Nevertheless I would give it a try (as the issue is not commented yet).
This works for me for deploying a pom file only (e.g next to an existing jar):
(Note: you need to specify packaging also, otherwise it will be uploaded as an .xml file which is not what you want.)
mvn deploy:deploy-file \
-Dfile=pom.xml \
-Dpackaging=pom \
-DgroupId=com.mycompany.package \
-DartifactId=my-artifact \
-Dversion=2.0.1 \
-DrepositoryId=serverIdFromSettingsXMLForCredentials \
-Durl=http://repositoryserver/myrepo/
Not exactly the answer these folks were asking for. My situation was I wanted to deploy only the parent pom. I'm using the spring-boot-thin-layout in a child module. This requires the parent module be deployed into artifactory. I added the following into my project. It enables skipping of install and/or deploy phase.
In my parent pom:
<properties>
<disable.install>true</disable.install>
<disable.deploy>true</disable.deploy>
<enable.deployAtEnd>true</enable.deployAtEnd>
</properties>
<profiles>
<profile>
<id>deploy-parent</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<disable.install>true</disable.install>
<disable.deploy>true</disable.deploy>
<deployAtEnd>${enable.deployAtEnd}</deployAtEnd>
</properties>
<build>
<finalName>${project.version}</finalName>
</build>
</profile>
</profiles>
And the in my child pom(s) or any module you don't want deployed with parent:
<properties>
<maven.install.skip>${disable.install}</maven.install.skip>
<maven.deploy.skip>${disable.deploy}</maven.deploy.skip>
<deployAtEnd>${enable.deployAtEnd}</deployAtEnd>
</properties>
So effectively when I run mvn deploy on the parent pom, it will compile all the modules, not run install on anything, and then at the end deploy any module not having `

Resources