首发于 Jenkins 中文社区php
本文介绍了笔者首个 Jenkins 插件开发的旅程,包括从产生 idea 开始,而后通过插件定制开发,接着申请将代码托管到 jenkinsci GitHub 组织,最后将插件发布到 Jenkins 插件更新中心的过程。前端
鉴于文章篇幅过长,将分为上下两篇进行介绍。java
前几天和朋友聊天时,聊到了 Maven 版本管理领域的 SNAPSHOT 版本依赖问题,这给他带来了一些困扰,消灭掉历史遗留应用的 SNAPSHOT 版本依赖并不是易事。git
相似问题也曾经给笔者带来过困扰,在最初没能去规避问题,等到再想去解决问题时却发现困难重重,牵一发而动全身,致使这个问题一直被搁置,而这也给笔者留下深入的印象。github
等到再次制定 Maven 规范时,从一开始就考虑强制禁止 SNAPSHOT 版本依赖发到生产环境。web
这里是经过在 Jenkins 构建时作校验实现的。由于没有找到提供相似功能的 Jenkins 插件,目前这个校验经过 shell 脚原本实现的,具体的作法是在 Jenkins 任务中 Maven 构建以前增长一个 Execute shell 的步骤,来判断 pom.xml 中是否包含 SNAPSHOT 关键字,若是包含,该次构建状态将被标记为失败。脚本内容以下:shell
#!/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
复制代码
刚好前不久在看 Jenkins 插件开发文档,那何不经过 Jenkins 插件的方式实现它呢?浏览器
因而笔者开始了首个 Jenkins 插件开发之旅。bash
Jenkins 是由 Java 语言开发的最流行的 CI/CD 引擎。架构
提及 Jenkins 强大的开源生态,天然就会说到 Jenkins 插件。Jenkins 插件主要用来对 Jenkins 的功能进行扩展。目前 Jenkins 社区有上千个插件,用户能够根据本身的需求选择合适的插件来定制 Jenkins 。
插件开发须要首先安装 JDK 和 Maven,这里不作进一步说明。
Jenkins 为插件开发提供了 Maven 原型。打开一个命令行终端,切换到你想存放 Jenins 插件源代码的目录,运行以下命令:
mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
复制代码
这个命令容许你使用其中一个与 Jenkins 相关的原型生成项目。
$ 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, case sensitive contains): : 3
Choose io.jenkins.archetypes:hello-world-plugin version:
1: 1.1
2: 1.2
3: 1.3
4: 1.4
Choose a number: 4: 4
......
[INFO] Using property: groupId = unused
Define value for property 'artifactId': maven-snapshot-check
Define value for property 'version' 1.0-SNAPSHOT: :
[INFO] Using property: package = io.jenkins.plugins.sample
Confirm properties configuration:
groupId: unused
artifactId: maven-snapshot-check
version: 1.0-SNAPSHOT
package: io.jenkins.plugins.sample
Y: : Y
复制代码
笔者选择了 hello-world-plugin
这个原型,并在填写了一些参数,如artifactId、version 后生成了项目。 可使用 mvn verify
命令验证是否能够构建成功。
Maven HPI Plugin
用于构建和打包 Jenkins 插件。它提供了一种便利的方式来运行一个已经包含了当前插件的 Jenkins 实例:
mvn hpi:run
复制代码
这将安装一个 Jenkins 实例,能够经过http://localhost:8080/jenkins/
来访问。等待控制台输出以下内容,而后打开 Web 浏览器并查看插件的功能。
INFO: Jenkins is fully up and running
复制代码
在 Jenkins 中建立一个自由风格的任务,而后给它取个名字。而后添加 "Say hello world" 构建步骤,以下图所示:
输入一个名字,如:Jenkins ,而后保存该任务,点击构建,查看构建日志,输出以下所示:
Started by user anonymous
Building in workspace /Users/mrjenkins/demo/work/workspace/testjob
Hello, Jenkins!
Finished: SUCCESS
复制代码
Jenkins 插件开发归功于有一系列扩展点。开发人员能够对其进行扩展自定义实现一些功能。
这里有几个重要的概念须要作下说明:
扩展点是 Jenkins 系统某个方面的接口或抽象类。 这些接口定义了须要实现的方法,而 Jenkins 插件须要实现这些方法。
笔者所写的插件须要实现 Builder 这个扩展点。 代码片断以下:
public class MavenCheck extends Builder {}
复制代码
Descriptor 静态内部类是一个类的描述者,用于指明这是一个扩展点的实现,Jenkins 经过这个描述者才能知道咱们写的插件。每个描述者静态类都须要被 @Extension 注解,Jenkins 内部会扫描 @Extenstion 注解来获取注册了哪些插件。
代码片断以下:
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
public DescriptorImpl() {
load();
}
@Override
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return true;
}
@Override
public String getDisplayName() {
return "Maven SNAPSHOT Check";
}
}
复制代码
在 DesciptorImpl 实现类中有两个方法须要咱们必需要进行重写: isApplicable() 和 getDisplayName() 。isApplicable() 这个方法的返回值表明这个 Builder 在 Jenkins Project 中是否可用,咱们能够将咱们的逻辑写在其中,例如作一些参数校验,最后返回 true 或 false 来决定这个 Builder 是否可用。
getDisplayName() 这个方法返回的是一个 String 类型的值,这个名称被用来在 web 界面上显示。
前端页面的数据要和后台服务端进行交互,须要进行数据绑定。 前端 config.jelly
页面代码片断以下:
<?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>
复制代码
如上所示,须要在 config.jelly 中包含须要传入的参数配置信息的选择框,field 为 check ,这样能够在 Jenkins 进行配置,而后经过DataBoundConstructor 数据绑定的方式,将参数传递到 Java 代码中。服务端 Java 代码片断以下:
@DataBoundConstructor
public MavenCheck(boolean check) {
this.check = check;
}
复制代码
笔者所写的插件的核心逻辑是检查 Maven pom.xml 文件是否包含 SNAPSHOT 版本依赖。
Jenkins 是 Master/Agent 架构, 这就须要读取 Agent 节点的 workspace 的文件, 这是笔者在写插件时遇到的一个难点。
Jenkins 强大之处在于它的生态,目前有上千个插件, 笔者参考了 Text-finder Plugin 的源码, 并在参考处添加了相关注释,最终实现了插件要实现的功能。
详细代码能够查看 jenkinsci/maven-snapshot-check-plugin 代码仓库。
使用 mvn package
命令能够打包出后缀为 hpi 的二进制包, 这样就能够分发插件,将其安装到 Jenkins 实例。
如下是对插件的使用简要描述。
若是勾选了下面截图中的选择框, Jenkins 任务在构建时将会检查 pom.xml 中是否包含 SNAPSHOT 。
若是检查到的话,则会将该次构建状态标记为失败。
文章上篇主要介绍了从产生 idea 到插件开发完成的过程。 那么插件在开发完成后是如何将它托管到 Jenkins 插件更新中心让全部用户均可以看到的呢? 两天后的文章下篇将对这个过程进行介绍,敬请期待!
做者:王冬辉