1.        搭建开发环境



1.1      安装Java Development Kit (JDK)

设置环境变量 ‘JAVA_HOME’

jenkins修改default json后无法 jenkins plugin_Plugin


1.2     安装Apache Maven

1. 获取最新版本从http://maven.apache.org/download.cgi 然后解压缩到你想要安装到的目录下

2. 设置环境变量M2_HOME


jenkins修改default json后无法 jenkins plugin_eclipse_02


3. 设置环境变量M2 and set it to“%M2_HOME%\bin”

4. 添加 %M2%;到PATH环境变量

5. 打开  command prompt ,运行“mvn -version”  来确认是否安装成功. 


jenkins修改default json后无法 jenkins plugin_xml_03

6.对%USERPROFILE%\.m2\settings.xml(USERPROFILE为用户名路径如C:\Documents and Settings下的用户)文件添加以下内容:

如果.m2文件夹不存在,你需要自己创建

Copy/Paste 以下内容到settings.xml:

<settings>

  <localRepository>d:\SW\localRepository\</localRepository>

  <pluginGroups>
    <pluginGroup>org.jenkins-ci.tools</pluginGroup>
  </pluginGroups>

  <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>
  <mirrors>
    <mirror>
      <id>repo.jenkins-ci.org</id>
      <url>http://repo.jenkins-ci.org/public/</url>
      <mirrorOf>m.g.o-public</mirrorOf>
    </mirror>
  </mirrors> 
    
</settings>



1.3 安装Integrated Development Environment (IDE)

http://www.eclipse.org/downloads/



2.        基于Eclipse开发JenkinsPlugin



2.1      Maven Projects

每个Maven工程的核心是pom.xml,他包含所有构建这个项目的信息,这个文件通常会很大而且难懂,通常我们不需要完整的理解它

当你下载一个Maven项目是,它至少包含pom.xml和src文件夹


jenkins修改default json后无法 jenkins plugin_eclipse_04



关于Maven详细内容:

Build Lifecycle: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html POM Reference: http://maven.apache.org/pom.html



2.2      导入Maven到Eclipise

为了能在Eclipse中使用Maven 工程,我们首先需要能够将其导,但是Eclipse并不支持Maven,这里我们有2种办法

1.     安装Eclipse plugin à m2e (Maven 2 Eclipse)

2.     使用Jenkins wiki推荐的方法

这里我们使用第2种方法,步骤如下

 

1. 打开commandprompt 然后切换到pom.xml所在的路径

2. 使用以下命令生成Eclipse工程


mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes eclipse:eclipse




3. Import工程到eclipse中

jenkins修改default json后无法 jenkins plugin_ci_05

4. 设置系统变量M2_REPO


jenkins修改default json后无法 jenkins plugin_Jenkins_06



Java > Build Path > Classpath Variables

jenkins修改default json后无法 jenkins plugin_Plugin_07

5. 点击New 按钮,然后定义M2_REPO 变量,内容为软件库的下载路径(见1.2,步骤6)

jenkins修改default json后无法 jenkins plugin_eclipse_08


2.3      编译和运行plugin

在pom.xml所在的目录下,执行mvn package, 编译结果<plugin-name>.hpi将被输出在target 文件夹下.

Clean 工程: mvn clean package

2.4      使用Eclipse  调试plugin

运行以下2个命令,将开启Jenkins 调试模式,其允许Eclipse连接到remote debugging session


set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n




mvn hpi:run




我们需要在Eclipse 创建Remote Java Application


jenkins修改default json后无法 jenkins plugin_eclipse_09


jenkins修改default json后无法 jenkins plugin_eclipse_10


举例:

set MAVEN_OPTS=-Xdebug-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
set JENKINS_HOME=d:\sw\ci\Jenkins
mvn hpi:run

 



3.        创建Plugin



3.1      使用skeleton generator

最简单的办法是使用skeleton generator来创建,然后在进行扩展

http://plugin-generator.jenkins-ci.org/


jenkins修改default json后无法 jenkins plugin_ci_11


3.2      扩展点

必须确保plugin实现了一个扩展点

HelloWorldBuilder 类实现了Builder这个扩展点。因此在Build Step中我们可以找到它,如果实现SCM扩展点,在Build Triggers中可以找到

更多扩展点:
https://wiki.jenkins-ci.org/display/JENKINS/Extension+points


3.3  源码分析

public class HelloWorldBuilder extends Builder {                                                 
    private final String name;                                                                   
                                                                                                 
