Skip Navigation
Resources Blog How to publish Java artifacts to Sonatype Nexus Repository ...

How to publish Java artifacts to Sonatype Nexus Repository using Jenkins and Maven

In this article, we are going to explore how you can publish your Java artifacts (.ear, .jar, .war) to Sonatype Nexus Repository 3 using Jenkins and Maven.

For this, I have created a docker compose file which comes with Sonatype Nexus Repository and Jenkins. Let's take into consideration these assumptions and details about how the example works:

  • You have docker and docker-compose installed properly.
  • Jenkins will create the maven 2 (hosted) repository in Sonatype Nexus Repository on the startup script.
    • Any .groovy file you placed under /var/jenkins_home/init.groovy.d/*.groovy will be automatically executed. So you will find a job that is executed which is in charge to create the repository. This is a chance to review how to use Jenkins Rest API operations and Script operations in Sonatype Nexus Repository.
  • Jenkins already have defined these items in the configuration as code file:
    • nexus-push: Jenkins pipeline example which will build the Java artifact and push it to Jenkins
    • nexus-create-repo: Jenkins pipeline which will run every time Jenkins is started and will try to create the repository
    • Credential nexus-credentials to login to use the rest API and the Nexus Jenkins plugin to push artifacts
    • Maven tool to use it in the Pipeline, I called it Maven 3.6.0

Let's start

Clone the project:
git clone https://github.com/danielalejandrohc/jenkins-examples.git
cd jenkins-examples/nexus
view raw clone hosted with ❤ by GitHub
Start docker-compose application:
docker-compose up
And that's it! You are ready to explore Jenkins in port 8080 and run the job but let's take a minute to review what just happened.

What happened in Jenkins?

For Jenkins in this example I already prepared everything for you, so let's review what are the changes about and what you might consider If you want to implement it in your environment.

The Dockerfile I am using looks like this:

FROM jenkins/jenkins:2.204.1-jdk11
# This will skip the wizard UI configuration so you can use Jenkins straight.
ENV JAVA_OPTS=-Djenkins.install.runSetupWizard=false
# This will install the plugins from "plugins.txt"
COPY plugins.txt /usr/share/jenkins/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/plugins.txt
# This will copy the Configuration as Code file - init.groovy:/var/jenkins_home/init.groovy.d/init.groovy
# Notice that this is used in conjunction with the enviroment variable CASC_JENKINS_CONFIG defined in docker-compose file
COPY jcasc.yaml /var/jenkins_home/casc_configs/jcasc.yaml
# Copy startup groovy script
COPY init.groovy /var/jenkins_home/init.groovy.d/init.groovy
view raw Dockerfile hosted with ❤ by GitHub

The configuration as code yaml is below. This will create the credential, jobs required, and configured the maven tool. You can find where those details are in the comments of the file.

credentials:
system:
domainCredentials:
- credentials:
# Credential to use with Rest API operations and Nexus Jenkins plugin
- usernamePassword:
description: "Nexus credential"
id: "nexus-credentials"
password: "admin123"
scope: GLOBAL
username: "admin"
jenkins:
agentProtocols:
- "JNLP4-connect"
- "Ping"
disableRememberMe: false
markupFormatter: "plainText"
mode: NORMAL
myViewsTabBar: "standard"
numExecutors: 2
primaryView:
all:
name: "all"
projectNamingStrategy: "standard"
quietPeriod: 5
remotingSecurity:
enabled: false
scmCheckoutRetryCount: 0
slaveAgentPort: 50000
updateCenter:
sites:
- id: "default"
url: "https://updates.jenkins.io/update-center.json"
views:
- all:
name: "all"
viewsTabBar: "standard"
security:
apiToken:
creationOfLegacyTokenEnabled: false
tokenGenerationOnCreationEnabled: false
usageStatisticsEnabled: true
envInject:
enableLoadingFromMaster: false
enablePermissions: false
hideInjectedVars: false
globalJobDslSecurityConfiguration:
useScriptSecurity: true
sSHD:
port: -1
unclassified:
bitbucketEndpointConfiguration:
endpoints:
- bitbucketCloudEndpoint:
enableCache: false
manageHooks: false
repositoriesCacheDuration: 0
teamCacheDuration: 0
defaultFolderConfiguration:
healthMetrics:
- worstChildHealthMetric:
recursive: true
diskUsageProjectActionFactory:
checkWorkspaceOnSlave: false
historyLength: 183
showGraph: false
timeoutWorkspace: 5
gitHubPluginConfig:
hookUrl: "http://localhost:8080/github-webhook/"
gitSCM:
createAccountBasedOnEmail: false
showEntireCommitSummaryInChanges: false
useExistingAccountWithSameEmail: false
location:
adminAddress: "address not configured yet <nobody@nowhere>"
mailer:
charset: "UTF-8"
useSsl: false
mavenModuleSet:
localRepository: "default"
pollSCM:
pollingThreadCount: 10
timestamperConfig:
allPipelines: false
elapsedTimeFormat: "'<b>'HH:mm:ss.S'</b> '"
systemTimeFormat: "'<b>'HH:mm:ss'</b> '"
whitelist:
enabled: false
tool:
git:
installations:
- home: "git"
name: "Default"
# Tool defined to use it in the Jenkinsfile
maven:
installations:
- name: "Maven 3.6.0"
properties:
- installSource:
installers:
- maven:
id: "3.6.0"
pipelineMaven:
triggerDownstreamUponResultAborted: false
triggerDownstreamUponResultFailure: false
triggerDownstreamUponResultNotBuilt: false
triggerDownstreamUponResultSuccess: true
triggerDownstreamUponResultUnstable: false
jobs:
# Example pipeline to build and publish a java artifact
- script: >
pipelineJob('nexus-push') {
logRotator(5, 1)
definition {
cpsScm {
lightweight(true)
scm {
git {
remote {
url("https://github.com/danielalejandrohc/jenkins-examples.git")
branch("master")
}
}
}
scriptPath("nexus/uploadArtifacts.groovy")
}
}
}
# This is the reference to a groovy script in github which contains the startup script
- script: >
pipelineJob('nexus-create-repo') {
logRotator(5, 1)
definition {
cpsScm {
lightweight(true)
scm {
git {
remote {
url("https://github.com/danielalejandrohc/jenkins-examples.git")
branch("master")
}
}
}
scriptPath("nexus/createNexusRepository.groovy")
}
}
}
view raw jcasc.yaml hosted with ❤ by GitHub

Jenkins has a capability which allow you to run Groovy code whenever Jenkins is started. This what it does is to create a script (Did you guess what language? Yes, Groovy!). The script basically build an existing job which is defined in the Jenkins configuration as code file with name: nexus-create-repo.

You can check the code below:

pipeline {
agent {
label "master"
}
environment {
// This can be nexus3 or nexus2
NEXUS_VERSION = "nexus3"
// This can be http or https
NEXUS_PROTOCOL = "http"
// Where your Nexus is running. 'nexus-3' is defined in the docker-compose file
NEXUS_URL = "nexus-3:8081"
// Repository where we will upload the artifact
NEXUS_REPOSITORY = "repository-example"
// Jenkins credential id to authenticate to Nexus OSS
NEXUS_CREDENTIAL_ID = "nexus-credentials"
NEXUS_SCRIPT = "maven-create-hosted"
}
stages {
// You might get more details in these links:
// https://github.com/sonatype/nexus-public/blob/master/plugins/nexus-script-plugin/src/main/java/org/sonatype/nexus/script/plugin/RepositoryApi.java
stage("clone code") {
steps {
script {
// Get the script and check the want we want to create does not exists
response = httpRequest authentication: NEXUS_CREDENTIAL_ID, url: "${NEXUS_PROTOCOL}://${NEXUS_URL}/service/rest/v1/script";
echo "Response: ${response.content}"
jsonGetResponse = readJSON text: response.content;
findResult = jsonGetResponse.find{element -> element.name.trim().equals(NEXUS_SCRIPT)};
echo "Result of finding: ${findResult}"
if(findResult == null) {
echo "Creating script"
// Create it!
jsonPayload = "{ " +
" \"name\": \"${NEXUS_SCRIPT}\", " +
" \"type\": \"groovy\", " +
" \"content\":\"repository.createMavenHosted('${NEXUS_REPOSITORY}', 'default', true, org.sonatype.nexus.repository.maven.VersionPolicy.MIXED, org.sonatype.nexus.repository.storage.WritePolicy.ALLOW, org.sonatype.nexus.repository.maven.LayoutPolicy.PERMISSIVE)\" " +
"}";
httpRequest authentication: NEXUS_CREDENTIAL_ID,
url: "${NEXUS_PROTOCOL}://${NEXUS_URL}/service/rest/v1/script",
contentType: 'APPLICATION_JSON',
httpMode: 'POST',
requestBody: jsonPayload;
echo "Using payload ${jsonPayload}"
// Invoke it!
httpRequest authentication: NEXUS_CREDENTIAL_ID,
contentType: 'TEXT_PLAIN',
url: "${NEXUS_PROTOCOL}://${NEXUS_URL}/service/rest/v1/script/${NEXUS_SCRIPT}/run",
httpMode: 'POST';
}
}
}
}
}
}

Let's try it out!

So easy as going here:
http://localhost:8080/job/nexus-push 
and build it! Enjoy the logs 😀😀

The code that is executed is below:

pipeline {
agent {
label "master"
}
tools {
// Note: This should match with the tool name configured in your jenkins instance (JENKINS_URL/configureTools/)
maven "Maven 3.6.0"
}
environment {
// This can be nexus3 or nexus2
NEXUS_VERSION = "nexus3"
// This can be http or https
NEXUS_PROTOCOL = "http"
// Where your Nexus is running. 'nexus-3' is defined in the docker-compose file
NEXUS_URL = "nexus-3:8081"
// Repository where we will upload the artifact
NEXUS_REPOSITORY = "repository-example"
// Jenkins credential id to authenticate to Nexus OSS
NEXUS_CREDENTIAL_ID = "nexus-credentials"
}
stages {
stage("clone code") {
steps {
script {
// Let's clone the source
git 'https://github.com/danielalejandrohc/cargotracker.git';
}
}
}
stage("mvn build") {
steps {
script {
// If you are using Windows then you should use "bat" step
// Since unit testing is out of the scope we skip them
sh "mvn package -DskipTests=true"
}
}
}
stage("publish to nexus") {
steps {
script {
// Read POM xml file using 'readMavenPom' step , this step 'readMavenPom' is included in: https://plugins.jenkins.io/pipeline-utility-steps
pom = readMavenPom file: "pom.xml";
// Find built artifact under target folder
filesByGlob = findFiles(glob: "target/*.${pom.packaging}");
// Print some info from the artifact found
echo "${filesByGlob[0].name} ${filesByGlob[0].path} ${filesByGlob[0].directory} ${filesByGlob[0].length} ${filesByGlob[0].lastModified}"
// Extract the path from the File found
artifactPath = filesByGlob[0].path;
// Assign to a boolean response verifying If the artifact name exists
artifactExists = fileExists artifactPath;
if(artifactExists) {
echo "*** File: ${artifactPath}, group: ${pom.groupId}, packaging: ${pom.packaging}, version ${pom.version}";
nexusArtifactUploader(
nexusVersion: NEXUS_VERSION,
protocol: NEXUS_PROTOCOL,
nexusUrl: NEXUS_URL,
groupId: pom.groupId,
version: pom.version,
repository: NEXUS_REPOSITORY,
credentialsId: NEXUS_CREDENTIAL_ID,
artifacts: [
// Artifact generated such as .jar, .ear and .war files.
[artifactId: pom.artifactId,
classifier: '',
file: artifactPath,
type: pom.packaging],
// Lets upload the pom.xml file for additional information for Transitive dependencies
[artifactId: pom.artifactId,
classifier: '',
file: "pom.xml",
type: "pom"]
]
);
} else {
error "*** File: ${artifactPath}, could not be found";
}
}
}
}
}
}

 
Download the code

https://github.com/danielalejandrohc/jenkins-examples/tree/master/nexus

Optional: How to manually create Sonatype Nexus Repository

This is only required If instead of pushing the artifacts to a repository other than the one we have used in this article which is repository-example. So here we detail the steps to create a repository called maven-repo.

Note: default login for admin user in Sonatype Nexus Repository is below. Please take in consideration the newer versions you need to extract the credentials from the container. That's why I picked this version because the default login works and we can make this article straight to the point.

Username: admin password: admin123

[ Editor's Note: The repository manager installation includes an administrative user with full access. Its username is  admin  and the initial password can be found in an  admin.password  file in the  $data-dir   directory. You can sign in with the button on the top right corner of the user interface.

Next steps after successfully accessing the user interface are detailed in User Interface, Configuration and setting up whatever Formats you will use. Read more here.]
  • Go to http://localhost:8081/#admin/repository/repositories

  • Click on Create repository

  • Select maven2 (hosted)

  • Fill the form with these values:

    • Name: maven-repo
    • Layout policy: Strict
    • Storage: default
    • Deployment policy: Allow redeploy

     

    Basically the only field that we have changed from the default value is Deployment policy since is a test environment you might want to redeploy the same artifact so many times as you want. Maybe that's the way your organization wants it, though I would not recommend this approach.

Picture of Daniel Hernández

Written by Daniel Hernández

Daniel Hernández is a Software Engineer working in CI/CD solutions and Java development.