Jenkins 插件开发

插件开发环境搭建

  • maven 3
  • jdk 6 +

修改 maven 配置文件 settings.xml


  
    org.jenkins-ci.tools
  

  
    
    
      jenkins
      
        true 
      
      
        
          repo.jenkins-ci.org
          https://repo.jenkins-ci.org/public/
        
      
      
        
          repo.jenkins-ci.org
          https://repo.jenkins-ci.org/public/
        
      
    
  
  
    
      repo.jenkins-ci.org
      https://repo.jenkins-ci.org/public/
      m.g.o-public
    
  

创建一个空的 plugins 工程

  • 默认为空工程
mvn archetype:generate -Dfilter=io.jenkins.archetypes:empty-plugin
  • hello_world 工程
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:global-shared-library (Uses the Jenkins Pipeline Unit mock library to test the usage of a Global Shared Library)
4: remote -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.)
5: remote -> io.jenkins.archetypes:scripted-pipeline (Uses the Jenkins Pipeline Unit mock library to test the logic inside a Pipeline script.)

# 4 HelloWorld

插件目录结构

jenkins_plugin_project.PNG
  • pom.xml:maven 使用这个文件来构建插件,所有的插件都是基于 Plugin Parent Pom
    
        org.jenkins-ci.plugins
        plugin
        3.43
        
    

intellij idea:直接在 ide 中导入 pom 文件就能导入。

调试插件

  • linux
export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
mvn hpi:run
  • windows
set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
mvn hpi:run

输入命令过后可以打开浏览器,输入:http://localhost:8080/jenkins, 就可以看见你的插件在 jenkins 中运行起来了,现在就可以开始进行调试了。

修改端口

mvn hpi:run -Djetty.port =8090

设置上下文路径

mvn hpi:run -Dhpi.prefix=/jenkins

打包发布

mvn package

该命令会在 target 目录创建出[插件名称].hpi 文件,其他用户可以直接将这个插件上传安装到 Jenkins 中使用(或者放到 $JENKINS_HOME/plugins 目录中)。

Jenkins 插件 Demo HelloWorld

在之前我们使用mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:创建插件目录时,Jenkins在我们的项目中生成了一个 HelloWorldBuilder 的插件,这是一个官方示例,下面分析一下这个插件的示例源码

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
    
}

首先创建一个类继承于 Builder,代表使用这个插件是一个构建插件(如果继承于 Scm,代表这个插件是一个源码插件,例如 GitSvn 插件),然后实现 SimpleBuildStep 接口。

在 Jenkins 的插件中,每一个插件类中都必须要有一个 ==Descriptor 内部静态类==,它代表一个类的 ’描述者‘,用于指明这是一个扩展点的实现,Jenkins 是通过这个描述者才能知道我们自己写的插件。

每一个 ‘描述者’ 静态类都需要被 @Extension注解,Jenkins 内部会扫描 @Extenstion 注解来知道注册了有哪些插件。

    @Symbol("greet")
    @Extension
    public static final class DescriptorImpl extends BuildStepDescriptor {

        public FormValidation doCheckName(@QueryParameter String value, @QueryParameter boolean useFrench)
                throws IOException, ServletException {
            if (value.length() == 0)
                return FormValidation.error("error");
            if (value.length() < 4)
                return FormValidation.warning("warning");
            if (!useFrench && value.matches(".*[éáàç].*")) {
                return FormValidation.warning("warning");
            }
            return FormValidation.ok();
        }

        @Override
        public boolean isApplicable(Class aClass) {
            return true;
        }

        @Override
        public String getDisplayName() {
            return "Say hello world";
        }

    }

Desciptor 类中有两个方法需要我们必须要进行重写

  • isApplicable
        @Override
        public boolean isApplicable(Class aClass) {
            return true;
        }

这个方法的返回值代表这个 Builder 在 Project 中是否可用,我们可以将我们的逻辑写在其中,例如判断一些参数,最后返回 true 或者 false 来决定这个 Builder 在此处是否可用。

  • getDisplayName
        @Override
        public String getDisplayName() {
            return "Say hello world";
        }

这个方法返回的是一个 String 类型的值,这个名称会用在 web 界面上显示的名称。

img

如果我们在插件中需要获取一些系统设置参数,我们可以在 Descriptor 中获取一个参数对应 Descriptor 中的一个属性,其中的 userFrench 属性是一个全局配置,可以在系统设置里面看到这个属性。

img
private boolean useFrench;
public DescriptorImpl() {
    //...
    load();
}

configure() 方法

在 Descirptor 构造函数中使用load()进行加载全局配置,然后我们就可以在插件中获取到配置信息。

 @Override
 public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
      useFrench = formData.getBoolean("useFrench");
      save();
      return super.configure(req,formData);
  }

当在全局配置修改属性后,需要在configure()方法中调用save()将全局配置信息持久化到xml,我们可以在workspace的插件名.xml中看到持久化的数据。

img

peform() 方法

