React Plugin Template,讓你可以使用 React 來編寫 Jenkins 插件

  • 2019 年 10 月 6 日
  • 筆記

模板地址

React Plugin Template https://github.com/jenkinsci/react-plugin-template

起因

這個模板是筆者在今年的 Google Summer of Code 中的項目 Working Hours - UI Improvement 中產出的。由於我們想使用 React 的一些組件來優化用戶體驗,例如在 Working Hours 裡面我們想用 ReactDatepicker 來幫助用戶選擇日期,於是整個 Working Hours 插件的前端部分都試圖用 React 來編寫,而由於 Jenkins 的傳統插件編寫主要還是用的 Jelly ,一套類似 JSP 的後端渲染引擎,因此筆者在一開始也踩了不少坑。以至於想到,可以抽象出一套插件的腳手架來幫助有相似需求的同學。

鏈接:

GSoC:Working Hours UI Improvement https://summerofcode.withgoogle.com/projects/#6112735123734528

Github:Working Hours 插件 https://github.com/jenkinsci/working-hours-plugin

概述

在以往,我們可以使用 Jelly 來開發 Jenkins 插件的前端部分,同時一些請求可以綁定到對應的類,但是當我們想要更高程度地去自定義插件介面的時候,Jelly 就顯得捉襟見肘了。這就是這個模板的目的,幫助開發者使用 React 來開發一個插件。

同時,有了 React ,我們就可以使用很多基於 React 的庫,webpack 也可以幫助我們更安全更高效地使用 js 庫。

特點

| 集成 React | 開發者可以使用 React 充分控制 UI | 使用了 Iframe | Iframe 隔離了之前 Jenkins 添加的一些 js 庫會造成的影響,例如 Prototype.js。| 使用 maven 的生命周期 | 使用了 Frontend Maven Plugin https://github.com/eirslett/frontend-maven-plugin, npm 腳本可以在 maven 的生命周期中被同步執行。| 使用 Webpack | Webpack 可以減少最終包的體積,同時避免 js 庫對全部命名空間的影響 | 為每個請求附加 Jenkins Crumb| Jenkins Crumb 是 Jenkins 的一套 token 系統,在本模板中已經自動附加到 axios 實例上 | 使用了 Express 的 devserver | 可以單獨打開一個支援熱更新的 devserver 來編寫前端介面

截圖

插件 UI

示例入口

入門

克隆倉庫:

git clone https://github.com/jenkinsci/react-plugin-template.git  cd react-plugin-template  

安裝 maven 依賴和 npm 包

mvn install -DskipTests  

運行支援熱更新的網頁

npm run start  

運行插件

mvn hpi:run -Dskip.npm -f pom.xml

發送HTTP請求

由於 Jenkins Crumb 策略默認是開啟的,每個請求都被要求包含一個 Jenkins Crumb, 所以請使用已經設置好了 Jenkins Crumb 的 axiosInstance,可以在 src/main/react/app/api.js 中找到。

export const apiGetData = () => {    return axiosInstance.post("/data");  };

或者你想使用你自己的 http client,你只需將 Jenkins Crumb 添加到你每個請求的 header中,這個 header 的 key 和 content 都可以使用 src/main/react/app/utils/urlConfig.js,具體操作如下

const headers = {};  const crumbHeaderName = UrlConfig.getCrumbHeaderName();    if (crumbHeaderName) {    headers[crumbHeaderName] = UrlConfig.getCrumbToken();  }

編寫後端的請求處理器

當你可以自定義你的請求後,你同時也需要一個在後端的 Handler。

Jenkins 使用了一個叫做 Stapler的框架來處理請求。你可以使用一個繼承了 Action 的類來創建一個子 url ,同時可以使用一個 StaplerProxy 來轉發或者直接處理請求。

鏈接

Stapler Reference http://stapler.kohsuke.org/reference.html.

示例 handler

如下,ManagementLink(已經繼承 Action )會首先拿到這個請求然後轉交個 PluginUI 來處理。

@Extension  public class PluginManagementLink extends ManagementLink implements StaplerProxy {        PluginUI webapp;        public Object getTarget() {          return webapp;      }        public String getUrlName() {          return "react-plugin-template";      }  }

隨後, stapler 會在 PluginUI 中尋找適合的處理函數,如下, doDynamic 便是一個處理函數, 然後便是我自定義的,根據 url 來判斷需要調用的函數, getTodos 或者 setTodos, 在這裡 PluginUI 可能更像一個 url router。

public class PluginUI{      public HttpResponse doDynamic(StaplerRequest request) {          ...            List<String> params = getRequestParams(request);            switch (params.get(0)) {          case "get-todos":              return getTodos();          case "set-todos":              return setTodos(request);          }          ...      }  }

保存數據

你可以使用一個 Descriptor 來保存你的數據

@Extension  public class PluginConfig extends Descriptor<PluginConfig> implements Describable<PluginConfig>

當你每次修改數據, 調用 save() 來保存他們。Jenkins 是使用基於 xml 的序列化方法來保持數據的。

public void setTodos(          @CheckForNull List<Todo> value) {      this.todos = value;      save();  }

而在你的 handler 中,你可以調用如下程式碼來找到你的這個存儲數據的對象。

config = ExtensionList.lookup(PluginConfig.class).get(0);

自定義你的插件

把所有 react-template 修改為你的插件名

  • 在 org/jenkinsci/plugins/reactplugintemplate/PluginUI/index.jelly , 修改 iframe 的 id 和 source url.
  • 在 src/main/react/server/config.js , 修改 devserver 的代理路徑
  • 在 src/main/react/package.json , 修 start 命令中的 BASE_URL
  • 在 pom.xml , 修改 artifactId
  • 在 org/jenkinsci/plugins/reactplugintemplate/PluginManagementLink.java , 修改 names。

為你的插件自定義一個頁面

比較推薦使用 Management Link, 這會讓你的插件有一個獨立的頁面, 並且可以在系統管理介面 /manage 放置一個入口。

原理

本模板僅僅是將一個 webpack 的 project

放到了 Maven project 中,同時將 webpack 的 build 結果複製到插件的 webapp 目錄,隨後便可以在 iframe 中通過 url 訪問到,最後被 Jelly 渲染到網頁。

為什麼使用Iframe?

因為到現在,Jenkins 的網頁已經添加了很多的 js 庫了(似乎是添加到全局的),因此可能會和較新的庫產生一些衝突。因此也一個 Iframe 可以使我們的插件運行在一個相對「乾淨」的環境裡面。

鏈接

Github: React Plugin Template https://github.com/jenkinsci/react-plugin-template

Github: Working Hours Plugin https://github.com/jenkinsci/working-hours-plugin

Tags: