First posted on Jenkins Chinese community

This paper introduces the author’s first Jenkins plug-in development journey, including the process of generating idea, then customizing the plug-in development, applying for hosting the code to Jenkinsci GitHub organization, and finally publishing the plug-in to Jenkins plug-in update Center.

In view of the length of the article is too long, it will be divided into two parts.

Start with an idea

A few days ago, I had a conversation with a friend about the SNAPSHOT version dependency problem in Maven’s version management domain. This caused some troubles for him. It is not easy to eliminate the SNAPSHOT version dependency of historical legacy applications.

Similar problems have also troubled the author. At the beginning, I failed to avoid the problem, but when I wanted to solve the problem, I found many difficulties. As a result, the problem has been put on hold, which also left a deep impression on the author.

When the Maven specification is rewritten, consider enforcing a ban on SNAPSHOT version dependencies to production from the outset.

This is done by doing validation during Jenkins builds. Since there is no Jenkins plugin that provides similar functionality, this verification is currently implemented through shell scripts by adding a Execute shell step before the Maven build in the Jenkins task. XML to determine whether the SNAPSHOT keyword is contained in pom.xml. If it is, the build status will be marked as failed. The script content is as follows:

#! /bin/bash
if [[ ` grep -R --include="pom.xml" SNAPSHOT .` =~ "SNAPSHOT"]].then echo "SNAPSHOT check failed" && grep -R --include="pom.xml" SNAPSHOT . && exit 1; 
else echo "SNAPSHOT check success"; 
fi
Copy the code

I happened to be reading the Jenkins plugin documentation, so why not implement it as a Jenkins plugin?

So I started my first Jenkins plug-in development trip.

Plug-in development process

Jenkins is the most popular CI/CD engine developed in the Java language.

Speaking of Jenkins’ strong open source ecosystem, the Jenkins plugin naturally comes up. The Jenkins plug-in is mainly used to extend the functions of Jenkins. There are currently thousands of plugins in the Jenkins community, and users can customize Jenkins according to their needs by choosing the right plugins.

Plug-in development preparation

Plug-in development requires the first installation of the JDK and Maven, which is not explained here.

Creating a plug-in

Jenkins provides Maven prototypes for plug-in development. Open a command line terminal, switch to the directory where you want to store the Jenins plug-in source code, and run the following command:

mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
Copy the code

This command allows you to use one of the Jenkins related prototype generation projects.

$ mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
......
Choose archetype:
1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.)
2: remote -> io.jenkins.archetypes:global-configuration-plugin (Skeleton of a Jenkins plugin with a POM and an example piece of global configuration.)
3: remote -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.)
Choose a number or apply filter (format: [groupId:]artifactId, caseSensitive: the contains) : 3 Choose IO. Jenkins. Archetypes: hello world - the plugin version: 1:1.1 2:3:1.2 1.3 4: 1.4 Choose a number: 4: 4...... [INFO] Using property: groupId = unused Define valuefor property 'artifactId': maven-snapshot-check
Define value for property 'version'1.0 - the SNAPSHOT: : [INFO] Using property: package = IO. Jenkins. Plugins. The sample Confirm the properties configuration: groupId: Unused artifactId: maven - the snapshot - check version: 1.0 the snapshot package: IO. Jenkins. Plugins. Sample: Y: YCopy the code

The author chose the prototype of Hello-world-plugin and created the project after filling in some parameters, such as artifactId and version. You can verify that the build was successful using the MVN verify command.

Build and run plug-ins

The Maven HPI Plugin is used to build and package Jenkins plug-ins. It provides a convenient way to run a Jenkins instance that already contains the current plug-in:

mvn hpi:run
Copy the code

This will install a Jenkins instance, can be access via http://localhost:8080/jenkins/. Wait for the console to output the following, then open your Web browser and view the plug-in’s capabilities.

INFO: Jenkins is fully up and running
Copy the code

Create a free-style task in Jenkins and give it a name. Then add the “Say Hello World “build step as shown below:

Enter a name like Jenkins, then save the task, click Build, and view the build log. Output looks like this:

Started by user anonymous
Building in workspace /Users/mrjenkins/demo/work/workspace/testjob
Hello, Jenkins! 
Finished: SUCCESS
Copy the code

Custom development plug-ins

Jenkins plug-ins are developed thanks to a series of extension points. Developers can extend and customize it to achieve some functions.

Here are a few important concepts that need to be explained:

Extension Point

Extension points are interfaces or abstract classes to an aspect of Jenkins’ system. These interfaces define the methods that need to be implemented, and the Jenkins plug-in needs to implement these methods.

The plug-in I wrote needs to implement the Extension point Builder. The code snippet is as follows:

public class MavenCheck extends Builder {}
Copy the code

Descriptor static inner class

The Descriptor static inner class is a Descriptor of a class that indicates that this is an implementation of an extension point, and Jenkins knows about the plug-in that we’re writing through this Descriptor. Each descriptor static class needs to be annotated by @extension, and Jenkins will internally scan the @Extenstion annotation to see which plug-ins are registered. The code snippet is as follows:

@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
    public DescriptorImpl(a) {
        load();
    }

    @Override
    public boolean isApplicable(Class<? extends AbstractProject> aClass) {
        return true;
    }

    @Override
    public String getDisplayName(a) {
        return "Maven SNAPSHOT Check"; }}Copy the code

There are two methods in the DesciptorImpl implementation class that we must override: isApplicable() and getDisplayName(). The return value of this method represents whether the Builder is available in Jenkins Project, we can write our logic in it, for example, do some parameter verification, Finally return true or false to determine whether the Builder is available.

The getDisplayName() method returns a String value that is used to display on the Web interface.

Data binding

Data binding is required for the front-end page data to interact with the backend server. The front-end config.jelly page code snippet is as follows:

<?jelly escape-by-default='true'? >
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  <f:entry title="check" field="check">
    <f:checkbox />
  </f:entry>
</j:jelly>
Copy the code

As shown above, you need to include a checkbox in config.jelly for the parameter configuration information to be passed in, with field as check, which can be configured in Jenkins and then via DataBoundConstructor data binding. Pass the parameters to the Java code. The server-side Java code snippet is as follows:

@DataBoundConstructor
public MavenCheck(boolean check) {
    this.check = check;
}
Copy the code

The core logic

The core logic of the plug-in I wrote is to check whether the Maven POM.xml file contains SNAPSHOT version dependencies.

Jenkins is a Master/Agent architecture, which requires reading the files of the workspace of the Agent node, which is a difficulty encountered by the author when writing the plug-in.

Jenkins’ strength lies in its ecology. At present, there are thousands of plug-ins. The author referred to the source code of Text-finder Plugin and added relevant notes in the reference section, and finally realized the functions to be realized by the plug-in.

See the Jenkinsci/Maven-snapshot-check-plugin repository for details.

Distribution of plug-in

The MVN package command can be used to package binary packages with the suffix HPI so that plug-ins can be distributed and installed into Jenkins instances.

Plug-in instructions

The following is a brief description of how to use the plug-in.

If the check box in the screenshot below is checked, the Jenkins task will check at build time if THE SNAPSHOT is included in pom.xml.

If it does, the build status will be marked as failed.

conclusion

The last part of the article mainly introduces the process from the generation of idea to the completion of plug-in development. How do you host your plugin to the Jenkins Plugin Update Center for all users to see once it’s developed? This process will be covered in the next post in two days, so stay tuned!

reference

  • Plugin tutorial
  • Preparing for Plugin Development
  • Create a Plugin
  • Build and Run the Plugin

Author: Wang Donghui