Jenkins插件开发hellworld
jenkins本身提供了一套插件的管理机制,这些插件允许可插拨形式存在。jenkins插件虽然能提供很多种插件,但还是不能满足我们持续集成的需要,所以需要定制一些插件来支撑整个持续集成平台的运行。
jenkins本身提供了一套插件的管理机制,这些插件允许可插拨形式存在。jenkins插件虽然能提供很多种插件,但还是不能满足我们持续集成的需要,所以需要定制一些插件来支撑整个持续集成平台的运行。
Jenkins运行周期:
1.checkout -check out出源码
2.Pre-build - 预编译
3.Build wrapper-准备构建的环境,设置环境变量等
4.Builder runs -执行构建,比如调用calling Ant, Make 等等
5.Recording - 记录输出,如测试结果
6.Notification - 通知成员
Jenkins提供的扩展点:
为了支撑插件能在各个生命周期中运行,jenkins提供了各种扩展点,我们主类必须要extends一个扩展点;针对现状,基本上只需要使用Notifier,与builder这两个扩展点;详细如下:
1.Builder:这个扩展点支撑的是构建这个阶段需要做的事情,包括prebuild postbuid、构建环境等步骤,例如我们更换slave机器的hosts插件
2.Notifier:包括通知、消息通知等情况,我们的sendnapolimessage是基于这种扩展点来开发的
具体扩展点说明请参考:Extension+points
插件开始手册:Plugin+tutorial
下面说一下开发流程:
开发环境需要安装maven和jdk环境。
1.maven配置
配置maven的settings.xml配置文件
<settings>
<pluginGroups>
<pluginGroup>org.jenkins-ci.tools</pluginGroup>
</pluginGroups>
<mirrors>
<mirror>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
<mirrorOf>m.g.o-public</mirrorOf>
</mirror>
</mirrors>
<profiles>
<!-- Give access to Jenkins plugins -->
<profile>
<id>jenkins</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- change this to false, if you don't like to have it on per default -->
</activation>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>
2.创建插件maven项目
运行命令:mvn -cpu hpi:create
该操作需要你输入一些参数,比如说groupid=cn.slimsmart.jenkins.plugin,artifactid=helloword。之后会创建一个新的插件模板便于开发者之后的开发工作。
创建完成使用mvn clean package试试是否可以打包完成。
注:我遇见javadoc: 错误 - java.lang.IllegalArgumentException错误,原因是在解析环境变量classpath时使用了%JAVA_HOME%导致,修改classpath不适用变量就解决了。
创建的项目结构如下:
pom.xml:Maven的构建配置文件
src/main/java:Java源文件目录
src/main/resources:插件Jelly/Grovy视图
src/main/webapps:插件的静态资源如images和html文件
生成的代码中包含一个HelloWorldBuilder模板。当用户配置项目并启用此构建器时{@link DescriptorImpl#newInstance(StaplerRequest)}会被调用创建一个{@link HelloWorldBuilder} 实例创建实例通过使用持久化到项目配置XML XStream,所以这样可以使用实例字段(如{@link #name})记住配置。执行构建时,{@link #perform}方法将被调用。
3.调试运行
增加远程调试
#window
set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
#linux
#export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
mvn hpi:run -Djetty.port=8090 -Dhpi.prefix=/jenkins
#hpi.prefix设置context path
可以在一运行的jenkins的web界面中由Manage Jenkins>Manage Plugins>Advanced上传插件。
设置及构建运行
4.实例代码
package cn.slimsmart.jenkins.plugin.helloword;
import hudson.Launcher;
import hudson.Extension;
import hudson.FilePath;
import hudson.util.FormValidation;
import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
import jenkins.tasks.SimpleBuildStep;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.QueryParameter;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* <p>
* 当用户配置项目并启用此构建器时{@link DescriptorImpl#newInstance(StaplerRequest)}会被调用创建一个{@link HelloWorldBuilder} 实例
* 创建实例通过使用持久化到项目配置XML XStream,所以这样可以使用实例字段(如{@link #name})记住配置。
* 执行构建时,{@link #perform}方法将被调用。
* </p>
*/
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
private final String name;
// config.jelly中的textbox字段必须与“DataBoundConstructor”中的参数名匹配
@DataBoundConstructor
public HelloWorldBuilder(String name) {
this.name = name;
}
/**
* 我们将在{@code config.jelly}中使用它。
*/
public String getName() {
return name;
}
/**
* 执行构建时,perform方法将被调用
* Build参数是描述了当前任务的一次构建,通过它可以访问到一些比较重要的模型对象如:project当前项目的对象、workspace构建的工作空间、Result当前构建步骤的结果。
* Launcher参数用于启动构建。
* BuildListener该接口用于检查构建过程的状态(开始、失败、成功..),通过它可以在构建过程中发送一些控制台信息给jenkins。
* perform方法的返回值告诉jenkins当前步骤是否成功,如果失败了jenkins将放弃后续的步骤。
*/
@Override
public void perform(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) {
// This is where you 'build' the project.
// Since this is a dummy, we just say 'hello world' and call that a build.
// This also shows how you can consult the global configuration of the builder
if (getDescriptor().getUseFrench())
listener.getLogger().println("Bonjour, "+name+"!");
else{
listener.getLogger().println("Hello, "+name+"!");
}
listener.getLogger().println("workspace="+workspace);
listener.getLogger().println("number="+build.getNumber());
listener.getLogger().println("url="+build.getUrl());
}
// Overridden for better type safety.
// If your plugin doesn't really define any property on Descriptor,
// you don't have to do this.
//插件描述类
@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl)super.getDescriptor();
}
/**
* Descriptor for {@link HelloWorldBuilder}. Used as a singleton.
* The class is marked as public so that it can be accessed from views.
*
* <p>
* See {@code src/main/resources/hudson/plugins/hello_world/HelloWorldBuilder/*.jelly}
* for the actual HTML fragment for the configuration screen.
* 用于配置屏幕的实际HTML片段
*/
@Extension // This indicates to Jenkins that this is an implementation of an extension point.
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
/**
* 要保留全局配置信息,只需将其存储在一个字段中并调用save
* 如果您不希望字段持久化,请使用{@code transient}。
* global.jelly中checkbox的useFrench一至,全局配置
*/
private boolean useFrench;
/**
* In order为了加载持久化的全局配置,你必须在构造函数中调用load()
*/
public DescriptorImpl() {
load();
}
/**
* Performs on-the-fly validation of the form field 'name'.
* 执行表单字段“名称”的即时验证。
* @param value
* This parameter receives the value that the user has typed.
* @return
* Indicates the outcome of the validation. This is sent to the browser.
* <p>
* Note that returning {@link FormValidation#error(String)} does not
* prevent the form from being saved. It just means that a message
* will be displayed to the user.
*/
public FormValidation doCheckName(@QueryParameter String value)
throws IOException, ServletException {
if (value.length() == 0)
return FormValidation.error("Please set a name");
if (value.length() < 4)
return FormValidation.warning("Isn't the name too short?");
return FormValidation.ok();
}
// 表示此构建器是否可用于各种项目类型
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
// Indicates that this builder can be used with all kinds of project types
return true;
}
/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "Say hello world";
}
@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
// To persist global configuration information,
// set that to properties and call save().
useFrench = formData.getBoolean("useFrench");
// ^Can also use req.bindJSON(this, formData);
// (easier when there are many fields; need set* methods for this, like setUseFrench)
save();
return super.configure(req,formData);
}
/**
* This method returns true if the global configuration says we should speak French.
*
* The method name is bit awkward because global.jelly calls this method to determine
* the initial state of the checkbox by the naming convention.
*/
public boolean getUseFrench() {
return useFrench;
}
}
}
该实例使用了jenkins的Builder作为扩展点,实现了BuildStep,通过内部类DescripotorImpl添加@Extension声明,告诉系统该内部类是作为BuildStepDescriptor的扩展出现。
这里基本完成了扩展点的后台代码部分,但是扩展过程中还需要对前端页面进行扩张,这时就需要建立一个pcakage放置该扩展类对应的视图。Jenkins使用了Jelly页面渲染技术,这是一个基于XML的服务端页面渲染引擎,其将基于Jelly的xml标签转换为对应的Html标签并输出到客户端。模型对象的信息通过Jexl表达式被传递到页面上(相当于Jsp的JSTL)。jelly文件以.jelly为后缀,在hudson中使用类全名的形式来查找模型类对应的jelly页面文件,如名为src/main/java/cn/slimsmart/jenkins/plugin/helloword/HelloWorldBuilder.java的类,其对应的页面文件应该存在于src/main/resources/cn/slimsmart/jenkins/plugin/helloword/HelloWorldBuilder目录的下。
视图有三种:
1.全局配置(global.jelly->系统管理-系统设置)
2.Job配置(config.jelly->每个Job而言需要的配置信息)
3.还有就是使用帮助(help-字段名).html
关于jelly学习参考:Basic+guide+to+Jelly+usage+in+Jenkins
参考文章:
1.jenkins插件开发
2.浅析 Jenkins 插件开发
3.Jenkins插件开发入门
更多推荐
所有评论(0)