夥伴們正如火如荼趕上線,我也趕緊利用開發閒置的時間,進行 Jenkins 建置 CI/CD 流程。
此流程對於大型團隊相當有助益,不過就算是一人團隊,若產品專案需要每隔一段時間釋出給他人測試,有持續部署機制就會節省許多時間!
早在 2018 年,我香港團隊就實施 Jenkins CI/CD 流程,我們 iOS 和 Android 團隊各有四個工程師,有此持續整合迭代流程,就可以很順暢地進行開發~😀
Jenkins 簡介
Jenkins 是一款流行的開源持續整合(Continuous Integration)工具,廣泛用於專案開發,具有自動化構建、測試和部署等功能。Jenkins 使用 Java 語言編寫,可在 Tomcat 等流行的 servlet 容器中執行,也可以獨立執行。通常與版本管理工具(SCM)、構建工具結合使用。常用的版本控制工具有 SVN、GIT,構建工具有 Maven、Ant、Gradle。

CI 簡介
CI(持續整合,Continuous Integration),是在開發的過程中,我們需要隨時隨地確保專案 code 主幹可以處在能正常發佈的狀態。也就是說,不能因為正在開發新功能或修正舊問題,導致我們的主幹程式無法運作或是打包新版本。
CD 簡介
CD(持續部署,Continuous Delivery),是我們希望在開發的任何一個階段,都能夠自動化打包出版本給需要的人使用。那誰會是需要的人?在開發的過程中,工程團隊想要手動測試 App 時,就會需要一個版本來測試;而在開發完畢後,UI/UX 測試人員也會需要一個版本來做測試。最後,在 App 要上線時,理所當然也會需要一個版本來送到 App Connect 上以供審核。
CICD 的好處
- 每次建置成功後的專案皆可順利部署
- 專案狀態透明完整,提早發現錯誤並修正
- 減少重複過程(編譯、發佈、測試),以節省開發者時間
- 時常編譯和測試,降低上線前的測試壓力
Jenkins 如何幫助 iOS 專案呢?
- 自動引入 git 倉庫的程式碼進行構建和測試
- 自動編譯、測試、分發部署
- 自動在指定日期時間進行建置和檢查
- 自動在 git 倉庫程式碼變動時進行建置和檢查
- 自動在建置時有特定事件時通知設定的使用者
- 自動在建置時任何階段加入 shell script
- 自動上傳建置好的版本至 Testflight 和 App Store
- 自動在遇到任何狀況時寄信通知相關人員

接下來,分享我所做的過程,網路上有太許多教學文章,有的很詳細,有的很繁瑣,還有些是已過時,我將簡化許多步驟,可讓需要的人參考~🙃
最終,我們想要建立的完整流程如下:

先說一下我執行下的環境:
- MacOS 12.0.1
- Xcode 13.3.1
安裝 Jenkins
我是使用 Homebrew 軟體包管理器安裝 Jenkins,沒安裝 Homebrew 的朋友請自行安裝。
安裝命令:
安裝最新的LTS版本: brew install jenkins-lts
安裝特定的LTS版本: brew install jenkins-lts@YOUR_VERSION
啟動 Jenkins 服務: brew services start jenkins-lts
重新啟動 Jenkins 服務: brew services restart jenkins-lts
更新 Jenkins 版本: brew upgrade jenkins-lts
啟動 Jenkins 服務後,瀏覽 http://localhost:8080。

該頁面需要確認是管理員安裝,讓我們輸入密碼,密碼存放在紅色的目錄下,取出來填到輸入框裡就行。

選擇安裝推薦的外掛~

有人說可能會安裝失敗,但我多次安裝 Jenkins 都是一次成功。
新增作業
請選擇「建立 Free Style 軟體專案」
接下來有幾個標籤,可以依照你的需求填入與選擇。

