天天看點

Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件

準備工作

因為

Jenkins

是基于

Java

開發的,插件開發首先需要安裝

JDK

Maven

Maven

安裝完成後,需要修改配置檔案,内容如下
<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>https://repo.jenkins-ci.org/public/</url>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>repo.jenkins-ci.org</id>
                    <url>https://repo.jenkins-ci.org/public/</url>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>
    <mirrors>
        <mirror>
            <id>repo.jenkins-ci.org</id>
            <url>https://repo.jenkins-ci.org/public/</url>
            <mirrorOf>m.g.o-public</mirrorOf>
        </mirror>
    </mirrors>
</settings>
           

插件工程初始化

1、建立插件

找一個目錄存放你想要方式

Jenkins

插件源碼的目錄,在此處打開指令行終端,鍵人如下指令,這條指令可以讓我們使用與

Jenkins

相關的原型項目中的一個并生成
mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
           
執行日志如下,根據提示依次輸入選擇原型,版本,輸出檔案名稱,版本号,确認
[INFO] Scanning for projects...
Downloading 。。。。。。
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.2.0:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.2.0:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.2.0:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[WARNING] No archetype found in remote catalog. Defaulting to internal catalog
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: local -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.)
2: local -> io.jenkins.archetypes:global-configuration-plugin (Skeleton of a Jenkins plugin with a POM and an example piece of global configuration.)
3: local -> io.jenkins.archetypes:global-shared-library (Uses the Jenkins Pipeline Unit mock library to test the usage of a Global Shared Library)
4: local -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.)
5: local -> io.jenkins.archetypes:scripted-pipeline (Uses the Jenkins Pipeline Unit mock library to test the logic inside a Pipeline script.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 4
Choose io.jenkins.archetypes:hello-world-plugin version:
1: 1.1
2: 1.2
3: 1.3
4: 1.4
5: 1.5
6: 1.6
7: 1.7
8: 1.8
9: 1.9
10: 1.10
Choose a number: 10: 10
Downloading 。。。。。
[INFO] Using property: groupId = unused
Define value for property 'artifactId': JenkinsPluginDemo
Define value for property 'version' 1.0-SNAPSHOT: : 1.0
[INFO] Using property: package = io.jenkins.plugins.sample
[INFO] Using property: hostOnJenkinsGitHub = true
Confirm properties configuration:
groupId: unused
artifactId: JenkinsPluginDemo
version: 1.0
package: io.jenkins.plugins.sample
hostOnJenkinsGitHub: true
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: hello-world-plugin:1.10
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: artifactId, Value: JenkinsPluginDemo
[INFO] Parameter: version, Value: 1.0
[INFO] Parameter: package, Value: io.jenkins.plugins.sample
[INFO] Parameter: packageInPathFormat, Value: io/jenkins/plugins/sample
[INFO] Parameter: version, Value: 1.0
[INFO] Parameter: package, Value: io.jenkins.plugins.sample
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: hostOnJenkinsGitHub, Value: true
[INFO] Parameter: artifactId, Value: JenkinsPluginDemo
[WARNING] Don't override file D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo
[WARNING] Don't override file D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo\src\main\resources\io\jenkins\plugins\sample
[WARNING] Don't override file D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo\src\main\java\io\jenkins\plugins\sample
[WARNING] Don't override file D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo\src\test\java\io\jenkins\plugins\sample
[INFO] Executing META-INF/archetype-post-generate.groovy post-generation script
[version:1.0, package:io.jenkins.plugins.sample, groupId:unused, hostOnJenkinsGitHub:true, artifactId:JenkinsPluginDemo]
[INFO] Project created from Archetype in dir: D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  16:53 min
[INFO] Finished at: 2021-10-14T15:23:42+08:00
[INFO] ------------------------------------------------------------------------
           
正常執行後,目前目錄下方會生成名稱為剛才輸入的

artifactId

的名稱相同的目錄,其中添加了工作插件的基本結構。

進入該目錄,執行如下

Maven

的指令
mvn verify
           
這一操作會校驗是否建構成功,如果需要的一些依賴包本地倉庫沒有,這一步還會下載下傳很多的依賴,然後周遊配置的建構周期,包括靜态分析(

FindBugs

)和測試,直到日志内容出現下面的資訊
[INFO] BugInstance size is 0
[INFO] Error size is 0
[INFO] No errors/warnings found
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:05 min
[INFO] Finished at: 2021-10-15T09:54:04+08:00
[INFO] ------------------------------------------------------------------------
           
使用

IDEA

打開這個原型工程,等待工程初始化,最後項目結構如下
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件

2、可能出現的問題

Ⅰ、提示遠端目錄沒有這個原型

[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.2.0:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.2.0:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.2.0:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[WARNING] No archetype found in remote catalog. Defaulting to internal catalog
[INFO] Your filter doesn't match any archetype, so try again with another value.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.091 s
[INFO] Finished at: 2021-10-14T14:25:17+08:00
[INFO] ------------------------------------------------------------------------
           
筆者的解決辦法為下載下傳

archetype-catalog.xml

檔案放到

Maven

本地倉庫的根目錄下方(非安裝目錄),點選連結下載下傳

Ⅱ、依賴下載下傳失敗

日志如下
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:24 h
[INFO] Finished at: 2021-10-14T16:59:06+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project JenkinsPluginDemo: Could not resolve dependencies for project io.jenkins.plugins:JenkinsPluginDemo:hpi:1.0-SNAPSHOT: Could not transfer artifact org.jenkins-ci.main:jenkins-war:war:2.277.1 from/to repo.jenkins-ci.org (https://repo.jenkins-ci.org/public/): GET request of: org/jenkins-ci/main/jenkins-war/2.277.1/jenkins-war-2.277.1.war from repo.jenkins-ci.org failed: Connection reset -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
           
部分依賴下載下傳可能特别緩慢,甚至如日志那樣逾時失敗,筆者沒有找到比較好的解決辦法,我直接将下載下傳不了的依賴連結複制下來,然後手動用下載下傳工具(如

IDM

)下載下傳下來後放在本地倉庫,然後重新建構。(整個過程持續了一個下午加一個晚上,早上到公司發現下載下傳又卡住了,可能一晚上都沒下吧,七七八八又花了一個多小時才下完)

建構并運作插件

Maven HPI Plugin

用于建構和打包

Jenkins

插件。它提供了一種便利的方式來運作一個已經包含了目前插件的

Jenkins

執行個體,在插件工程根目錄下輸入如下指令
mvn hpi:run
           
這将在

http://localhost:8080/jenkins/

建立一個

Jenkins

執行個體。等待以下控制台輸出如下内容
這一步也可以通過

IDEA

可視化視窗來做
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
這裡建立的

Jenkins

執行個體主目錄為目前插件工程根目錄下的

work/

目錄,也就是說後續運作産生的資料都會存放在這個地方
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
這裡我們建立一個自由風格的插件,命名為

HelloWorld

Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
找到

Build

配置選項,輸入你想輸入的任何内容
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
然後點選建構可以看到輸出日志如下
Started by user unknown or anonymous
Running as SYSTEM
Building in workspace D:\IDEA WorkSpace\JenkinsPluginsDemo\JenkinsPluginDemo\work\workspace\HelloWorld
Hello, 哈喽世界!
Finished: SUCCESS
           

嘗試擴充插件

接下來我們做兩個擴充點:
  • 不單單是在建構日志中記錄問候語中使用的名稱,還要有适當的資料結構。
  • 在建構中添加一個新頁面,用來顯示問候語中使用的名稱。

1、記錄名稱

我們必須存儲運作建構時使用的名稱,而不是使用配置中的名稱,因為配置随時可能會被更改。和

HelloWorldBuilder

同級目錄建立一個

HelloWorldAction

類,該類需要實作

Action

接口。

Action

下的實作類是

Jenkins

中可擴充性的基本建構塊,他們可以附加到許多模型對象上并被儲存起來,并可以添加到他們的

UI

中。
/**
 * @author PengHuAnZhi
 * @ProjectName JenkinsPluginDemo
 * @Description TODO
 * @time 2021/10/20 10:13
 */
public class HelloWorldAction implements Action {
    @Override
    public String getIconFileName() {
        return null;
    }

    @Override
    public String getDisplayName() {
        return null;
    }

    @Override
    public String getUrlName() {
        return null;
    }
}
           
因為我們需要存儲問候中使用的名稱,是以我們需要在這個

Action

中定義這個變量,并設定它的

Getter

和以它為參數的構造方法
/**
 * @author PengHuAnZhi
 * @ProjectName JenkinsPluginDemo
 * @Description TODO
 * @time 2021/10/20 10:13
 */
public class HelloWorldAction implements Action {
    private final String name;

    public HelloWorldAction(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
	(...)
}
           
現在我們需要在建構步驟執行的時候建立這個

Action

的一個執行個體。這就需要擴充

HelloWorldBuilder

類中的

perform

方法來吧我們建立的動作執行個體添加到正在運作的建構中
(...)
    @Override
    public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
        run.addAction(new HelloWorldAction(name)); 
        if (useFrench) {
            listener.getLogger().println("Bonjour, " + name + "!");
        } else {
            listener.getLogger().println("Hello, " + name + "!");
        }
    }
(...)
           
現在重新開機

Jenkins

(除了靜态資源的修改不需要重新開機,其他都需要重新開機才能生效),我們也可以通過檢視

work/jobs/JOBNAME/builds/BUILDNUMBER/

下方的

build.xml

檔案來确認:
(...)    
	<actions>
      <hudson.model.CauseAction>
        <causeBag class="linked-hash-map">
          <entry>
            <hudson.model.Cause_-UserIdCause/>
            <int>1</int>
          </entry>
        </causeBag>
      </hudson.model.CauseAction>
      <io.jenkins.plugins.sample.HelloWorldAction plugin="[email protected]">
        <name>哈喽世界</name>
      </io.jenkins.plugins.sample.HelloWorldAction>
    </actions>
(...)
           
第一個

CauseAction

表示的是建構原因(建構如何觸發),這種情況下,是匿名使用者開始的這次建構

第二個

HelloWorldAction

便是我們建立的

Action

,其中的

name

是我們建立建構的時候使用的名稱

2、添加一個顯示名稱的視圖

第一步我們将名稱存儲下來了,現在我們讓他在頁面上面顯示出來,也就是将我們正在存儲的建構可視化

首先,我們回到

HelloWorldAction

類,并定義圖示,标題和

URL

名稱,如下:
@Override
public String getIconFileName() {
    return "document.png";
}

@Override
public String getDisplayName() {
    return "Greeting";
}

@Override
public String getUrlName() {
    return "greeting";
}
           
其中:
  • document.png

    用于側面闆項目的圖示,這是一個

    Jenkins

    綁定的預定義圖示之一
  • Greeting

    用于側面闆項目的标簽
  • greeting

    用于此操作的

    URL

    片段
操作完畢後,重新開機

Jenkins

,就會發現在

http://JENKINS/job/JOBNAME/BUILDNUMBER/

頁面中出現一個新的

Greeting

小圖示
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
點開這個頁面會連結到

URL

http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/

,不過現在還沒有定義這個頁面,是以是一個

404

的頁面
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
是以接下來需要定義出現在這個

URL

上的頁面。為了在

Jenkins

建立這樣的視圖,通常使用Apache Commons Jelly,

Jelly

允許用

XML

定義

XML

XHTML

輸出,它支援如下功能
  • 支援條件和循環
  • 允許包含在其他地方定義的

    view fragments

  • 可用于定義可重用的

    UI

    元件
現在我們在

src/main/resources/io/jenkins/plugins/sample/

目錄下建立一個新的名為

HelloWorldAction

的目錄,如你所見,這個目錄是和

HelloWorldAction

對應的,它可以放置與其相關的資源。

我們可以觀察到

src/main/resources/io/jenkins/plugins/sample/HelloWorldAction/

目錄便是

HelloWorldBuilder

相關的資源目錄,其中

config.jelly

便是建構步驟配置表單,包含建構步驟配置的本地化的各種

config*.properties

檔案和為配置提供了 本地化的内聯幫助

help*.html

檔案
在剛在建立的目錄

HelloWorldAction

下建立名為

index.jelly

的檔案,這将會顯示在

http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/

連結下,添加如下内容
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler">
    <l:layout title="Greeting"> 
        <l:main-panel> 
            <h1> 
                Name: ${it.name} 
            </h1>
        </l:main-panel>
    </l:layout>
</j:jelly>
           
其中:
  • layout

    Jenkins

    核心中定義的可重用

    tag

    ,它提供了頁眉,側面闆,主要内容區域和頁腳的基本頁面布局。
  • 為了使名稱顯示在内容區域(非側面闆),我們需要将輸出包裹在

    main-panel

    标簽中。
  • main-panel

    标簽内部我們可以使用任何

    HTML

    标簽,并将它們用于輸出。
  • Name:${it.name}

    是一個

    JEXL

    表達式,引用視圖所屬的

    Java

    對象(類似

    Java

    中的

    this

    ),在本例中為

    HelloWorldAction

    執行個體。

    it.name

    等同于調用

    getName()

最後 重新開機

Jenkins

觀察
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
由于這個界面和對應建構的版本相關,如圖
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件
是以我們需要顯示對應版本的側面闆,為此我們首先需要擷取的就是對我們動作中響應建構的引用,然後在動作視圖中包含建構建構的側面視圖

fragment

,為了擷取

HelloWorldAction

所屬的建構(

Run

)引用,我們需要改變現有的類,

Jenkins

為我們提供了新的接口

RunAction2

,這個接口相較于

Action

接口新增了兩個方法:
  • onAttached(Run):首次連接配接到建構回調方法
  • onLoad(Run):從磁盤加載操作和運作分别都會調用的方法
/**
 * @author PengHuAnZhi
 * @ProjectName JenkinsPluginDemo
 * @Description TODO
 * @time 2021/10/20 10:13
 */
public class HelloWorldAction implements RunAction2 {
    private final String name;
    private transient Run<?, ?> run;

    public HelloWorldAction(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String getIconFileName() {
        return "document.png";
    }

    @Override
    public String getDisplayName() {
        return "Greeting";
    }

    @Override
    public String getUrlName() {
        return "greeting";
    }

    @Override
    public void onAttached(Run<?, ?> run) {
        this.run = run;
    }

    @Override
    public void onLoad(Run<?, ?> run) {
        this.run = run;
    }

    public Run<?, ?> getRun() {
        return run;
    }
}
           
這些一旦完成之後,我們需要将擴充這個視圖來将

Run

的側面闆視圖檔段包含進來:
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler">
    <l:layout title="Greeting">
        <l:side-panel>
            <st:include page="sidepanel.jelly" it="${it.run}" optional="true" />
        </l:side-panel>
        <l:main-panel>
            <h1>
                Name: ${it.name}
            </h1>
        </l:main-panel>
    </l:layout>
</j:jelly>
           

main-panel

類似,我們希望内容僅在側面闆中顯示,是以我們需要将它們包裹在元素

side-panel

中。

includes

另一個對象

Run

的視圖檔段

sidepanel.jelly

。 我們把它标記為可選的,是以如果這個視圖檔斷不存在,就不會顯示錯誤,因為抽象類

Run

沒有定義這樣的視圖,隻有它的子類

AbstractBuild

至此,官方

Demo

擴充已經完成,重新開機檢查一下
Jenkins插件開發官方Demo準備工作插件工程初始化建構并運作插件嘗試擴充插件

參考1

參考2

參考3

參考4