在每个插件的 perform() 方法中,是 perform 真正开始执行的地方,我们如果要在插件中完成什么事,代码逻辑也是写在 perform 方法中,perform 方法参数中 build 代表当前构建。

  • workspace 代表当前工作目录,通过 workspace 可以获取到当前工作目录的信息,并可以做些操作,如 workspace.copyTo("/home")

  • launcher 代表启动进程,可以通过 launcher 执行一些命令,如launcher.launch().stdout(listener).cmds("pwd").start();

  • listener 代表一个监听器,可以将运行的内容信息通过 listener 输出到前台 console output

    @Override
    public void perform(Run run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
        if (useFrench) {
            listener.getLogger().println("Bonjour, " + name + "!");
        } else {
            listener.getLogger().println("Hello, " + name + "!");
        }
    }

如上面的代码所示,在 perform 方法中我们通过 listener 打印了一行信息。在 web 界面上的控制台可以看见如下内容。其中 Jeffrey 为系统设置中输入的内容。

jenkins_plugin_helloworld.PNG

用户自定义参数

在 Jenkins 插件中,如果我们需要一些自定义的参数信息,如构建时执行一些命令,命令的内容是由用户输入,这个时候需要一个变量来记录用户输入的信息。
所以在 HelloWorkdBuilder 中定义一个属性与用于输入的信息相对应,如上面的 name 属性。

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
    private final String name;
    ....
}
img

这个属性的值是在 job 的配置过程中输入,由 Jenkins 从 web 前端界面传递过来的值,我们还需要在HelloWorldBuilder的构造方法中进行参数的注入

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
    private final String name;
    
    @DataBoundConstructor
    public HelloWorldBuilder(String name) {
        this.name = name;
}

类似于 Spring 的依赖注入,在这里 Jenkins 要求进行参数注入的构造方法需要用 @DataBoundConstructor 注解标注,以便 Jenkins 可以找到这个构造函数,并且调用这个构造函数,将 web 界面上配置的参数传递进HelloWorldBuilder,这样就可以在 HelloWorldBuilder 中使用这个属性了。

到此,这个插件的后台代码就已经搞定了,现在给大家讲讲怎么样编写这个前端配置的视图。

Jenkins 中的视图

Jenkins 使用 Jelly 来编写视图,Jelly 是一种基于 Java 技术和 XML 的脚本编制和处理引擎。Jelly 的特点是有许多基于 JSTL (JSP 标准标记库,JSP Standard Tag Library)、Ant、Velocity 及其它众多工具的可执行标记。Jelly 还支持 Jexl(Java 表达式语言,Java Expression Language),Jexl 是 JSTL 表达式语言的扩展版本。Jenkins 的界面绘制就是通过 Jelly 实现的。
在 Jenkins 中的视图的类型有三种:

  • global.jelly:全局的配置视图


  
    
      
    
  

img
  • config.jelly:Job的配置视图


  
    
  

img

在定义一个属性时,使用标签代表这是一个属性,其中title是指在界面上显示的字段名,而field是指这个属性在 HelloWorldBuilder 中对应的属性名,Jenkins 通过这个名称来与 HelloWorldBuilder 中的属性相对应,从而使用 @DataBoundConstructor 标注的构造函数将这些变量注入到 HelloWorldBuilder 类中。

  • help-属性名.html:帮助视图 html 片段
Help file for fields are discovered through a file name convention. This file is help for the "name" field. You can have arbitrary HTML here. You can write this file as a Jelly script if you need a dynamic content (but if you do so, change the extension to .jelly).
img

这是 Jenkins 中的三种视图,上面也介绍了两个简单的控件 textbox 和 checkbox 的使用,更多的关于 Jelly 的视图使用可以查看jelly官网。

Jenkins 数据持久化

之前在web界面上输入了 name,这个信息在下一次构建的时候仍然存在,说明 Jenkins 中需要使用数据持久化来将我们配置的信息保存下来,而 Jenkins 使用文件来存储数据(所有数据都存储在 $JENKINS_HOME),有些数据,比如 console 输出,会作为文本文件存储;大多数的结构数据,如一个项目的配置或构建(build)记录信息则会通过 XStream 持久化为一个 xml 文件,如下图所示

img

而在需要信息的时候,Jenkins 又从 xml 文件中读取到相应的数据,返回给应用程序。

总结总结

在本文,主要介绍了 Jenkins 的简单使用,以及 Jenkins 的插件开发环境,以及 Jenkins 插件结构的一些介绍。本文主要还是做一个简单入门介绍,如果想要了解更多的关于 Jenkins 的东西,还是需要去看 Jenkins 的官方wiki, 上面有详细的关于每个扩展点已经 Jenkins 的 api 的使用介绍,同样,你也可以下载 Jenkins 的源码来查看内部的一些实现方式。在Github Jenkinci也有很多的关于 Jenkins 插件的源码,我们可以通过源码了解一些扩展点是怎样使用,参照别人的源码来写出自己的插件。

参考

https://www.jianshu.com/p/8c05b6191d2f

你可能感兴趣的:(Jenkins 插件开发)