首發於 Jenkins 中文社區php
本文介紹了筆者首個 Jenkins 插件開發的旅程,包括從產生 idea 開始,而後通過插件定製開發,接着申請將代碼託管到 jenkinsci GitHub 組織,最後將插件發佈到 Jenkins 插件更新中心的過程。前端
鑑於文章篇幅過長,將分爲上下兩篇進行介紹。java
前幾天和朋友聊天時,聊到了 Maven 版本管理領域的 SNAPSHOT 版本依賴問題,這給他帶來了一些困擾,消滅掉歷史遺留應用的 SNAPSHOT 版本依賴並不是易事。git
相似問題也曾經給筆者帶來過困擾,在最初沒能去規避問題,等到再想去解決問題時卻發現困難重重,牽一髮而動全身,致使這個問題一直被擱置,而這也給筆者留下深入的印象。github
等到再次制定 Maven 規範時,從一開始就考慮強制禁止 SNAPSHOT 版本依賴發到生產環境。web
這裏是經過在 Jenkins 構建時作校驗實現的。由於沒有找到提供相似功能的 Jenkins 插件,目前這個校驗經過 shell 腳原本實現的,具體的作法是在 Jenkins 任務中 Maven 構建以前增長一個 Execute shell 的步驟,來判斷 pom.xml 中是否包含 SNAPSHOT 關鍵字,若是包含,該次構建狀態將被標記爲失敗。腳本內容以下:shell
#!/bin/bash
if [[ ` grep -R --include="pom.xml" SNAPSHOT .` =~ "SNAPSHOT" ]];
then echo "SNAPSHOT check failed" && grep -R --include="pom.xml" SNAPSHOT . && exit 1;
else echo "SNAPSHOT check success";
fi
複製代碼
剛好前不久在看 Jenkins 插件開發文檔,那何不經過 Jenkins 插件的方式實現它呢?瀏覽器
因而筆者開始了首個 Jenkins 插件開發之旅。bash
Jenkins 是由 Java 語言開發的最流行的 CI/CD 引擎。架構
提及 Jenkins 強大的開源生態,天然就會說到 Jenkins 插件。Jenkins 插件主要用來對 Jenkins 的功能進行擴展。目前 Jenkins 社區有上千個插件,用戶能夠根據本身的需求選擇合適的插件來定製 Jenkins 。
插件開發須要首先安裝 JDK 和 Maven,這裏不作進一步說明。
Jenkins 爲插件開發提供了 Maven 原型。打開一個命令行終端,切換到你想存放 Jenins 插件源代碼的目錄,運行以下命令:
mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
複製代碼
這個命令容許你使用其中一個與 Jenkins 相關的原型生成項目。
$ mvn -U archetype:generate -Dfilter=io.jenkins.archetypes:
......
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
4: 1.4
Choose a number: 4: 4
......
[INFO] Using property: groupId = unused
Define value for property 'artifactId': maven-snapshot-check
Define value for property 'version' 1.0-SNAPSHOT: :
[INFO] Using property: package = io.jenkins.plugins.sample
Confirm properties configuration:
groupId: unused
artifactId: maven-snapshot-check
version: 1.0-SNAPSHOT
package: io.jenkins.plugins.sample
Y: : Y
複製代碼
筆者選擇了 hello-world-plugin
這個原型,並在填寫了一些參數,如artifactId、version 後生成了項目。 可使用 mvn verify
命令驗證是否能夠構建成功。
Maven HPI Plugin
用於構建和打包 Jenkins 插件。它提供了一種便利的方式來運行一個已經包含了當前插件的 Jenkins 實例:
mvn hpi:run
複製代碼
這將安裝一個 Jenkins 實例,能夠經過http://localhost:8080/jenkins/
來訪問。等待控制檯輸出以下內容,而後打開 Web 瀏覽器並查看插件的功能。
INFO: Jenkins is fully up and running
複製代碼
在 Jenkins 中建立一個自由風格的任務,而後給它取個名字。而後添加 "Say hello world" 構建步驟,以下圖所示:
輸入一個名字,如:Jenkins ,而後保存該任務,點擊構建,查看構建日誌,輸出以下所示:
Started by user anonymous
Building in workspace /Users/mrjenkins/demo/work/workspace/testjob
Hello, Jenkins!
Finished: SUCCESS
複製代碼
Jenkins 插件開發歸功於有一系列擴展點。開發人員能夠對其進行擴展自定義實現一些功能。
這裏有幾個重要的概念須要作下說明:
擴展點是 Jenkins 系統某個方面的接口或抽象類。 這些接口定義了須要實現的方法,而 Jenkins 插件須要實現這些方法。
筆者所寫的插件須要實現 Builder 這個擴展點。 代碼片斷以下:
public class MavenCheck extends Builder {}
複製代碼
Descriptor 靜態內部類是一個類的描述者,用於指明這是一個擴展點的實現,Jenkins 經過這個描述者才能知道咱們寫的插件。每個描述者靜態類都須要被 @Extension 註解,Jenkins 內部會掃描 @Extenstion 註解來獲取註冊了哪些插件。
代碼片斷以下:
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
public DescriptorImpl() {
load();
}
@Override
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return true;
}
@Override
public String getDisplayName() {
return "Maven SNAPSHOT Check";
}
}
複製代碼
在 DesciptorImpl 實現類中有兩個方法須要咱們必需要進行重寫: isApplicable() 和 getDisplayName() 。isApplicable() 這個方法的返回值表明這個 Builder 在 Jenkins Project 中是否可用,咱們能夠將咱們的邏輯寫在其中,例如作一些參數校驗,最後返回 true 或 false 來決定這個 Builder 是否可用。
getDisplayName() 這個方法返回的是一個 String 類型的值,這個名稱被用來在 web 界面上顯示。
前端頁面的數據要和後臺服務端進行交互,須要進行數據綁定。 前端 config.jelly
頁面代碼片斷以下:
<?jelly escape-by-default='true'?>
<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">
<f:entry title="check" field="check">
<f:checkbox />
</f:entry>
</j:jelly>
複製代碼
如上所示,須要在 config.jelly 中包含須要傳入的參數配置信息的選擇框,field 爲 check ,這樣能夠在 Jenkins 進行配置,而後經過DataBoundConstructor 數據綁定的方式,將參數傳遞到 Java 代碼中。服務端 Java 代碼片斷以下:
@DataBoundConstructor
public MavenCheck(boolean check) {
this.check = check;
}
複製代碼
筆者所寫的插件的核心邏輯是檢查 Maven pom.xml 文件是否包含 SNAPSHOT 版本依賴。
Jenkins 是 Master/Agent 架構, 這就須要讀取 Agent 節點的 workspace 的文件, 這是筆者在寫插件時遇到的一個難點。
Jenkins 強大之處在於它的生態,目前有上千個插件, 筆者參考了 Text-finder Plugin 的源碼, 並在參考處添加了相關注釋,最終實現了插件要實現的功能。
詳細代碼能夠查看 jenkinsci/maven-snapshot-check-plugin 代碼倉庫。
使用 mvn package
命令能夠打包出後綴爲 hpi 的二進制包, 這樣就能夠分發插件,將其安裝到 Jenkins 實例。
如下是對插件的使用簡要描述。
若是勾選了下面截圖中的選擇框, Jenkins 任務在構建時將會檢查 pom.xml 中是否包含 SNAPSHOT 。
若是檢查到的話,則會將該次構建狀態標記爲失敗。
文章上篇主要介紹了從產生 idea 到插件開發完成的過程。 那麼插件在開發完成後是如何將它託管到 Jenkins 插件更新中心讓全部用戶均可以看到的呢? 兩天後的文章下篇將對這個過程進行介紹,敬請期待!
做者:王冬輝