Russell Bateman April 2019 last update:
Sonatype Nexus Repository OSS is a free artifact repository (artifactory) with universal support for popular formats.
$ curl -v -u admin:admin123 --upload-file com/acme/psjbase/986/psjbase-986-sources.jar \ https://maven.acme.com:8081/repository/testing/com/acme/psjbase/986/psjbase-986-sources.jar * Trying 10.10.10.164... * TCP_NODELAY set * Connected to maven.acme.com (10.10.10.164) port 8081 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS handshake, Client hello (1): * error:1408F10B:SSL routines:ssl3_get_record:wrong version number * stopped the pause stream! * Closing connection 0 curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
In Nexus, there are three repository types:
1. Proxy repositories —for access to public repositories, like a doorway to a public repository, in fact a link to a remote repository. When no local component is found, the request is forwarded to the remote repository.
Think Maven here; keeping high-use JARs locally for consumption, especially when your Internet connection is down while Maven is forwarded on to Maven Central in the case of artifacts that aren't held locally. Copies retrieved from remote repositories remain in the Nexus proxy repository until removed by the administrator.
Or think Docker; Docker Hub is targeted.
2. Hosted repository —for use by your development organization, but not public. In case of missing component, no forwarding on (to Maven Central, Docker Hub, etc.) is done. This is for use by your organization to host internal artifacts that should not be made public.
It's also a place to put third-party artifacts that aren't available publicly.
3. Repository groups —allows host repositories to be accessed from a single URL. This combines many repositories into one. For example, maven-public is a group that includes maven-central, maven-releases and maven-snapshots into one for easy access (instead of listing all three).
* Note that maven-snapshots is special: no artifacts can be put into it that do not have a version ending in -SNAPSHOT.
I'm going to create a place to use as an artifactory for Docker containers.
First, amend the ~/.m2/settings.xml file of every build user to contain:
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> <servers> <server> <id>nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-releases</id> <username>admin</username> <password>admin123</password> </server> </servers> <mirrors> <mirror> <id>central</id> <name>central</name> <url>http://your-host:8081/repository/maven-group/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> </settings>
In your project pom.xml, you'll need to set up Nexus as a repository. Remember (see in later notes) that a Nexus repository can be a proxy repository for Maven Central or others.
<project ...> ... <repositories> <repository> <id>maven-group</id> <url>http://your-host:8081/repository/maven-group/</url> </repository> </repositories> ... <dependencies> <dependency> (reference JARs you want to get from Nexus or Maven Central, etc.) </dependency> </dependencies> </project>
Upon issuing mvn {compile,test,install, etc.}, Maven will go to your Nexus instance.
There are pom.xml-based instructions you can add if you wish to publish what you build to your repository. See this page.
First, configure the Docker dæmon for your host to accept to work with HTTP instead of HTTPS. This is usually done by modifying /etc/docker/daemon.json thus. If this file doesn't exist, create it:
{ "insecure-registries" : [ "your-repo:8082", "your-repo:8083" ], "disable-legacy-registry" : true }
Restart Docker (sudo systemctl restart docker).
You'll have to authenticate your host to the artifactory thus:
$ docker login -u admin -p admin123 your-repo:8082 pulling $ docker login -u admin -p admin123 your-repo:8083 pushing
...which will create an entry in ~/.docker/config.json with authorizations:
{ "auths" : { "your-repo:8082" : { "auth" : "YWRtaW46YWRtaW4xMjM=" }, "your-repo : 8083": { "auth" : "YWRtaW46YWRtaW4xMjM=" } }
To pull images (e.g.: alpine) from the artifactory, use this command
$ docker pull your-repo:8082/httpd:2.4-alpine
To get information about tagging and pushing your container images, see this page.
I just wanted to get something—anything—to upload to Nexus. I happened to have an image and that works as a test.
$ curl -v -u admin:admin123 --upload-file notre-dame.jpg http://maven.acme.com:8081/repository/testing/com/example/notre-dame/1.0.0/notre-dame-1.0.0.jpg * Trying 10.10.10.164... * TCP_NODELAY set * Connected to maven.acme.com (10.10.10.164) port 8081 (#0) * Server auth using Basic with user 'admin' > PUT /repository/testing/com/example/notre-dame/1.0.0/notre-dame-1.0.0.jpg HTTP/1.1 > Host: maven.acme.com:8081 > Authorization: Basic YWRtaW46YWRtaW4xMjM= > User-Agent: curl/7.58.0 > Accept: */* > Content-Length: 100939 > Expect: 100-continue > < HTTP/1.1 100 Continue * We are completely uploaded and fine < HTTP/1.1 201 Created < Date: Tue, 16 Apr 2019 21:03:05 GMT < Server: Nexus/3.10.0-04 (OSS) < X-Frame-Options: SAMEORIGIN < X-Content-Type-Options: nosniff < Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation < X-Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation < Content-Length: 0 < * Connection #0 to host maven.acme.com left intact
$ curl -v -u admin:admin123 --upload-file com/acme/psjbase/986/psjbase-986.jar http://maven.acme.com:8081/repository/testing/com/acme/psjbase/986/psjbase-986.jar $ curl -v -u admin:admin123 --upload-file com/acme/psjbase/986/psjbase-986.jar.md5 http://maven.acme.com:8081/repository/testing/com/acme/psjbase/986/psjbase-986.jar.md5 $ curl -v -u admin:admin123 --upload-file com/acme/psjbase/986/psjbase-986.jar.sha1 http://maven.acme.com:8081/repository/testing/com/acme/psjbase/986/psjbase-986.jar.sha1 $ curl -v -u admin:admin123 --upload-file com/acme/psjbase/986/psjbase-986.pom http://maven.acme.com:8081/repository/testing/com/acme/psjbase/986/psjbase-986.pom
I want to get Nexus working for deploying JARs to it. I'll be following the page linked at the bottom of this note, or Using Nexus 3 as Your Repository—Part 1: Maven Artifacts. Here's my first stab at that. For posterity (myself), I'm going to detail the entire struggle since knowing how I failed is almost as useful as knowing how I succeeded. Sorry, it's going to get ugly.
First, in order to understand this thoroughly, I think I'll start from scratch including my own copy of Nexus. I'll use host moria, for the installation. It's a VM running Ubuntu Server 18.04.2 LTS Bionic Beaver.
See the next two sections. In another note I'll handle fronting Docker Hub using Nexus.
russ@moria:~/dev/nexus$ docker pull sonatype/nexus3 Using default tag: latest latest: Pulling from sonatype/nexus3 8ba884070f61: Pull complete 10f65a60e03d: Pull complete e4bf25d22017: Pull complete Digest: sha256:664306d8b367ff2ebf7ff3272560404d34ec9b611826d9777c4fd5b3c8b79b7a Status: Downloaded newer image for sonatype/nexus3:latest russ@moria:~/dev/nexus$ docker run -d -p 8081:8081 --name nexus sonatype/nexus3 33cfd17ee5a58871b8ced2f1601911c778c13bd6948f8a1c65151a1f927dc003
I'm doing this test from a different host, nargothrond. Note also that one should not jump the gun immediately: it takes Nexus two to three minutes to launch in a new container. Before that finishes, responses to curl or answering a browser GET may be refused; this doesn't mean that Nexus isn't working.
russ@nargothrond: ~ $ curl -u admin:admin123 http://moria:8081/service/metrics/ping curl: (7) Failed to connect to moria port 8081: Connection refused (Because moria was intentionally rebooted, ...) russ@nargothrond ~ $ curl -u admin:admin123 http://moria:8081/service/metrics/ping pong (And, on moria itself:) russ@moria:~/dev/nexus$ curl -u admin:admin123 http://localhost:8081/service/metrics/ping pong
So far, so good. Note (to self) that, just as for Kibana, I can't access Nexus via Chrome, only via Firefox or an incognito Chrome window, because Chrome insists on using HTTPS rather than HTTP even if you specify the latter.
http://moria:8081/
I sign in using admin/admin123. Clicking on Browse, I see the following assets and components (repositories). I'm concentrating on Maven, of course. Note that all the repositories are empty (as this is a new Nexus installation).
Now, it's important to map additional ports, 8082, 8083, in the Docker run command in order to be able to pull and push from Maven. To reissue the run command, it will be necessary to remove the container, and its name, then re-run it:
russ@moria:~/dev/nexus$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7a8a9480d89f sonatype/nexus3 "sh -c ${SONATYPE_DI..." 2 hours ago Up 2 hours 0.0.0.0:8081->8081/tcp nexus russ@moria:~/dev/nexus$ docker stop 7a8a9480d89f 7a8a9480d89f russ@moria:~/dev/nexus$ docker rm 7a8a9480d89f 7a8a9480d89f russ@moria:~/dev/nexus$ docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 --name nexus sonatype/nexus3 d48805b220159dee6d59ef5b78acf8119f96de2f5ac58330576f12ef3b4767ef russ@moria:~/dev/nexus$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d48805b22015 sonatype/nexus3 "sh -c ${SONATYPE_DI..." 2 minutes ago Up 2 minutes 0.0.0.0:8081-8083->8081-8083/tcp nexus
(If you had left the browser window open at Nexus, you probably noticed that you lost connection, then got it back after restart.)
If we wanted to force Nexus, which is running in a Docker container, nevertheless to place its data container in the filesystem of the host running it, we could append another option to the Docker run command. This is desirable because we don't see Nexus data as ephemeral. We want to persist it. We'd like to dedicate subdirectory /opt/nexus-data on our host, moria, to be where Docker sets up Nexus' data volume, also named /nexus-data. But, we cannot do this. You'll notice that... (protracted, unhappy discusion here)
$ docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 \ -v /opt/nexus-data:/nexus-data \ --name nexus sonatype/nexus3 root@moria:/opt# ll total 16 drwxr-xr-x 4 root root 4096 Apr 24 13:02 . drwxr-xr-x 23 root root 4096 Apr 24 11:00 .. drwx--x--x 4 root root 4096 Apr 23 12:16 containerd drwxr-xr-x 2 root root 4096 Apr 24 13:02 nexus-data
However, because this subdirectory belongs to root, the container will not remain up—unable to write to it. Observe this diagnosis:
russ@moria:~/dev/nexus$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES bbec00ca76b1 sonatype/nexus3 "sh -c ${SONATYPE_DI..." 7 seconds ago Exited (255) 1 second ago nexus russ@moria:~/dev/nexus$ docker logs bbec00ca76b1 mkdir: cannot create directory '../sonatype-work/nexus3/log': Permission denied mkdir: cannot create directory '../sonatype-work/nexus3/tmp': Permission denied Warning: Cannot open log file: ../sonatype-work/nexus3/log/jvm.log Warning: Forcing option -XX:LogFile=/tmp/jvm.log OpenJDK 64-Bit Server VM warning: Cannot open file ../sonatype-work/nexus3/log/jvm.log due to No such file or directory java.io.FileNotFoundException: ../sonatype-work/nexus3/tmp/i4j_ZTDnGON8hezynsMX2ZCYAVDtQog=.lock (No such file or directory) at java.io.RandomAccessFile.open0(Native Method) at java.io.RandomAccessFile.open(RandomAccessFile.java:316) at java.io.RandomAccessFile.(RandomAccessFile.java:243) at com.install4j.runtime.launcher.util.SingleInstance.check(SingleInstance.java:72) at com.install4j.runtime.launcher.util.SingleInstance.checkForCurrentLauncher(SingleInstance.java:31) at com.install4j.runtime.launcher.UnixLauncher.checkSingleInstance(UnixLauncher.java:88) at com.install4j.runtime.launcher.UnixLauncher.main(UnixLauncher.java:67) Unable to update instance pid: Unable to create directory /nexus-data/instances /nexus-data/log/karaf.log (No such file or directory) Unable to update instance pid: Unable to create directory /nexus-data/instances
Now, tweak /opt/nexus-data (unnaturally) to be writable and you'll see that it works:
root@moria:/opt# chmod a+rwx nexus-data/ root@moria:/opt# ll total 16 drwxr-xr-x 4 root root 4096 Apr 24 13:38 . drwxr-xr-x 23 root root 4096 Apr 24 11:00 .. drwx--x--x 4 root root 4096 Apr 23 12:16 containerd drwxrwxrwx 2 russ russ 4096 Apr 24 13:38 nexus-data russ@moria:~/dev/nexus$ docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 \ --volume /opt/nexus-data:/nexus-data \ --name nexus sonatype/nexus3 6cee176ef003b44be7a1adc5696144d68d22c9f9889c95b4753e584068ba86c0 russ@moria:~/dev/nexus$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6cee176ef003 sonatype/nexus3 "sh -c ${SONATYPE_DI..." 4 seconds ago Up 2 seconds 0.0.0.0:8081-8083->8081-8083/tcp nexus russ@moria:~/dev/nexus$ docker exec -it 6cee176ef003 bash bash-4.2$ whoami nexus
User nexus must be, then, the owner of /opt/nexus-data in order to be able to write to it. However, this user doesn't exist on the host:
root@moria:/opt# cat /etc/passwd | grep nexus
Since Docker 1.7, this is a proposed solution, but it doesn't work, especially in the presence of SELinux. It involves adding a third part to the volume-mapping option of docker run:
The Nexus Docker Hub read-me gives this as a solution, but it doesn't work either because, no matter what, the host path ends up being created by root. I'm betting that they always run Docker as root, so they've never noticed this screw-up. You can only reasonably pre-create the mapped filesystem entry point, establish its ownership and privileges as promiscuous, then launch Docker:
russ@moria:~/dev/nexus$ mkdir nexus-data russ@moria:~/dev/nexus$ chmod a+w nexus-data/ russ@moria:~/dev/nexus$ ll -d nexus-data/ drwxrwxrwx 2 russ russ 4096 Apr 24 14:36 nexus-data/ russ@moria:~/dev/nexus$ docker volume create --name nexus-data nexus-data russ@moria:~/dev/nexus$ docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 \ --volume /home/russ/dev/nexus/nexus-data:/nexus-data \ --name nexus sonatype/nexus3
This sucks badly. The only real way to do this is by way of Docker Compose. I'm not going to cover that right now. So, this saga was not too helpful except for what there was to learn from it.
Back, instead, to our work with the artifactory (enough of Docker now)...
Note: If you create a new blob store for each new repository you create, the data for it will be in a different subdirectory under nexus-data. I'll look into the significance of this later, I hope, but this is only polish to be rubbed on the fender.
This doesn't trash Maven for the entire host you're on—only the user that does it. Normally, at least in the past, I've never generally even had a settings.xml at ~/.m2, so this file is completely devoted to my use of Nexus (and nothing else, that is, the contents aren't prescribed by any other consideration).
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> <servers> <server> <id>nexus-central</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-releases</id> <username>admin</username> <password>admin123</password> </server> </servers> <mirrors> <mirror> <id>central</id> <name>central</name> <url>http://moria:8081/repository/maven-central/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> <profiles> <profile> <id>nexus</id> <!--Enable snapshots for the built in central repo to direct --> <!--all requests to nexus via the mirror --> <repositories> <repository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <!--make the profile active all the time --> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>
This is a very simple pom.xml file for a tiny project I once did. The lines specifically added to it to make it go to my Nexus artifactory first, then to Maven, are highlighted. Of course, moria is hardly an obvious name for my Nexus artifactory: it would be better to name it something mneumonic.
<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.acme</groupId> <artifactId>json-to-xml</artifactId> <version>1.0.0-SNAPSHOT</version> <name>json-to-xml</name> <description>JSON-to-XML filter not requiring a POJO description of the JSON.</description> <properties> <log4j.version>2.1</log4j.version> <junit.version>4.12</junit.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <repositories> <repository> <id>maven-central</id> <url>http://moria:8081/repository/maven-central/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <resources> <resource> <directory>src</directory> <excludes> <exclude>**/*.java</exclude> </excludes> </resource> </resources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.acme.filter.JsonToXmlFilter</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build> </project>
Next up: using pom.xml to update the Nexus artifactory with newly created artifacts. This is done by an addition that I'm going to put at the end. (For some reason, my source-code display macros are lower-casing identifiers in the segment below: beware!)
nexus-snapshots http://moria:8081/repository/maven-snapshots/ nexus-releases http://moria:8081/repository/maven-releases/
This artifact pushing will never go "higher" than Nexus (because we do not have rights in places like Maven Central, etc.). This is how it works:
$ mvn deploy
Right now, on my test server (moria), these two repositories are empty. Indeed, after running it, I see this:
tester@nargothrond:~/dev/json-to-xml$ mvn deploy tester@nargothrond:~/.m2$ tree repository/com/acme/ repository/com/acme/ └── json-to-xml ├── 1.0.0-SNAPSHOT │ ├── json-to-xml-1.0.0-SNAPSHOT.jar │ ├── json-to-xml-1.0.0-SNAPSHOT.pom │ ├── maven-metadata-local.xml │ └── _remote.repositories └── maven-metadata-local.xml 2 directories, 5 files
...and I see the same thing in Nexus. Notice all the noise at the end of Maven's run that says it all:
tester@nargothrond:~/dev/json-to-xml$ mvn deploy . . . [INFO] --- maven-deploy-plugin:2.7:deploy (default-deploy) @ json-to-xml --- Downloading from nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/maven-metadata.xml Uploading to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/json-to-xml-1.0.0-20190424.231519-1.jar Uploaded to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/json-to-xml-1.0.0-20190424.231519-1.jar (27 kB at 256 kB/s) Uploading to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/json-to-xml-1.0.0-20190424.231519-1.pom Uploaded to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/json-to-xml-1.0.0-20190424.231519-1.pom (2.8 kB at 60 kB/s) Downloading from nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/maven-metadata.xml Uploading to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/maven-metadata.xml Uploaded to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/1.0.0-SNAPSHOT/maven-metadata.xml (782 B at 18 kB/s) Uploading to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/maven-metadata.xml Uploaded to nexus-snapshots: http://moria:8081/repository/maven-snapshots/com/acme/json-to-xml/maven-metadata.xml (292 B at 8.6 kB/s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.767 s [INFO] Finished at: 2019-04-24T17:15:19-06:00 [INFO] ------------------------------------------------------------------------
Next, I'll change the version of my build artifact to a proper version number, 1.0.9, and try again. Afterward, I see the artifacts installed in ~/.m2/repository/com/acme/json-to-xml as well as Nexus:
tester@nargothrond:~/dev/json-to-xml$ mvn deploy . . . [INFO] --- maven-deploy-plugin:2.7:deploy (default-deploy) @ json-to-xml --- Uploading to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/1.0.9/json-to-xml-1.0.9.jar Uploaded to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/1.0.9/json-to-xml-1.0.9.jar (27 kB at 228 kB/s) Uploading to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/1.0.9/json-to-xml-1.0.9.pom Uploaded to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/1.0.9/json-to-xml-1.0.9.pom (2.8 kB at 68 kB/s) Downloading from nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/maven-metadata.xml Uploading to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/maven-metadata.xml Uploaded to nexus-releases: http://moria:8081/repository/maven-releases/com/acme/json-to-xml/maven-metadata.xml (312 B at 9.5 kB/s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.665 s [INFO] Finished at: 2019-04-24T17:18:02-06:00 [INFO] ------------------------------------------------------------------------
This is to underscore the narrative that Maven...
Note: As best practice, the first-time administrator should create a new user and assign it with the Administrator role (i.e.: not use user admin), then give user admin a real password (such that no one else can use it).
Note: It's also possible to use LDAP or other frameworks to manage users, roles, permissions and this is how things should be done. This is because, without that, you're reduced to putting usernames and password into exposed places like ~/.m2/settings.xml (see notes above on "Deploying artifacts to Nexus"). The notes in here, however, presume simple, native Nexus user and role management.
As it comes with the software, the Nexus admin user is powerful enough to deploy artifacts via a) the ReST interface, b) curl or c) Maven to Nexus.
Administrator users (including admin) can create users. These are typically external users. It's pretty obvious how to create a user and assign a password.
To endow the user with abilities, you need first to create named roles beyond the couple (nx-admin and nx-anonymous) Nexus offers. To do this:
Now edit the user you wish to endow with abilities:
Maven reports that it can't find an artifact; you're trying to put the artifact up to Nexus (of course it's not there—yet). This is an awkward attempt at telling you you've misspelled the repository name which the message below can't find as maven-released (because it's maven-releases).
Failed to deploy artifacts: Could not find artifact lpg.runtime.java:lpg.runtime.java:jar:2.0.17.v201004271640 \ in maven-released (http://maven.acme.com:8081/repository/maven-released/)
Failed to deploy artifacts: Could not transfer artifact Failed to transfer file: Return code is: 401, ReasonPhrase: Unauthorized.
Etc.
After Googling this problem to death, it's pretty clear that the 401 is next to meaningless. Ultimately, whatever other problems I had (and that were not fixed by the exercise in creating a new user with roles) were put to rest. The real reason was the myriad, fantastic Nexus artifactory paths reported in blog posts, documentation and stackoverflow out there—all tragically wrong! The only way to compose a proper URL (for use under your pom.xml's <distributionManagement> paragraph is to...
As I've intimated here, that path was considerably different from all the suggestions in the help I read on the way to conquering this problem. Moral of the story: you can't trust what anyone says your real path is going to be. Mine was http://maven.acme.com:8081/repository/maven-releases/.
Also, it is imperative that the <servers> <id>s noted in ~/.m2/settings.xml and used in the pom.xml's <distributionManagement> paragraph match rigorously.
Here are the (temporary) pom.xml I'm using (because I'm issuing mvn deploy:deployFile directly from a script. (This is because it's for ant builds for which there's no pom.xml, and I need the artifacts in a usable place.) Here also is part of my settings.xml:
Yeah, literally groupId and artifactId of "nothing" and Maven doesn't complain or choke because it's not really trying to what a pom.xml would suggest. I'm running the script I've given here.* The only purpose of this file is to tell Maven—and I couldn't find any other way—what's in the <distributionManagement> paragraph.
* This script is set up to generate the temporary pom.xml automatically, though the URL inside must be modified to fit the instance.
<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>nothing</groupId> <artifactId>nothing</artifactId> <version>nothing</version> <description>Temporary pom.xml to make Maven work.</description> <distributionManagement> <repository> <id>maven-releases</id> <name>maven-releases</name> <url>http://maven.acme.com:8081/repository/maven-releases</url> <layout>default</layout> </repository> <snapshotRepository> <id>maven-snapshots</id> <name>maven-snapshots</name> <url>http://maven.acme.com:8081/repository/maven-snapshots/</url> <layout>default</layout> </snapshotRepository> </distributionManagement> </project>
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> <servers> <server> <id>maven-releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>maven-snapshots</id> <username>admin</username> <password>admin123</password> </server> <server> <id>maven-public</id> <username>admin</username> <password>admin123</password> </server> </servers> <!-- Mirror all external repositories in Nexus, i.e.: pull down everything to Nexus. --> <mirrors> <mirror> <id>nexus</id> <mirrorOf>*</mirrorOf> <url>http://maven.acme.com:8081/repository/maven-central/</url> </mirror> </mirrors> <profiles> <profile> <id>nexus</id> <!-- Enable snapshots for the built in Central repository to redirect --> <!-- all requests to Nexus via the mirror. --> <repositories> <repository> <id>central</id> <name>central</name> <url>http://maven.acme.com:8081/repository/maven-central/</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <!--make the profile active all the time --> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>