Jenkins插件開發入門

jopen 12年前發布 | 58K 次閱讀 Jenkins 項目構建

Jenkins插件開發指南

環境變量

為了能開發插件,開發環境需安裝MavenJDK 6.0以上版本

配置mavensettings.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命令:

mvn -cpu hpi:create

該操作需要你輸入一些參數,比如說groupid,artifactid。之后會創建一個新的插件模板便于開發者之后的開發工作。確保你可以使用一下命令:

cd newly-create-directory 
mvn package

設置Eclipse開發環境

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

或者 使用m2eclipse插件在Eclipse打開即可

插件目錄結構

pom.xmlMaven的構建配置文件

src/main/javaJava源文件目錄

src/main/resources:插件Jelly/Grovy視圖

src/main/webapps:插件的靜態資源如imageshtml文件

插件調試

插件開發中在使用一下命令對插件進行調試

Windows

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

Linux

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

改變端口

mvn hpi:run -Djetty.port=8090

設置comtext path

mvn hpi:run -Dhpi.prefix=/jenkins

插件發布

mvn package

源碼分析

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;
        }
    }
}

這里主要使用了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可以判斷當前構建工作的結果

 

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!