2019独角兽企业重金招聘Python工程师标准>>>
目前Hudson和Jenkins基本上就是同一个东西,Hudson的插件可以直接用于Jenkins。以下是参照Hudson wiki的extend plugin文档和实际操作。 一、软件 1. maven2 以上 2. JDK1.6 以上
maven和JDK都需要加入到环境变量中,IDE可以选eclipse
二、配置
-
修改~/.m2/settings.xml或者maven/config/settings.xml
org.eclipse.hudson.tools -
cmd或终端执行: mvn hpi:create 使用hudson hpi tool创建插件的主体结构,这过程中maven需要下载相关的插件, 同时需要你输入,groupId和artifactId这两个字段 groupId是项目唯一的标识符,实际对应java的包的结构,是main目录里的java的目录结构。 artifcatid 是项目唯一的标识符对应项目的名称,就是项目根目录的名称 如果执行过程中报错,请按顺序检查下面几项 a. 在~/.m2/settings.xml 文件中配置了pluginGroup org.eclipse.hudson.tools b. 在$M2_HOME/config/settings.xml中配置 pluginGroup org.eclipse.hudson.tools c. 本地 .m2/respoistory/org/eclipse/hudson/tools/maven-hpi-plugin 目录移除就版本 做完以上步骤如果还失败,可以执行 mv org.eclipse.hudson.tools:maven-hpi-plugin:create
三、代码目录结构 pom.xml Maven Pom文件用于构建我们的plugin src/main/java plugin的java源码目录 src/main/resources plugin jelly视图(界面)源码 src/main/webapp plugin的资源文件目录,例如图片和html文件
四、编译和执行plugin hudson plugin虽然是个迷你的maven项目,但它是完整的。它可以执行maven的所有功能。 mvn package 是构建项目命令。在第一次构建的时候会下载一堆依赖的包,目录在~/.m2/repository 此命令先构建项目,然后打包成Hudson的插件安装目录,目标文件在工程target目录下,.hpi结尾的jenkins插件和.jar结尾的jar包 mvn hpi:run 执行,同样会下载一堆依赖包(不能连外网的可以创建本地的maven仓库,具体参照网络上maven的文档) 将当前开发的plugin安装到Jetty容器中的Hudson, Hudson wiki的实例扩展的是build,所以当插件安装后,在任务的构建步骤中会看到你的插件。
五、Hudson 扩展点 项目构建任务一般由以下几步构成 1. SCM checkout 从代码库检查代码(CVS/SVN/git等) 2. Pre-build 构建开始前 3. Build wrapper 设置构建所需的环境 4. Builder runs 执行构建,例如执行Ant、Make、shell命令等 5. Recording 记录构建结果和过程中的输出信息、测试结果等 6. Notification 发送构建结果通知,默认是邮件的方式
Builder是负责构建的,Hudson提供的builder run step扩展点还是叫Builder。Hudson默认提供Ant和Maven这两个builders。
为了让Hudson识别一个class是扩展点,这个class必须从一个扩展点类继承,必须实现扩展需要的抽象方法,在Hudson中定义这个class是一个扩展类。
代码片段:
public class HelloWorkBuilder extends Builder{
HelloWorkBuilder从Builder类继承,Builder类是扩展点Builder的接口。Builder类本身是从BuildStep类继承的,BuildStep定义了perform抽象方法需要子类实现。
public boolean perform(AbstractBuild> ab,Launcher launcher,BuildListener bl)throws InterruptedException IOException;下面的方法告诉Hudson这个类实现了某个扩展点,必须加上@Extension的注解
@Extension // this marker indicates Hudson that this is an implementation of an extension point.
public static final class DescriptorImpl extends BuildStepDescriptor
1. Build 这个表示job开始执行,通Build也可以获取三个对象a. Project 这个Job对象b. WorkSpace 构建的工作区c. Result 构建步骤的执行结果
2. Launcher 用于启动这个任务的构建,也可以执行外部的可执行命令或程序
3. BuildListener 这是个接口,用于将构建步骤执行过程中的信息在Hudson控制台中显示。BuildListener.getLogger()方法从Build Listener 对象获取将信息输出到控制台的Logger
Hudson监控构建的执行和停止是通过下面两个方法。
listener.started(buildStepCause);
listener.finished(Result.SUCCESS);
Hudson这样设计的原因是1.Hudson需要将build构建过程信息输出到控制台2.当构建失败的时候Hudson必须停止后面构建的执行,并将此次构建标识为FALIED。这是通过BuildListener向Hudson发送build的状态消息实现的。int r;
r = launcher.launch().cmds(args).stdout(listener).join();
执行外部命令并将执行信息输出到控制台。
Launcher可以正确地启动Master或Slave节点上的任务执行。通过Launcher返回的状态码可以判断执行是否成功。Launcher执行的标准输出可以被listener获得。
六、输入扩展的配置信息 两种方式配置扩展数据: 1. plugin局部配置 2. Hudson全局配置
Hudson UI采用的是Jelly,Jelly是服务端的技术,通过解析将XML转换为客户端的HTML/Javascript和Ajax。Hudson内置了许多方便的Jelly tags。模型和tags属性通过表达式语言Jexl绑定。
当将tags解析为HTML和Javascript的时,代码中会包括模型对象的属性字段。通过Jelly tags 就可以避免书写大量的HTML和JS代码。Jelly文件已.jelly作为扩展名,保存在插件的resources目录中。Jelly文件的路径和model类包的名称要一致,这样才能保证Hudson将模型和UI匹配上。如果配置文件config.jelly就是plugin局部的配置。
如果配置文件global.jelly就是Hudson全局的配置。字段的帮助信息通过help-{fieldName}.html 这是个纯粹的HTML文件,fieldName要和jelly中字段名称对应。
UI和Model字段的绑定
model中必须要有和UI中对应字段的属性,例如
@DataBoundConstructor
public HelloWorldBuilder(String name) {this.name = name;
}
@DataBoundConstructor 这个注解将属性和UI的提交绑定,它的值只有通过页面上的字段修改。
对应需要给一个getter方法用于在页面展示
public String getName(){return name;
}
UI字段值的校验是通过doCheck+{nameOfTheField}作为Ajax URL的一部分。
public FormValidation doCheckName(&#64;QueryParameter String value) throws IOException, ServletException { if (value.length() &#61;&#61; 0) { return FormValidation.error("Please set a name"); } if (value.length() <4) { return FormValidation.warning("Isn&#39;t the name too short?"); } return FormValidation.ok(); } FormValidation.error()返回错误信息。 FormValidation.warning()返回警告信息。
七、Hudson结构 代码的根模块就是Hudson&#xff0c;其他子模块都是从这里开始的。Hudson所有的配置数据、构建记录都是以文件的形式存储在硬盘上的&#xff0c;默认的存储路径是$HUDSON_HOME如果没有添加这个环境变量&#xff0c;默认存储在当前用户主目录的.hudson目录下。控制台文件是以文本文件的格式存储&#xff0c;项目的配置文件和构建记录是以XML的格式存储&#xff0c;其他一些文件是以Java格式文件的形式存储的。
八、插件结构 plugin和jar的结构很相似
foo.hpi &#43;- META-INF | &#43;- MANIFEST.MF &#43;- WEB-INF | &#43;- classes | &#43;- lib &#43;- (static resources
插件的名称以.hpi结尾&#xff0c;名称要是独一无二的以便和其他plugin区别。这个结构也类似于war包&#xff0c;但没有web.xml文件。
MANIFEST.MF文件主要包括的是插件的版本和插件编译机器的一些信息。 WEB-INF/classes plugin的类文件和Jelly及Jelly用到tag文件 WEB-INF/lib 被plugin应用的.jar文件,CLassLoad加载的 static resources 存放HTML/CSS/JS/image等
九、插件开发新手常见问题 1. 如何执行shell命令 每个构建都是通过调用perform(Build,Launcher,BuildListener)方法执行的。在这个方法中&#xff0c;通过Launcher可以执行我们定义的 命令。Launcher提供了不同参数的launch方法&#xff0c;详细的参加API文档。推荐使用 launcher.launch(cmd,env,out,workDir) cmd: 要执行的命令是个字符串 env: 环境变量是字符串数组&#xff0c;每个元素是值键对("Varibale&#61;Value") out: 命令的输出&#xff0c;是OutputStream 输出流。 workDir: 工作区目录&#xff0c;FilePath
示例:launcher.launch("dir",new String[0],listener.getLogger(),build.getProject().getWorkspace());Hudson构建成功或失败是通过perform方法的返回值判断的。大部分值是命令执行的返回码或者抛出的异常。如果launch方法抛出一个异常&#xff0c;意味执行出错了将返回false构建失败。如果launch方法没有异常抛出&#xff0c;就要解析返回码&#xff0c;获取返回码的方式是。Proc proc &#61; launcher.launch("dir",build.getEnvVars(),listener.getLogger(),build.getProject().getWorkspace());int exitCode &#61; proc.join();完成的代码
public boolean perform(Build build, Launcher launcher, BuildListener listener) { try { Proc proc &#61; launcher.launch("dir", build.getEnvVars(), listener.getLogger(),build.getProject().getWorkspace()); int exitCode &#61; proc.join(); return exitCode &#61;&#61; 0; } catch (IOException e) { e.printStackTrace(); listener.getLogger().println("IOException !"); return false; } catch (InterruptedException e) { e.printStackTrace(); listener.getLogger().println("InterruptedException!"); return false; } 2. 如何获取图片路径&#xff0c;图片存放的目录是src/main/webapp public String getIconPath() { PluginWrapper wrapper &#61; Hudson.getInstance().getPluginManager().getPlugin([YOUR-PLUGIN-MAIN-CLASS].class); return Hudson.getInstance().getRootUrl() &#43; "plugin/"&#43; wrapper.getShortName()&#43;"/"; }
3. HTML中添加空格
十、开发Hudson CLI 命令 有两种方法在Hudson CLI中新增命令. 1. 通过在方法上加&#64;CLIMethod注解定义新命令&#xff0c;可以在注解中指定命令的名称。定义CLI.command-name.shortDescription作为命令的说明&#xff0c;通过CLICommand.getShortDescription()方法获取。 示例代码: public class AbstractItem { &#64;CLIMethod(name&#61;"delete-job") public synchronized void delete() throws IOException, InterruptedException { performDelete();
if(this instanceof TopLevelItem)Hudson.getInstance().deleteJob((TopLevelItem)this);Hudson.getInstance().rebuildDependencyGraph();
}
...
} 这是方法的具体实现&#xff0c;不能判断哪个job会被删除。通过定义CLI resolver&#xff0c;将它作为参数的一部分&#xff0c;传递给调用此方法的对象实例。 &#64;CLIResolver public static AbstractItem resolveForCLI( &#64;Argument(required&#61;true,metaVar&#61;"NAME",usage&#61;"Job name") String name) throws CmdLineException { AbstractItem item &#61; Hudson.getInstance().getItemByFullName(name, AbstractItem.class); if (item&#61;&#61;null) throw new CmdLineException(null,"No such job exists:"&#43;name); return item; } 2. 扩展现有的CLICommand命令&#xff0c;作为现有命令的子命令通过&#64;Extension注解。详细的参见javadoc of CLICommand