https://jenkins.io/doc/developer/
1. 向導
jenkins是一個強大的插件系統,開發者使用插件,幾乎可以影響到所有的jenkins操作。這個章節演示了簡單的插件功能。
1.1 准備插件的開發環境
安裝JDK環境,安裝maven,保證mvn路徑被添加到path變量。
1.2 創建插件
打開命令行,進入我們希望的目錄中,然后執行
mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:plugin -DarchetypeCatalog=remote
運行結果
… Choose archetype: 1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.) 2: remote -> io.jenkins.archetypes:global-configuration-plugin (Skeleton of a Jenkins plugin with a POM and an example piece of global configuration.) 3: remote -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.) Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 3 Choose io.jenkins.archetypes:hello-world-plugin version: 1: 1.1 2: 1.2 3: 1.3 Choose a number: 3: 3 [INFO] Using property: groupId = unused Define value for property artifactId: demo Define value for property version 1.0-SNAPSHOT: : [INFO] Using property: package = io.jenkins.plugins.sample Confirm properties configuration: groupId: unused artifactId: demo version: 1.0-SNAPSHOT package: io.jenkins.plugins.sample Y: : y
以上標出了關鍵的地方:
- 選擇hello-word-plugin作為我們的插件結構
- 選擇1.3最新版本
- artifactid是強制要求的,而且在jenkins中運行時要求唯一,我們設置為demo。
- 默認是 1.0-SNAPSHOT作為開發的版本號。(maven中的版本號區別可以查看: 這里 )
- 最后最列舉出來的信息進行確認Y
以上會創建一個目錄,名字與artifactid的值一致(這里的值是demo),而且里面有一些基本的可運行的插件結構。執行以下指令來校驗這個插件源碼是否可以運行:
$ mv demo demo-plugin $ cd demo-plugin $ mvn verify
這里第一步就是重命名這個文件夾,接着verify會下載一系列依賴,然后進入生命周期,包括FindBugs靜態分析和測試,直到顯示如下信息:
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 06:11 min [INFO] Finished at: 2017-03-02T14:14:34+01:00 [INFO] Final Memory: 73M/872M [INFO] ------------------------------------------------------------------------
1.3 構建和運行插件
maven的HPI插件用來構建和打包jenkins插件,這個插件提供了一個簡單的方式來運行插件:
mvn hpi:run
以上指令會運行一個jenkins實例(這個jenkins實例的數據保存在插件目錄的work目錄下),在 http://localhost:8080/jenkins/,然后自動打開一個控制台(這個控制台對應jenkins實例),控制台顯示
INFO: Jenkins is fully up and running
接着我們打開一個瀏覽器訪問http://localhost:8080/jenkins/,在jenkins中創建一個自由風格的項目,在構建設置中,啟用我們的插件“Say Hello Word”,在出現的選項中隨便輸入一個問候語“Hello Jenkins!”,然后保存,進行項目構建,打開這次構建的控制台,可以看到我們之前輸入的值,被輸出到控制台上了:
Started by user anonymous Building in workspace /Users/mrjenkins/demo/work/workspace/testjob Hello, Jenkins! Finished: SUCCESS
1.4 擴展插件
這次通過以下特征來擴展插件:
- 用合適的數據結構來保存我們的問候語,而不是僅僅在構建控制台中輸出
- 添加一個新的頁面來顯示我們用過的問候語
1.4.1 記錄問候語
首先,在HelloWorldBuilder所在的包中創建一個類HelloWorldAction:
package io.jenkins.plugins.sample; import hudson.model.Action; public class HelloWorldAction implements Action { @Override public String getIconFileName() { return null; } @Override public String getDisplayName() { return null; } @Override public String getUrlName() { return null; } }
Action都是jenkins的基礎構建擴展單位,這些單位可以訪問並且保存許多對象,而且還可以把這些對象顯示到界面上。
我們往這個Action類中添加我們希望保存的數據(私有字段和對應的geter、setter):
(...) public class HelloWorldAction implements Action { private String name; public HelloWorldAction(String name) { this.name = name; } public String getName() { return name; } (...)
現在,需要我們在構建的時候,生成這個類的實例,在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 + "!"); } } (...)
保存后,mvn hpi:run來運行插件。之后每次進行構建,上面的Action都會被應用。可以通過查看build.xml來確認,這個文件在這里work/jobs/JOBNAME/builds/BUILDNUMBER/。文件大致如下:
<build> <actions> <hudson.model.CauseAction> <causes> <hudson.model.Cause_-UserIdCause/> </causes> </hudson.model.CauseAction> <io.jenkins.plugins.sample.HelloWorldAction plugin="demo@1.0-SNAPSHOT"> <name>Jenkins</name> </io.jenkins.plugins.sample.HelloWorldAction> </actions> (...) </build>
對於以上三個標注的地方:
- 代表了構建的原因(如果觸發這次構建)也被作為action來保存了,這里指出了是匿名用戶觸發的構建
- 這是我們新建的Action
- 這個name值就是我們在構建配置中設置的值
1.4.2 添加個頁面來顯示數據
首先,回到HelloWorldAction中修改如下函數的返回值:
@Override public String getIconFileName() { return "document.png"; } @Override public String getDisplayName() { return "Greeting"; } @Override public String getUrlName() { return "greeting"; }
分別代表了多出的側邊欄的圖標、標題,以及地址(http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/)。
接着,以上地址的頁面需要被配置。在jenkins中創建視圖,通過Jelly來實現。
在src/main/resources/io/jenkins/plugins/sample/中創建一個目錄,名為HelloWorldAction。這個目錄對應我們的HelloWorldAction類,代表這個目錄中的資源給對應的類來使用。目錄中有如下資源:
- config.jelly:代表構建配置中的表單界面
- config*.properties:contain the localizations for the build step configuration
- help*.html:provide the localized inline help for the configuration
在以上目錄(src/main/resources/io/jenkins/plugins/sample/HelloWorldAction/)中創建一個 index.jelly,這個界面會顯示在http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/中。index.jelly內容如下:
<?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提供的一個基礎界面容器,包含了header、footer、主內容區域,側邊欄等
- main-panel標簽中的內容代表了容器中的主內容
- jelly中可以使用任意html標簽
- 這是一個JEXL表達式,it代表了這個視圖所屬的Java對象(類似於this),在這個例子中就是指向了HelloWorldAction實例,it.name等價於調用了這個屬性的getter,也就是調用了getName方法
做好以上配置后,當我們訪問http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/時,jelly以及里面的name數據就顯示出來了
1.4.3 為頁面添加側邊欄
以上的顯示的界面中沒有側邊工具欄,這里我們要做的就是把側邊工具欄顯示出來。要實現這個效果,
- 在Action中獲取到這次構建的引用
- 從這個引用中獲取到工具欄fragment
- 將這個fragment引用到我們的界面中
實現第一步,讓HelloWorldAction來實現RunAction2這個接口,里面有兩個方法:
- onAttached(Run):called when the run is first attached to a build
- onLoad(Run):called when the action and run are loaded from disk
(...) import hudson.model.Run; import jenkins.model.RunAction2; public class HelloWorldAction implements RunAction2 { private transient Run run; @Override public void onAttached(Run<?, ?> run) { this.run = run; } @Override public void onLoad(Run<?, ?> run) { this.run = run; } public Run getRun() { return run; } (...)
對於以上標注的兩個個地方:
- 被這個關鍵字修飾的run,不會隨着這個action序列化到硬盤上
- 使我們獲取到的run,可以用在jelly視圖中
然后在jelly中引入run提供的側邊工具欄:
(...) <l:layout title="Greeting"> <l:side-panel> <st:include page="sidepanel.jelly" it="${it.run}" optional="true" /> </l:side-panel> <l:main-panel> (...) </l:main-panel> </l:layout> (...)
類似於main-panel,side-panel代表了側標欄。接着include標簽,可以從這個標簽的it屬性所指向的對象中,引入page所指向的視圖,效果就是從run中引入了sidepanel.jelly視圖。然后將這個標簽設置為可選的,這樣一來,當sidepanel.jelly不存在時也不會報錯了
以上配置后,http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/就有側邊欄了。