实战Jenkins插件开发 - Telegram通知插件

介绍

Jenkins的很多功能都是借助它的插件机制来实现的,它本身提供的功能是很少的。开发Jenkins插件是基于扩展点(ExtensionPoint)来完成的,它是Jenkins系统的某个方面的接口或抽象类,这些接口定义了需要实现的方法,而Jenkins插件需要实现这些方法。更多扩展点的详细信息,可以参考官方ExtensionPoint文档,通过这些扩展点我们可以写插件来实现自己的需求。
下面是一些常用的扩展点:

  • SCM:代表源码管理的一个步骤,如Git,CVS,SVN等就是扩展的SCM
  • Builder:代表构建的一个步骤,我们可以增加一个构建步骤,而每个选项都是对应一个Builder,在每一个Builder中都有自己不同的功能。
  • Publisher:代表构建后的一个步骤,比如通知,上传文件到目标服务器等,我们要开发的Telegram通知插件则是基于此扩展点实现。
  • Trigger:代表一个构建的触发操作,当满足什么样的条件才可以触发这个项目的构建。比较常用的触发就是代码变更时自动触发。

环境搭建

Jenkins是基于Java语言开发的,因此它的插件也应该基于Java来进行开发。它需要安装Java开发环境和Maven,至于如何搭建Java和Maven开发环境,网上有很多教程,这里不做介绍。

安装完成后,修改maven用户目录下的settings.xml文件(如果用户目录没有settings.xml文件,则从maven安装目录下的conf目录复制到用户目录,maven安装目录下的settings.xml是全局配置,针对所有用户,不建议更改

用户目录下settings.xml文件位置:

  • linux:~/.m2/settings.xml
  • windows:%USERPROFILE%.m2\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
    
  

创建插件

  1. 在需要创建插件的目录打开cmd窗口,运行一下命令
mvn -U archetype:generate -Dfilter="io.jenkins.archetypes:"

这个命令将允许你生成与Jenkins相关的多个项目原型之一,这里我们使用empty原型1.5版,因此选择如下:

$ 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.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1 
Choose io.jenkins.archetypes:hello-world-plugin version:
1: 1.0
2: 1.1
3: 1.2
4: 1.3
5: 1.4
6: 1.5
Choose a number: 6: 6 
…
[INFO] Using property: groupId = unused 
Define value for property 'artifactId': TelegramNotification
Define value for property 'version' 1.0-SNAPSHOT: : 1.0
[INFO] Using property: package = io.jenkins.plugins.sample
Confirm properties configuration:
groupId: unused
artifactId: TelegramNotification
version: 1.0
package: io.jenkins.plugins.sample
 Y: : y 
  1. 让我们确保可以构建插件,执行以下命令进行验证
    mvn verify
    验证结果如下,则说明验证通过。
    验证通过

如果使用的是其他原型的话,在测试用例这一步可能会失败,则可以考虑在pom.xml中增加以下配置忽略测试用例


                org.apache.maven.plugins
                maven-surefire-plugin
                
                    true
                
            

编写插件

  1. 创建一个TelegramPublisher实现Notifier类
public class TelegramPublisher extends Notifier {
}

在Jenkins的插件中,每一个插件类中都必须要有一个Descriptor内部静态类,它代表一个类的描述者,用于指明这个一个扩展点的实现,Jenkins是通过这个描述者才能知道我们自己写的插件。这个静态类需要被@Extension注解,Jenkins内部会扫描@Extension注解来知道注册了哪些插件。

public class TelegramPublisher extends Notifier {

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

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

        @Override
        public String getDisplayName() {
            return "Telegram Notification";
        }
    }
}

在Descriptor类中有两个方法一定需要我们进行重写

  • isApplicable方法:这个方法的返回值代表这个Publisher在项目中是否可用,我们可以将我们的逻辑写在其中,例如判断一些参数,最后返回true或者false来决定在项目中是否可用。
  • getDisplayName方法:这个方法返回的是一个String类型的值,这个名称会用在构建选项下拉框中显示的名称。
    构建操作显示名称
  1. 全局配置定义
    插件如果需要在全局配置中进行配置的话,我们需要在Descriptor中定义一个属性。这里我们需要把Telegram Bot Token和Telegram Bot Name保存到全局配置中,则可以在这里进行定义。
  • 定义botUserName和botToken(必须与global.jelly中的field相同,global.jelly是Jenkins中的全局配置视图,在后面进行详细介绍)
//全局配置中,进行Telegram Bot Name设置,必须与global.jelly中的field字段相同
        private String botUserName;
        //全局配置中,进行Telegram Bot Token设置,必须与global.jelly中的field字段相同
        private String botToken;

        public String getBotUserName() {
            return botUserName;
        }

        public String getBotToken() {
            return botToken;
        }
  • 在Descriptor构造函数中使用load()进行加载全局配置
public DescriptorImpl() {
            load();
        }
  • 在插件中获取配置信息并持久化到xml中
@Override
        public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
            botUserName = json.getString("botUserName"); //获取Telegram Bot Name配置
            botToken = json.getString("botToken");  //获取Telegram Bot Token配置
            save(); //将全局配置信息持久化到xml中
            return super.configure(req, json);
        }
  • 在global.jelly中定义全局配置视图,这部分后面讲视图的时候再来详细讲解。

  
    
      
    
    
          
     
  

注意:这里的field字段必须与Descriptor里面的属性保持一致,才能绑定上。

使用mvn hpi:run查看运行效果

全局配置