    // Fields in config.jelly must match the parameter names in the "DataBoundConstructor"       
    @DataBoundConstructor                                                                        
    public HelloWorldBuilder(String name) {                                                      
        this.name = name;                                                                        
    }                                                                                            
    /**                                                                                          
    * We'll use this from the <tt>config.jelly</tt>.                                             
    */                                                                                           
    public String getName() {                                                                    
        return name;                                                                             
    }                                                                                            
                                                                                                 
    @Override                                                                                    
    public boolean perform(AbstractBuild build, Launcher launcher, BuildListener 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+"!");                                    
                                                                                                 
        return true;                                                                             
    }                                                                                            
                                                                                                 
    // 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 <tt>src/main/resources/hudson/plugins/hello_world/HelloWorldBuilder/*.jelly</tt>       
    * for the actual HTML fragment for the configuration screen.                                 
    */                                                                                           
                                                                                                 
    @Extension // This indicates to Jenkins that this is an implementation of an extension point.
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {              
    /**                                                                                          
    * To persist global configuration information,                                               
    * simply store it in a field and call save().                                                
    *                                                                                            
    * <p>                                                                                        
    * If you don't want fields to be persisted, use <tt>transient</tt>.                          
    */                                                                                           
        private boolean useFrench;                                                               
    /**                                                                                          
    * 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.                      
    */                                                                                           
        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;                                                                    
        }                                                                                        
    }                                                                                            
}



 HelloWorldBuilder类中的构造函数使用@DataBoundConstructor来声明。构造函数要对变量进行赋值。

HelloWorldBuilder类中perform重载函数。构建的执行通过实现perform方法来进行自定义。每次执行编译时都会运行perform函数。它有三个参数:

Build参数是描述了当前任务的一次构建,通过它可以访问到一些比较重要的模型对象如:project当前项目的对象、workspace构建的工作空间、Result当前构建步骤的结果。

Launcher参数用于启动构建。

BuildListener该接口用于检查构建过程的状态(开始、失败、成功..),通过它可以在构建过程中发送一些控制台信息给jenkins。

perform方法的返回值告诉jenkins当前步骤是否成功,如果失败了jenkins将放弃后续的步骤。


3.4  视图配置文件

Jenkins使用了Jelly页面渲染技术,这是一个基于XML的服务端页面渲染引擎,其将基于Jelly的xml标签转换为对应的Html标签并输出到客户端。模型对象的信息通过Jexl表达式被传递到页面上(相当于Jsp的JSTL)。jelly文件以.jelly为后缀,在hudson中使用类全名的形式来查找模型类对应的jelly页面文件,如名为src/main/java/com/jysong/jenkins/HelloWorldBuilder.java的类,其对应的页面文件应该存在于src/main/resources/com/jysong/jenkins/HelloWorldBuilder目录的下。

此外hudson通过固定的命名方式来确定页面文件属于局部配置还是全局配置:config.jelly提供局部配置;global.jelly提供全局配置。config.jelly是具体的某个job的配置,global.jelly是指jenkins的系统配置。

视图有三种:1,全局配置(global.jelly)2,Job配置(config.jelly),还有就是使用帮助(help-字段名).html


1.全局配置详解

global.jelly为全局配置页面。

<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:section title="Hello World Builder">
   <f:entry title="French" field="useFrench"  description="Check if we should say hello in French">
   <f:checkbox />
   </f:entry>
</f:section>
</j:jelly>

其中title为标题,表示要显示的内容。field为将调用DescriptorImpl内部类的方法getUseFrench(),field域会将方法去掉get并且将第一个字母小写,找到相对应的方法。description将显示描述信息。f:checkbox为复选框控件。

在每次保存全局配置时,jenkins都会调用该descriptor对象,并调用其configure方法,可以实现该方法并提供自己的定制

在DescriptorImpl中的configure方法中,可以对全局配置进行操作。save方法用于将当前Descriptor所提供的配置持久化(通过get**方法),为了使save能正常工作,需要提供配置项的get方法。


2.局部配置详解

config.jelly 的内容将被包含在扩展功能的配置中,在HelloWorldBuilder文件夹下面的配置内容是:


jenkins修改default json后无法 jenkins plugin_Jenkins_12


其中entry 表示用于交互的html表单域,title将作为表单域label的值,在界面中要显示的内容。  field表示的是HelloWorldBuilder的构造函数中的参数。如果有多个field,就要有多个相对应的参数。textbox 表示简单的渲染一个输入文本,输入的值将赋给name。