Jenkins插件開發hellworld

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插件開發入門

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章