在Manage Jenkins -> Configure System里面,我们已经看到了相关设置了。

  1. 项目配置定义
    项目的相关配置,则是定义在插件类本身中,这里就是我们的TelegramPublisher类,其视图则是定义在config.jelly中,规则和global.jelly一样。
  • 获取全局配置信息
    我们定义一个方法来获取Descriptor类来获取全局配置信息,代码如下:
@Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }
  • 参数注入
// 项目配置,Telegram消息接收者
    private final String receivers;
    // 项目配置,额外消息
    private final String messageTemplate;
    // 项目配置,消息发送条件,构建成功是否发送消息
    private final Boolean condition;

    @DataBoundConstructor
    public TelegramPublisher(String receivers, String messageTemplate, Boolean condition) {
        this.receivers = receivers;
        this.messageTemplate = messageTemplate;
        this.condition = condition;
    }

    public String getMessageTemplate() {
        return messageTemplate;
    }

    public String getReceivers() {
        return receivers;
    }

    public Boolean getCondition() {
        return condition;
    }

这三个属性的值是项目配置过程中输入,由Jenkins从Web前端界面传递过来,通过在TelegramPublisher构造函数中进行参数的注入。这个类似于Spring的依赖注入,需要用@DataBoundConstructor注解标注。

  • 在config.jelly中定义项目视图


        
            
        
        
                
        
        
                
        

运行效果图如下:


项目配置
  • 插件业务逻辑
    在每个插件的perform()方法中,是真正开始执行的地方,我们如果要在插件中完成什么事,是在perform方法中实现,其中的方法参数build代表当前构建,launcher代表启动进程,listener代表一个监听器,可以将运行的内容信息通过listener输出到前台console。
@Override
    public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
        listener.getLogger().println("[INFO] test message" + getDescriptor().getBotUserName());
        return true;
    }

触发一次构建输出日志如下:


构建日志

Jenkins视图

Jenkins使用jelly来编写视图,Jelly是一种基于Java技术和XML的脚本编制和处理引擎。在Jenkins中的视图类型有三种

  • global.jelly 全局配置视图
  • config.jelly 项目配置视图
    标签用来标记一组属性集合。标签代表这是一个属性,其中title是指界面上显示的字段名,而field是属性在插件类中对应的属性名,description则是描述信息
  • help-属性名.html 帮助视图html片段
    帮助视图

这就是Jenkins中的三种视图,更多关于Jelly的视图控件可以查看Jenkins控件

另外

  1. Jenkins插件管理页面中,关于插件介绍的内容在index.jelly视图中进行更改
  2. 插件名称在pom.xml中更改name元素即可
插件介绍

插件名称

配置文件与多语言国际化

  • config.jelly视图和global.jelly视图
    在resources中创建对应的properties文件,比如config.properties对应的config.jelly视图的配置文件(这个是默认英文版本的),其他语言版本,使用config_语言编码.properties文件,比如简体中文,则使用config_zh_CN.properties配置文件,global.jelly视图类似。
    格式使用属性名=数据的方式,如下图所示:

    配置文件格式

    使用的时候,则遵照${%属性名}的格式
    配置使用

  • help帮助视图
    help帮助视图多语言,采用的是help-属性名_语言编码.html的方式

  • 插件类多语言
    创建Messages.properties配置的方式来实现,其他语言如上面两种情况一样,使用Messages_语言编码.properties的方式实现。

    Jenkins使用Localizer生成Messages类,能够以类型安全的方式访问Message资源。src/main/resources/**/Messages.properties匹配的所以文件都会生成一个对应的Messages类。如过IDE找不到这些类,需要手动将target/generated-sources/localizer目录加入源码的根目录。返回用于显示的字符串的代码(如Descriptor.getDisplayName())可以使用Messages类获取本地化的消息。在运行时,适当的locale会被自动选择。

典型的工作流如下:

  1. 确定需要本地化的Messages
  2. 将消息写入Messages.properties文件,即可以为每个package编写一个,也可以整个module或者plugin只用一个
  3. 运行mvn compile生成Messages.java
  4. 更新代码,使用最新生成的消息格式方法读取

代码中则可以通过如下的方式进行引用

@Override
        public String getDisplayName() {
            return Messages.TelegramPublisher_DescriptorImpl_DisplayName();
        }
2.png

国际化更多内容,请查看Jenkins官方文档

参数检查

Jenkins中进行字段检验,是通过创建doCheck字段名称()这样的方法来处理的,value参数,则代表该字段的值。

///校验botUserName参数
        public FormValidation doCheckBotUserName(@QueryParameter String value) {
            if (value.length() == 0)
                return FormValidation.error(Messages.TelegramPublisher_DescriptorImpl_errors_missingBotUserName());

            return FormValidation.ok();
        }

        ///校验botToken参数
        public FormValidation doCheckBotToken(@QueryParameter String value) {
            if (value.length() == 0)
                return FormValidation.error(Messages.TelegramPublisher_DescriptorImpl_errors_missingBotToken());

            return FormValidation.ok();
        }

这样的话,当我们在全局配置中,未填写botUserName和botToken就会收到相应提示了。


1.png

打包上传

使用指令mvn package进行打包,完成后会在target目录下生成hpi文件。

有两种方式上传插件到Jenkins

  1. 将hpi文件放到Jenkins下的plugins目录下
  2. 通过Jenkins的插件管理中心,进行手动上传hpi文件,具体操作步骤如下图所示:


    插件上传

本文对应的源代码,请移步Jenkins Telegram通知插件

你可能感兴趣的:(实战Jenkins插件开发 - Telegram通知插件)