Here’s the challenge. I have a private Nexus repository that I want to set up a formal approach to publishing artifacts to, especially release artifacts. The place I am working at prefers a product called Codeship to handle builds. All my code is in maven projects so the obvious approach is to get Codeship to run the maven-release-plugin. I use the maven-release-plugin manually and it does a good job (though I see some people hate it). But you get a predictable result and that result is the one I want.
Specifically I want it to:
- Compile my code.
- Run my tests.
- Adjust the version numbers sensibly, a version number 1.2.3-SNAPSHOT should result in a release version of 1.2.3 and a new version 1.2.4-SNAPSHOT.
- The new versions should be committed to the master branch and the code actually released should be tagged with the release version.
- Copy the resulting artifacts to Nexus repository.
Obviously I want all this to be as secure as I can get it. That means minimizing hard coded passwords in files etc.
This is basically what I get with maven-release-plugin, although there are some traps to avoid. Make sure you are using the right version because there are some bugs in some of the not-so-old ones. It worked for me on version 2.5.3 when using maven 3.2.3 and git 2.11.0.
The demo project for this is at: https://github.com/RogerParkinson/test-repo In that you will find the pom.xml file has distributionManagement, scm and issueManagement tags like this:
<distributionManagement> <snapshotRepository> <id> snapshots</id> <url>http://nexus-server:8081/nexus/content/repositories/efs-snapshots</url> </snapshotRepository> <repository> <id> releases</id> <url>http://nexus-server:8081/nexus/content/repositories/efs-releases</url> </repository> </distributionManagement> <scm> <connection>scm:svn:https://github.com/RogerParkinson/test-repo</connection> <developerConnection>scm:git:https://github.com/RogerParkinson/test-repo</developerConnection> <tag>HEAD</tag> </scm> <issueManagement> <system>GitHub</system> <url>https://github.com/RogerParkinson/test-repo/issues</url> </issueManagement>
I also set up a special property in the properties section:
Maven expects to find a file ~/.m2/settings/xml that contains repositories and server credentials. I left a copy of the one I am using in sample-settings.xml. This is actually a template because the key information such as user names and passwords are just tokens I replace before the build starts.
This is the servers section:
<servers> <server> <id>plugins</id> <username>deployment</username> <password>NEXUSPWD</password> </server> <server> <id>releases</id> <username>deployment</username> <password>NEXUSPWD</password> </server> <server> <id>snapshots</id> <username>deployment</username> <password>NEXUSPWD</password> </server> <server> <id>my-scm-server</id> <username>GITUSER</username> <password>GITPWD</password> </server> </servers>
Notice the last entry for my-scm-server. Recall that we assigned the value my-scm-server to the property project.scm.id earlier. Maven knows this is the git repo so the username and password in that entry are the credentials for that. This allows maven to commit the updated version number etc.
With all this in place we can take a look at the Codeship project. Codeship offers a free plan and a pro plan. This only needs the free plan. You sign up for a Codeship account and create a project, giving the link to your git repo. Now Codeship will monitor push operations to that repo and launch a build when ever one happens. First you configure the setup commands and the test pipelines. I don’t use the test pipeline because my maven build runs unit tests in the setup commands. My setup commands look like this:
wget $settingsurl sed -i -e "s/NEXUSURL/$nexusurl/g" settings.xml sed -i -e "s/NEXUSPWD/$nexuspwd/g" settings.xml sed -i -e "s/GITUSER/$CI_COMMITTER_USERNAME/g" settings.xml sed -i -e "s/GITPWD/$gitpassword/g" settings.xml mvn --settings ./settings.xml clean deploy
What this does is pull the settings.xml template from some accessible location then it edits it using environment variables. This ensures you don’t have user names and passwords in your (possibly public) settings.xml template. Once the edit is complete it runs the maven clean deploy using the edited settings file.
The clean deploy will run the build and unit tests, then post the build jar file to the snapshots section of the nexus repository. This happens every time there is a push to the repository.
The deployment section gives you a lot of deployment options and we want the last one: Custom Script. We tie the deployment to a branch named production because we only want this section to run when there is a push to the git branch named production. This is the script:
git config user.name $CI_COMMITTER_USERNAME git config user.email $CI_COMMITTER_EMAIL git checkout -b master git pull origin master git checkout production git checkout master git merge production wget $settingsurl sed -i -e "s/NEXUSURL/$nexusurl/g" settings.xml sed -i -e "s/NEXUSPWD/$nexuspwd/g" settings.xml sed -i -e "s/GITUSER/$CI_COMMITTER_USERNAME/g" settings.xml sed -i -e "s/GITPWD/$gitpassword/g" settings.xml mvn -B --settings ./settings.xml release:clean release:prepare release:perform
There is a little bit of git management going on at the start. This ensures we have the master branch pulled down from the git repo and the production branch is merged with it. It is important that we run the build on the master branch not the production branch because whatever branch is used will get the new version number committed and pushed. If it is the production branch that will trigger another build and before you know it you will have done 80 or so releases while it cycles.
The build itself is similar to the setup. It pulls the settings template again and edits it. Then it runs the release sequence.
The release sequence compiles, runs the tests again and adjusts the version number. It posts the result to the release section of the maven repo and adjusts the version number in the master branch so that it is incremented from the release version but still has the SNAPSHOT suffix, ready for more development.
You have probably noticed that there are several environment variables needed here:
- gitpassword: the password to your git repo. We already have the user name.
- nexusurl: the url that points to your nexus repository. This one needs to have slashes escaped.
- nexuspwd: the password on your nexus repository for user name: deployment
- settingsurl: the url of your settings template
Now, whenever you push to the repo you will get a snapshot version posted to the nexus repo. If you push to the production branch you will also get a release generated and posted.