1. 程式人生 > >Jenkins外掛開發入門

Jenkins外掛開發入門

Jenkins外掛開發指南

環境變數

為了能開發外掛,開發環境需安裝Maven和JDK 6.0以上版本

配置maven的settings.xml配置檔案


<settings>
  <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>
</settings>

建立新的外掛

建立外掛之前需執行以下Maven命令:


1
mvn -cpu hpi:create
該操作需要你輸入一些引數,比如說groupid,artifactid。之後會建立一個新的外掛模板便於開發者之後的開發工作。確保你可以使用一下命令:


1
cd newly-create-directory
2
mvn package
設定Eclipse開發環境

1
mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes eclipse:eclipse
或者 使用m2eclipse外掛在Eclipse開啟即可

外掛目錄結構

pom.xml:Maven的構建配置檔案

src/main/java:Java原始檔目錄

src/main/resources:外掛Jelly/Grovy檢視

src/main/webapps:外掛的靜態資源如images和html檔案

外掛除錯

外掛開發中在使用一下命令對外掛進行除錯

Windows

1
set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
2
mvn hpi:run
Linux

1
$ export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
2
$ mvn hpi:run
改變埠

1
mvn hpi:run -Djetty.port=8090
設定comtext path

1
mvn hpi:run -Dhpi.prefix=/jenkins
外掛釋出

1
mvn package
原始碼分析
<b>import hudson.Launcher;
import hudson.Extension;
import hudson.util.FormValidation;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.AbstractProject;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
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;
 
/**
 * Sample {@link Builder}.
 *
 * <p>
 * When the user configures the project and enables this builder,
 * {@link DescriptorImpl#newInstance(StaplerRequest)} is invoked
 * and a new{@link HelloWorldBuilder} is created. The created
 * instance is persisted to the project configuration XML by using
 * XStream, so this allows you to use instance fields (like {@link #name})
 * to remember the configuration.
 *
 * <p>
 * When a build is performed, the {@link #perform(AbstractBuild, Launcher, BuildListener)}
 * method will be invoked.
 */
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;

        }

    }
}</b>

這裡主要使用了jenkins的Builder作為擴充套件點,通過內部類DescripotorImpl新增@Extension宣告,告訴系統該內部類是作為BuildStepDescriptor的擴展出現

這裡基本完成了擴充套件點的後臺程式碼部分,但是擴充套件過程中還需要對前端頁面進行擴張,這時就需要建立一個pcakage放置該擴充套件類對應的檢視

檢視有三種:1,全域性配置(global.jelly)2,Job配置(config.jeely),還有就是使用幫助(help-欄位名).html

global.jeely(對於外掛需要使用的全域性配置)


<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">

  <!--

    This Jelly script is used to produce the global configuration option.

    Jenkins uses a set of tag libraries to provide uniformity in forms.

    To determine where this tag is defined, first check the namespace URI,

    and then look under $JENKINS/views/. For example, <f:section> is defined

    in $JENKINS/views/lib/form/section.jelly.

 

    It's also often useful to just check other similar scripts to see what

    tags they use. Views are always organized according to its owner class,

    so it should be straightforward to find them.

  -->

  <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>
將外掛部署到Jenkins後實際效果如下圖(系統管理-系統設定)

 

config.jeely(正對每個Job而言需要的配置資訊)


<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">

  <!--

    This jelly script is used for per-project configuration.


    See global.jelly for a general discussion about jelly script.

  -->


  <!--

    Creates a text field that shows the value of the "name" property.

    When submitted, it will be passed to the corresponding constructor parameter.

  -->

  <f:entry title="Name" field="name">

    <f:textbox />

  </f:entry>

</j:jelly>
 部署到jenkins後的實際效果

 

這裡細心的人可能已經看出來了,config.jelly中定義的欄位實際就是擴充套件類中建構函式的引數,對於HelloWorldBuilder類而言自成了Builder父類,通過使用@DataBoundConstructor申明,當用戶在介面填寫配置資訊點選儲存後將自動初始化該類,同時會在對應的job的配置檔案中儲存相關資訊本機是在.jenkins\jobs\TestJob目錄下的config.xml檔案中點選檢視檔案,在publishers節點下即可看見與該外掛有關的資訊


<prebuilders>

    <org.wocloud.jenkins.manager.HelloWorldBuilder>

      <name>Hello!!!!!!!!!!</name>

    </org.wocloud.jenkins.manager.HelloWorldBuilder>

  </prebuilders>
每一次修改配置並儲存時都將修改該配置檔案。
在Job進行構建時,將會啟用HelloWorldBuilder類的perform方法,而該方法中就是你外掛真正開始完成工作的地方

public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener)

listener是此次構建工作的監聽器

通過該listener可以輸出內容資訊到前端jenkins頁面

使用build可以判斷當前構建工作的結果