使用Jenkins生成 二維碼
背景描述
根據專案需求,現要在團隊內部搭建一個統一的打包平臺,實現對iOS和Android專案的打包。而且為了方便團隊內部的測試包分發,希望在打包完成後能生成一個二維碼,體驗使用者(產品、運營、測試等人員)通過手機掃描二維碼後就能直接安裝測試包。
該需求具有一定的普遍性,基本上所有開發APP的團隊都可能會用到,因此我將整個需求實現的過程整理後形成此文,並且真正地做到了零基礎上手,到手即飛、開箱即用
,希望能對大家有所幫助。
首先,先給大家展示下平臺建設完成後的整體效果:
該平臺主要實現的功能有3點:
- 定期對GitHub倉庫進行檢測,若有更新則自動執行構建打包;
- 構建成功後根據ipa/apk生成二維碼,並可在歷史構建列表中展示各個版本的二維碼,通過手機掃描二維碼可直接安裝對應版本;
- 在構建結果頁面中展示當次構建的成果物(Artifact,如
.ipa
、.app
、.apk
、info.plist
等檔案),供有需要的使用者進行下載。
接下來,本文就開始對平臺建設的完整實現過程進行詳細介紹。
安裝Jenkins
Jenkins依賴於Java執行環境,因此需要首先安裝Java。
安裝Jenkins的方式有多種,可以執行對應系統型別的安裝包,可以通過docker獲取映象,也可以直接執行war
包。
我個人傾向於直接執行war
包的形式,只需下載jenkins.war
後,執行如下命令即可啟動Jenkins。
1 | $ nohup java -jar jenkins_located_path/jenkins.war --httpPort=88 & |
如果不指定httpPort
,Jenkins的預設埠為8080。
Jenkins外掛
Jenkins有非常多的外掛,可以實現各種功能的擴充套件。
針對搭建的iOS/Android持續整合打包平臺,我使用到了如下幾個外掛。
- GIT plugin
- SSH Credentials Plugin
- Git Changelog Plugin: 獲取倉庫提交的commit log
- build-name-setter:用於修改Build名稱
- description setter plugin:用於在修改Build描述資訊,在描述資訊中增加顯示QRCode(二維碼)
- Post-Build Script Plug-in:在編譯完成後通過執行指令碼實現一些額外功能
- Xcode integration: iOS專用(可選)
- Gradle plugin: Android專用(可選)
安裝方式也比較簡單,直接在Jenkins的外掛管理頁面搜尋上述外掛,點選安裝即可。
建立專案(Job)
在Jenkins中,構建專案以Job的形式存在,因此需要針對每個專案建立一個Job。有時候,一個專案中可能有多個分支同時在進行開發,為了分別進行構建,也可以針對每個分支建立一個Job。
建立Job的方式有多種,本次只需要建立Freestyle project
型別的即可。
Main page
->New Item
->Freestyle project
對於一個持續整合打包平臺,每次打包都由4步組成:觸發構建、拉取程式碼、執行構建、構建後處理。對應的,在每個Job中也對應了這幾項的配置。
配置Git程式碼倉庫
要對專案進行構建,配置專案的程式碼倉庫是必不可少的。由於當前我們的專案託管在GitHub私有倉庫中,因此在此需要對Git
進行配置。
在【Source Code Management】
配置欄目下,如果之前GIT plugin
安裝成功,則會出現Git
選項。
配置Git程式碼倉庫時,有三項是必須配置的:倉庫URL地址(Repository URL
)、倉庫許可權校驗方式(Credentials
),以及當前Job需要構建的程式碼分支(Branches to build
)。
在配置Repository URL
時,選擇HTTPS URL
或SSH URL
均可。不過需要注意的是,Credentials
要和Repository URL
對應,也就是說:
- 如果
Repository URL
是HTTPS URL
形式的,那麼Credentials
就要採用GitHub使用者名稱密碼的校驗方式;而且,如果在GitHub中開啟了2FA(two-factor authentication)
,那麼還需要在GitHub中建立一個Personal access token
,輸入密碼時將這個Personal access token
作為密碼進行輸入。 - 如果
Repository URL
是SSH URL
形式的,那麼就需要先在Jenkins所在的伺服器上建立一個SSH
祕鑰對,並將公鑰新增到GitHub的SSH keys
中,然後在填寫Credentials
時,選擇SSH Username with private key
的校驗方式,填入GitHub Username、SSH私鑰、以及建立SSH
祕鑰對時設定的Passphrase
。
如果對Git許可權校驗的概念還比較模糊,可以參考《深入淺出Git許可權校驗》。
在配置Branches to build
時,可以採用多種形式,包括分支名稱(branchName
)、tagName
、commitId
等。其中分支名稱的形式用的最多,例如,若是構建master
分支,則填寫refs/heads/master
,若是構建develop
分支,則填寫refs/heads/develop
。
除了以上關於Git的必填配置項,有時根據專案的實際情況,可能還需要對Jenkins的預設配置項進行修改。
比較常見的一種情況就是對clone
的配置進行修改。
在Jenkins的預設配置中,clone
程式碼時會拉取所有歷史版本的程式碼,而且預設的超時時限只有10分鐘。這就造成在某些專案中,由於程式碼量本身就比較大,歷史版本也比較多,再加上網路環境不是特別好,Jenkins根本沒法在10分鐘之內拉取完所有程式碼,超時後任務就會被自動終止了(錯誤狀態碼143)。
這種問題的解決方式也很簡單,無非就是兩種思路,要麼少拉取點程式碼(不獲取歷史版本),要麼提高超時時限。對應的配置在Advanced clone behaviours
中:
Shallow clone
:勾選後不獲取歷史版本;Timeout (in minutes) for clone and fetch operation
:配置後覆蓋預設的超時時限。
配置構建觸發器
程式碼倉庫配置好了,意味著Jenkins具有了訪問GitHub程式碼倉庫的許可權,可以成功地拉取程式碼。
那Jenkins什麼時候執行構建呢?
這就需要配置構建觸發策略,即構建觸發器,配置項位於【Build Triggers】
欄目。
觸發器支援多種型別,常用的有:
- 定期進行構建(Build periodically)
- 根據提交進行構建(Build when a change is pushed to GitHub)
- 定期檢測程式碼更新,如有更新則進行構建(Poll SCM)
構建觸發器的選擇為複合選項,若選擇多種型別,則任一型別滿足構建條件時就會執行構建工作。如果所有型別都不選擇,則該Jenkins Job
不執行自動構建,但可通過手動點選【Build Now】
觸發構建。
關於定時器(Schedule)的格式,簡述如下:
MINUTE HOUR DOM MONTH DOW
- MINUTE: Minutes within the hour (0-59)
- HOUR: The hour of the day (0-23)
- DOM: The day of the month (1-31)
- MONTH: The month (1-12)
- DOW: The day of the week (0-7) where 0 and 7 are Sunday.
通常情況下需要指定多個值,這時可以採用如下operator(優先順序從上到下):
*
適配所有有效的值,若不指定某一項,則以*
佔位;M-N
適配值域範圍,例如7-9代表7/8/9均滿足;M-N/X
或*/X
:以X作為間隔;A,B,C
:列舉多個值。
另外,為了避免多個任務在同一時刻同時觸發構建,在指定時間段時可以配合使用H
字元。新增H
字元後,Jenkins會在指定時間段內隨機選擇一個時間點作為起始時刻,然後加上設定的時間間隔,計算得到後續的時間點。直到下一個週期時,Jenkins又會重新隨機選擇一個時間點作為起始時刻,依次類推。
為了便於理解,列舉幾個示例:
H/15 * * * *
:代表每隔15分鐘,並且開始時間不確定,這個小時可能是:07,:22,:37,:52
,下一個小時就可能是:03,:18,:33,:48
;H(0-29)/10 * * * *
:代表前半小時內每隔10分鐘,並且開始時間不確定,這個小時可能是:04,:14,:24
,下一個小時就可能是:09,:19,:29
;H 23 * * 1-5
:工作日每晚23:00至23:59之間的某一時刻;
配置構建方式
觸發策略配置好之後,Jenkins就會按照設定的策略自動執行構建。但如何執行構建操作,這還需要我們通過配置構建方式來進行設定。
常用的構建方式是根據構建物件的具體型別,安裝對應的外掛,然後採用相應的構建方式。例如,若是構建Android
應用,安裝Gradle plugin
之後,就可以選擇Invoke Gradle script
,然後採用Gradle
進行構建;若是構建iOS
應用,安裝Xcode
integration
外掛之後,就可以選擇Xcode
,然後選擇Xcode
進行構建。
該種方式的優勢是操作簡單,UI視覺化,在場景不復雜的情況下可以快速滿足需求。不過缺點就是依賴於外掛已有的功能,如果場景較複雜時可能單個外掛還無法滿足需求,需要再安裝其它外掛。而且,有些外掛可能還存在一些問題,例如對某些作業系統版本或XCode版本相容不佳,出現問題時我們就會比較被動。
我個人更傾向於另外一種方式,就是自己編寫打包指令碼,在指令碼中自定義實現所有的構建功能,然後在Execute Shell
中執行。這種方式的靈活度更高,各種場景的構建需求都能滿足,出現問題後也能自行快速修復。
另外,對於iOS應用的構建,還有一個需要額外關注的點,就是開發者證書的配置。
如果是採用Xcode integration
外掛進行構建,配置會比較複雜,需要在Jenkins中匯入開發證書,並填寫多個配置項。不過,如果是採用打包指令碼進行構建的話,情況就會簡單許多。只要在Jenkins所執行的計算機中安裝好開發者證書,打包命令在Shell中能正常工作,那麼在Jenkins中執行打包指令碼也不會有什麼問題。
構建後處理
完成構建後,生成的編譯成果物(ipa/apk)會位於指定的目錄中。但是,如果要直接在手機中安裝ipa/apk
檔案還比較麻煩,不僅在分發測試包時需要將好幾十兆的安裝包進行傳送,體驗使用者在安裝時也還需要通過資料線將手機與計算機進行連線,然後再使用PP助手或豌豆莢等工具進行安裝。
當前比較優雅的一種方式是藉助蒲公英(pgyer)
或fir.im
等平臺,將ipa/apk
檔案上傳至平臺後由平臺生成二維碼,然後只需要對二維碼連結進行分發,體驗使用者通過手機掃描二維碼後即可實現快速安裝,效率得到了極大的提升。
上傳安裝包檔案,生成二維碼
不管是蒲公英
還是fir.im
,都有對應的Jenkins外掛,安裝外掛後可以在Post-build
中實現對安裝包的上傳。
除了使用Jenkins外掛,fir.im
還支援命令上傳的方式,蒲公英
還支援HTTP Post
介面上傳的方式。
我個人推薦採用命令或介面上傳的方法,並在構建指令碼中進行呼叫。靈活是一方面,更大的好處是如果上傳失敗後還能進行重試,這在網路環境不是很穩定的情況下極其必要。
Jenkins成功完成安裝包上傳後,pgyer/fir.im
平臺會生成一個二維碼圖片,並在響應中將圖片的URL連結地址進行返回。
展示二維碼圖片
二維碼圖片的URL連結有了,那要怎樣才能將二維碼圖片展示在Jenkins專案的歷史構建列表中呢?
這裡需要用到另外一個外掛,description setter plugin
。安裝該外掛後,在【Post-build Actions】
欄目中會多出description setter
功能,可以實現構建完成後設定當次build的描述資訊。這個描述資訊不僅會顯示在build頁面中,同時也會顯示在歷史構建列表中。
有了這個前提,要將二維碼圖片展示在歷史構建列表中貌似就可以實現了,能直觀想到的方式就是採用HTML
的img
標籤,將<img src='qr_code_url'>
寫入到build描述資訊中。
這個方法的思路是正確的,不過這麼做以後並不會實現我們預期的效果。
這是因為Jenkins出於安全的考慮,所有描述資訊的Markup Formatter
預設都是採用Plain text
模式,在這種模式下是不會對build描述資訊中的HTML編碼進行解析的。
要改變也很容易,Manage Jenkins
-> Configure Global Security
,將Markup Formatter
的設定更改為Safe HTML
即可。
更改配置後,我們就可以在build描述資訊中採用HTML
的img
標籤插入圖片了。
另外還需要補充一個點。如果是使用蒲公英(pyger)
平臺,會發現每次上傳安裝包後返回的二維碼圖片是一個短連結,神奇的是這個短連線居然是固定的(對同一個賬號而言)。這個短連線總是指向最近生成的二維碼圖片,但是對於二維碼圖片的唯一URL地址,平臺並沒有在響應中進行返回。在這種情況下,我們每次構建完成後儲存二維碼圖片的URL連結就沒有意義了。
應對的做法是,每次上傳完安裝包後,通過返回的二維碼圖片短連結將二維碼圖片下載並儲存到本地,然後在build描述資訊中引用該圖片在Jenkins中的地址即可。
收集編譯成果物(Artifacts)
每次完成構建後,編譯生成的檔案較多,但是並不是所有的檔案都是我們需要的。
通常情況下,我們可能只需要其中的部分檔案,例如.ipa/.app/.plist/.apk
等,這時我們可以將這部分檔案單獨收集起來,並在構建頁面中展示出來,以便在需要時進行下載。
要實現這樣一個功能,需要在【Post-build Actions】
欄目中新增Archive the artifacts
,然後在Files to archive
中通過正則表示式指定成果物檔案的路徑。
設定完畢後,每次構建完成後,Jenkins會在Console Output
中採用設定的正則表示式進行搜尋匹配,如果能成功匹配到檔案,則會將檔案收集起來。
總結
本文主要是對如何使用Jenkins搭建iOS/Android持續整合打包平臺的基礎概念和實施流程進行了介紹。對於其中涉及到的執行命令、構建指令碼(build.py),以及Jenkins的詳細配置,出於篇幅長度和閱讀體驗的考慮,並沒有在文中進行詳細展開。
為了實現真正的開箱即用
,我將Jenkins的配置檔案和構建指令碼抽離出來形成一套模板,只需要匯入到Jenkins中,然後針對具體的專案修改少量配置資訊,即可將這一套持續整合打包平臺執行起來,實現和文章開頭插圖中完全相同的功能效果。