In this article by Donald Simpson, the author of the book, Extending Jenkins, we will be “hands-on” as we try to become familiar with the tools and conventions that we need to know about when creating Jenkins plugins
(For more resources related to this topic, see here.)
An introduction to Maven
We will use Maven to build our plugin. If you are unfamiliar with Maven, don’t worry—the main point of Maven is that you don’t necessarily need to know a lot about Maven to use it and to get a lot from it!
For a build tool that’s quite unusual, you may well have expected yourself to be knee-deep in configuration files and code; however, Maven works quite well without these due to the core philosophy that is at its heart; it uses “convention over configuration”.
Maven works on the assumption that you and your project are following a set of standard, sensible conventions. These are nothing too weird or onerous, so if you are following this path then Maven should just know where everything is and what you would like to achieve and will help you get it up and running very quickly and easily.
One of these core assumptions is related to your project structure; specifically, if you are using a directory layout like this:
Default dir (relative to the project directory)
Given a project that adheres to this convention, Maven will automatically know how to build your code, how to test it, and how to package it all up nicely for you with no other configuration or intervention required, providing a lot of benefit for a very little cost.
This is great as long as your project sticks to the path that Maven expects. If you stray, things can get messy very quickly! This makes Maven great for new and well-structured projects, but requires a bit more work when introducing legacy projects that have their own ideas about locations and naming conventions.
Maven is a Java tool, and therefore, we need to have Java installed to use it. You should have Java on your system already if you’re running Jenkins locally, but if not, you can download a JDK for your platform from the following link—version 6.0 or later is required:
Once you’ve got the Java prerequisite sorted out, download Maven for your platform from the Apache site here:
Then follow the installation steps for your operating system from this page:
On all platforms, the main requirement is to ensure that you have a JAVA_HOME variable in your PATH variable, and that your PATH variable also contains the Maven bin directory from the download you extracted.
Once you are set up, you should get something roughly comparable to the following output when you run java –version and then mvn –version—I am also displaying the Java and Maven environment variables here for your information:
We now need to tell Maven about Jenkins; where it is, and how to build it. We do this by updating the settings.xml file in the m2 home directory with the XML provided in the Setting up Environment section of the Jenkins Plugin Tutorial page mentioned earlier:
Find your settings.xml file here for Linux or Mac:
For Windows, the file is available at:
Add the following text within the settings.xml file:
org.jenkins-ci.tools jenkins true repo.jenkins-ci.org http://repo.jenkins-ci.org/public/ repo.jenkins-ci.org http://repo.jenkins-ci.org/public/ repo.jenkins-ci.org http://repo.jenkins-ci.org/public/ m.g.o-public
It’s a good idea to make a new directory for each project. This keeps things clean and simple, rather than having multiple projects coexist in one folder. So now, use mkdir for creating a new folder for this project and then use cd to enter into it as follows:
mkdir jenkinspluginexample cd jenkinspluginexample
After this, we can start the build, which will create a skeleton plugin project for us:
mvn -U org.jenkins-ci.tools:maven-hpi-plugin:create
If you have any issues at this point, check these three common causes first:
Does mvn work in this directory? Check using mvn -version
Does Java work in this directory? Check using ‘java -version’
Do you have Internet connectivity? Check using ‘ping www.google.com’
If all goes well, you will be prompted to answer a couple of simple questions; Maven will want you to specify the values for groupId and artifactId of your plugin.
For groupId, the convention is to use your domain name in reverse followed by the project name, all in lower case and separated by dots. Given the donaldsimpson.co.uk domain name and the jenkinspluginexample project name, I would use this: uk.co.donaldsimpson.jenkinspluginexample
The artifactId attribute should be your project name, that is, jenkinspluginexample.
If you are going to have several components or services comprising the jenkinspluginexample project, you should append additional service names here, such as the following:
jenkinspluginexample-service jenkinspluginexample-web jenkinspluginexample-gui
The intention behind this approach is to ensure that when used in conjunction with the group ID, each part of your project will remain uniquely and readily identifiable.
The preceding screenshot is the result of the previous input and states that a version 1.0 Snapshot build has been created using the skeleton plugin, which has produced a very basic and the first plugin for us to examine.
Now take a look inside the newly created subdirectory, whose name should match your artifactId value.
On exploring this directory, we should now have a few generated examples of everything that is required to create the most basic plugin. These include the following:
- pom.xml: A new Maven POM file for our project that contains the information required for Maven to build, package, and distribute our example plugin
- src/main: This directory contains both a Java directory and a resources directory
- src/main/java: This directory contains the Hello World builder class that we will update later
- src/main/resources: This folder contains configuration and help files
Taking a good look around at the contents of this new folder will help you become familiar with the different files and structure used by Maven and Jenkins to develop, build, and distribute plugins. The layout follows the Maven conventions and is used for many other projects as well.
As you previously saw, our new project directory has its own pom.xml file, so we should be able to build it as a Maven project—let’s take a look and try it out!
Change directory to the location of your new pom.xml file and have a look at it—you will see the various goals available in here along with all of the details required to work with our project.
There is also a packaging declaration, as follows:
This tells Maven that you would like this project to be packaged into an hpi file—this is the standard file format for Jenkins plugins. Other packaging instructions typically include ZIP, JAR, WAR, and EAR.
Maven also assumes that you will want to perform a standard set of tasks with your project—these will usually include functions, or phases such as the following:
- validate: This validates that the project is correct and all necessary information is available.
- compile: This compiles the source code of the project.
- test: This tests the compiled source code using a suitable unit testing framework. The testing do not require the code be packaged or deployed
- package: This takes the compiled code and packages it in its distributable format, such as a JAR.
- integration-test: This processes and deploys the package, if necessary, into an environment where integration tests can be run.
- verify: This runs checks to verify that the package is valid and meets quality criteria.
- install: This installs the package into the local repository, for use as a dependency in other projects locally.
- deploy: This is done in an integration or release environment. This function copies the final package to the remote repository to share the package with other developers and projects.
- clean: This cleans up artifacts created by prior builds.
- site: This generates site documentation for this project.
This guide has more information on Maven phases and goals and how they are linked:
If we run the package goal now, Maven should run through all of the prerequisite steps and then produce an HPI file, which we can deploy to Jenkins by running the following:
This phase will download all the required dependencies using the information in the POM file. It will then compile the Java code and would also run tests (if any existed in the expected location—src/test).
Depending on your Internet connection, this may take some time, as Maven will perform an initial download for all the declared dependencies (and their dependencies!) that it doesn’t already have locally. In subsequent runs, things should be much quicker, as Maven will retain the downloaded resources in the .m2/repository cache folder, which is in your home directory next to settings.xml that we updated earlier.
On completion, you should now have a distributable .hpi file!
As you can see from the preceding image, the console output that is produced near the end explains that the code has been compiled into a Java Archive (.jar) file, the resources (the Jelly, config, and HTML files) have been included, and everything has, in my case, been packaged into a resultant archive named jenkinspluginexample.hpi, which now resides in the target/ directory.
In this article, we learned a few basic things about Maven and its installation. We also saw how we used Maven to generate Jenkins plugins.
Resources for Article:
- Best Practices [article]
- Integrating Elasticsearch With The Hadoop Ecosystem [article]
- Understanding TDD [article]