準備工作
因為是基于
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
打開這個原型工程,等待工程初始化,最後項目結構如下

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
目錄,也就是說後續運作産生的資料都會存放在這個地方
work/
這裡我們建立一個自由風格的插件,命名為 HelloWorld
找到 Build
配置選項,輸入你想輸入的任何内容
然後點選建構可以看到輸出日志如下
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
點開這個頁面會連結到
URL
,不過現在還沒有定義這個頁面,是以是一個
http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/
的頁面
404
是以接下來需要定義出現在這個上的頁面。為了在
URL
建立這樣的視圖,通常使用Apache Commons Jelly,
Jenkins
允許用
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
由于這個界面和對應建構的版本相關,如圖
是以我們需要顯示對應版本的側面闆,為此我們首先需要擷取的就是對我們動作中響應建構的引用,然後在動作視圖中包含建構建構的側面視圖,為了擷取
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
參考1
參考2
參考3
參考4