General
原始碼管理
我是使用 GitLab,需輸入 credentials 登入的帳號與密碼;打包分支可以根據需要設定,我使用的是 main
拉下來的專案將位於 /Users/[username]/.jenkins/workspace,也許你的不在此
建置觸發程序
可以設定特定時間,或是每隔多久觸發。例如:
H 08 * * 1-5
H 16 * * 1-5
代表週一至週五,早上八點和下午四點進行建置。
建置(關鍵)
點選增加構建步驟,選擇執行 Shell,輸入打包指令碼
建置後動作
馬上建置
就能立刻進行所有流程,一旦有問題就會報錯。
我們也可以看建置歷程的時間軸~
事實上,最難的地方在於寫 Script,是 CI/CD 精髓所在!我們來看一下,大致上必須要怎麼寫~
if [ "HEADHASH" != "UPSTREAMHASH" ]
then
echo -e Not up to date with release.
echo
# iTuneConnect info
ITUNECONNECT_USER_NAME="happyman.cg@gmail.com"
ITUNECONNECT_USER_PASSWORD="oztp-uaat-qchh"
# 專案名
TARGET_NAME="HappyCurrency"
# 文件名前缀
APP_NAME="HappyCurrency"
# 證書
CODE_SIGN_DISTRIBUTION="iPhone Distribution: Happy Studio"
# info.plist 路徑
project_infoplist_path="./${TARGET_NAME}/Info.plist"
# exportOptions.plist 路徑
project_exportOptionsplist_path="./${TARGET_NAME}/exportOptions.plist"
# 取版本號
bundleShortVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${project_infoplist_path}")
# 取建置值
bundleVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" "${project_infoplist_path}")
# Build archive 位置
WORKPLACE_PATH="$HOME/Desktop/JenkinsWorkPlace"
ARCHIVE_FILE_LOCATION="${WORKPLACE_PATH}/Build/${TARGET_NAME}.xcarchive"
# ipa檔案名設定
DATE="$(date +%Y%m%d)"
IPA_FOLDER_NAME="${APP_NAME}_V${bundleShortVersion}_${DATE}"
# 要上傳的ipa檔案路徑
IPA_PATH="${WORKPLACE_PATH}/${IPA_FOLDER_NAME}"
IPA_FILE_LOCATION="${IPA_PATH}/${TARGET_NAME}.ipa"
echo ${IPA_FILE_LOCATION}
echo "${IPA_FILE_LOCATION}">> text.txt
# 下面2行是整合有Cocopods的用法
echo "=================Clean================="
xcodebuild -workspace "${TARGET_NAME}.xcworkspace" -scheme "${TARGET_NAME}" -configuration 'Release' clean
echo "+++++++++++++++++Build+++++++++++++++++"
xcodebuild -workspace "${TARGET_NAME}.xcworkspace" -scheme "${TARGET_NAME}" -sdk iphoneos -configuration AppStoreDistribution archive -archivePath "${ARCHIVE_FILE_LOCATION}"
echo "+++++++++++++++++Archive+++++++++++++++"
xcodebuild -exportArchive -archivePath "${ARCHIVE_FILE_LOCATION}" -exportOptionsPlist "${project_exportOptionsplist_path}" -exportPath "${IPA_PATH}"
echo "+++++++++++++++++Upload+++++++++++++++"
xcrun altool --upload-app -f ${IPA_FILE_LOCATION} -t iOS -u "${ITUNECONNECT_USER_NAME}" -p "${ITUNECONNECT_USER_PASSWORD}"
else
echo -e Current branch is up to date with origin/release.
fi
-exportArchive 有一個檔案 exportOptions.plist。不用自己建立,可以使用 Xcode 匯出 ipa 檔後,從匯出的資料夾裡獲取。名字為 exportOptions.plist,修改一下名字就行了。
method 可有四種渠道,可將它們做區分,分成四個不同的 plist 檔案。分別為 app-store、ad-hoc、enterprise、development。

我將打包的 ipa 檔匯出在 /Users/[username]/Desktop/JenkinsWorkPlace。先用 Xcode 打開專案,在 Signing & Capabilities 將 Team 給設定正確。
以上 Script 是我的草稿,可自行添加所需程序,若有報錯或不知道的部分,請再上網查詢唄~
第一行的程式碼,我是想要判斷當前的 commit,跟 Git 分支最新的 commit 是否一樣,不一樣的話再執行編譯。請聰明的朋友,可幫我補完喔~🤠
我已經寫得夠簡潔,可以讓你很容易上手,若要我再寫更詳細,等我有更多空閒時間吧~😌
參考:
隨意留個言吧:)~