使用gitlab ci構建IOS包並發送通知消息到企業微信

在之前的文章中,我們介紹了使用gitlab ci構建Android包的方法。今天我們介紹使用gitlab ci如何構建IOS包,並且在打包成功或者失敗時,如何將消息通知到企業微信。

如果對gitlab ci還不熟悉的,可以參考之前的文章使用gitlab ci構建Android包,這篇文章我們主要涉及三個知識點:

  • IOS開發者證書。
  • IOS打包命令。
  • 如何往企業微信發消息。

IOS開發者證書

IOS打包離不開開發者證書,因此首先我們需要先搞清楚IOS的證書是怎麼回事,它是怎麼工作的。

為什麼需要證書

我們知道,如果手機不越獄的情況下,iphone手機安裝app只能通過官方的App Store。這種限制是怎麼做到的呢?其實很簡單,主要用到了非對稱加密

首先蘋果官方生成一堆公私鑰,在所有的iphone手機里內置了一個公鑰,在蘋果公司的後台保存著私鑰。當app開發者上傳app到App Store時,會用保存在後台的私鑰對App進行簽名,在iphone手機上下載這個app後,用手機上的公鑰來驗證這個簽名,如果簽名驗證通過,則表示這個app是由蘋果後台認證的,並且沒有被篡改過。

基於這種簽名機制,保證了在iphone手機上安裝的每一個app都是經過蘋果認證允許的。

但是,一個新的問題來了,如果我們的app還處於開發中,還沒有上傳到appStore,該怎麼安裝到iphone手機上呢?這就需要用到開發者證書在中間做一個過渡作用。

證書類型

常用的開發者證書分為兩種,一種是個人開發者證書,一種是企業開發者證書。其中,我們常見的有兩種模式:

  • In-House:企業內部分發,可以直接的安裝ipa包(一般是將包上傳到服務端,生成鏈接,點擊鏈接可以下載)。不過最新的ios系統,需要在【通用—關於本機—證書信任設置】中對企業證書進行信任。
  • Ad-Hoc:相當於是企業分發的限制版,限制100個設備安裝,需要提前在蘋果後台配置iphone設備的設備號(可通過第三方工具或者訪問蒲公英查詢)。

需要注意的是由企業證書籤名的包,是不能上傳到App Store的,因此需要根據自己公司的情況申請不同的開發者證書。

原理介紹

上面其實已經提到,證書的工作原理是通過非對稱加密,從網上找了一幅圖,很好的介紹了這個過程:

上圖對應的步驟如下:

  • 在mac電腦上申請一對公私鑰,圖中是公鑰M和私鑰M。
  • 對於蘋果的證書來說,跟App Store工作原理一樣,在蘋果後台伺服器放置了私鑰A,在蘋果設備上存放了對應的公鑰A。
  • 將公鑰M上傳到蘋果後台,用私鑰A進行簽名,得到包含公鑰M及其簽名。同時還有一個Provision profile(大家常說的pp文件)文件(其中包含了AppID、設備列表、App可使用的許可權),將證書文件下載到本地mac。
  • 在開發app時,使用本地的私鑰M對app進行簽名,連同上面的pp文件一起被打包到app中。
  • 在安裝app時,ios系統獲取證書,通過內置的公鑰A,去驗證app內的證書是正確的。如果能驗證通過,則可以將App內的證書數據取出來,使用公鑰M驗證App的簽名是否正確,驗證安裝app的設備ID是否存在設備列表中等。

上面的步驟,大致描述了蘋果開發者證書的工作原理,如果你沒太理解也沒關係。可以結合著上圖多看幾遍。

另外,這裡還有個知識點,在mac電腦申請的公鑰和私鑰M只能在申請的電腦使用,怎麼讓其他夥伴也能正常使用呢?可以將私鑰M導出成.p12文件,其他Mac電腦導入私鑰M,就可以正常使用了。

IOS打包

首先打包之前,需要清理工程(workspace和scheme參數的值需要拿到ios程式碼才能查看):

$ xcodebuild clean -workspace xxxx.xcworkspace -scheme xxxx

其次,如果你想要ios包的構建號是自動遞增的,可以使用agvtool這個工具:

$ agvtool next-version -all 

接著,就可以開始archive包(對Target進行編譯、歸檔,生成.xcarchive)。

$ xcodebuild -workspace xxxx.xcworkspace -scheme xxxx -configuration Debug archive -archivePath xxxxPath/xxxxx.xcarchive

最後,就是將歸檔文件導出,生成不同渠道的ipa包:

$ xcodebuild -exportArchive -archivePath build/$SCHEME_NAME.xcarchive -exportPath build -exportOptionsPlist $EXPORT_OPTIONS_PLIST

這裡需要指定一個exportOptionsPlist,是對導出ipa的配置,我這裡寫的比較簡單,格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "//www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>compileBitcode</key>
	<true/>
	<key>destination</key>
	<string>export</string>
	<key>method</key>
	<string>development</string>
	<key>signingStyle</key>
	<string>automatic</string>
	<key>stripSwiftSymbols</key>
	<true/>
	<key>teamID</key>
	<string>xxxxxxxx</string>
	<key>thinning</key>
	<string>&lt;none&gt;</string>
</dict>
</plist>

如果你覺得使用命令行的方式相對麻煩的話,可以考慮使用打包工具fastlane(後面我再專門寫文章介紹)。

發送消息通知

企業微信中可以創建一個群機器人,然後通過webhook來進行消息通知。企業微信提供了詳細的配置方式,可以參考:企業機器人配置。其本質上就是發送了一個請求:

curl '//qyapi.weixin.qq.com/cgi-bin/webhook/send?key=693axxx6-7aoc-4bc4-97a0-0ec2sifa5aaa' \
   -H 'Content-Type: application/json' \
   -d '
   {
        "msgtype": "text",
        "text": {
            "content": "hello world"
        }
   }'

我們只需要將key替換成我們創建機器人的key即可。

整合之後的.gitlab-ci.yml配置文件

最後貼一個我在項目中使用的配置文件,如下所示:

variables:
  CONFIGURATION: "Debug"
  WORKSPACE: "xxxx.xcworkspace"
  SCHEME_NAME: "xxxx"
  EXPORT_MAIN_DIRECTORY: "build"
  EXPORT_OPTIONS_PLIST: "ExportOptions-dev.plist"
  CODE_SIGN_IDENTITY: "xxxxxx"
  PROVISIONING_PROFILE: "xxxxx"
  LANG: "en_US.UTF-8"

stages:
  - makedir
  - archive
  - ipa
  - upload
  - notify
# 創建對應目錄
dir_job:
  stage: makedir
  script:
    - mkdir $EXPORT_MAIN_DIRECTORY
    - EXPORT_MAIN_DIRECTORY=$EXPORT_MAIN_DIRECTORY/$(date "+%Y%m%d%H%M%S")
    - echo $EXPORT_MAIN_DIRECTORY
    - mkdir $EXPORT_MAIN_DIRECTORY
  tags:
    - ios

# 構建archive
archive_job:
  stage: archive
  script:
    - agvtool next-version -all   # 更新構建號,版本號之後再更新
    - xcodebuild clean -workspace $WORKSPACE -scheme $SCHEME_NAME
    - xcodebuild -workspace $WORKSPACE -scheme $SCHEME_NAME -configuration Debug archive -archivePath $EXPORT_MAIN_DIRECTORY/$SCHEME_NAME.xcarchive
  artifacts:
    expire_in: '2 day'
    name: "下載xcarchive,保留2天"
    paths:
      - $EXPORT_MAIN_DIRECTORY/$SCHEME_NAME.xcarchive
  tags:
    - ios
# 導出ipa
ipa_job:
  stage: ipa
  script:
    - echo 'export ipa'
    - xcodebuild -exportArchive -archivePath $EXPORT_MAIN_DIRECTORY/$SCHEME_NAME.xcarchive -exportPath $EXPORT_MAIN_DIRECTORY -exportOptionsPlist $EXPORT_OPTIONS_PLIST
  artifacts:
    expire_in: '5 day'
    name: "下載ipa,保留5天"
    paths:
      - $EXPORT_MAIN_DIRECTORY/$SCHEME_NAME.ipa
  only:
    - qa
  tags:
    - ios
# 上傳ipa
upload_job:
  stage: upload
  script:
    - curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjVlOTE4Yzc5MmMzZGQ0MDAxZTRiOGY1YiIsInVzZXJuYW1lIjoic3VodWNoZW4ddiLCJlbWFdddpbCI6InN1aHVjaGVuQHFxLmNvbSJ9LCJleHAiOjQ3NDAxOTcyODgsImlhdCI6MTU4NjU5NzI4OH0.5UUkM4lJddYrnvXvHaNNJIY_j5OsBQmLw0mBUrXG3d9E4" -F "file=@$EXPORT_MAIN_DIRECTORY/$SCHEME_NAME.ipa" //上傳包地址/api/apps/5e916b9eac2363001dd7554a/upload
  only:
    - qa
  tags:
    - ios 

# 構建失敗時的通知消息
notifyFailWeChat:
  stage: notify
  script:
    - curl '//qyapi.weixin.qq.com/cgi-bin/webhook/send?key=26b07c1b-03ea-49da-afc7-f68a359f2a52' -H 'Content-Type:application/json' -d "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"ios項目構建結果:<font color=\\"warning\\">失敗</font>\n>本次構建由:$GITLAB_USER_NAME 觸發\n>項目名稱:$CI_PROJECT_NAME\n>提交號:$CI_COMMIT_SHA\n>提交日誌:$CI_COMMIT_MESSAGE\n>構建分支:$CI_COMMIT_BRANCH\n>流水線地址:[$CI_PIPELINE_URL]($CI_PIPELINE_URL)\"}}"
  tags:
    - ios
  only:
    - qa
  when: on_failure

# 構建成功時的通知消息
notifySuccessWeChat:
  stage: notify
  script:
    - curl '//qyapi.weixin.qq.com/cgi-bin/webhook/send?key=26b07c1b-03ea-49da-afc7-f68a359f2a52' -H 'Content-Type:application/json' -d "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"ios項目構建結果:<font color=\\"info\\">成功</font>\n>請前往發布平台下載體驗:[下載地址](//app下載地址)\n>本次構建由:$GITLAB_USER_NAME 觸發\n>項目名稱:$CI_PROJECT_NAME\n>提交號:$CI_COMMIT_SHA\n>提交日誌:$CI_COMMIT_MESSAGE\n>構建分支:$CI_COMMIT_BRANCH\n>流水線地址:[$CI_PIPELINE_URL]($CI_PIPELINE_URL)\"}}"
  tags:
    - ios
  only:
    - qa
  when: on_success

總結

如果你們公司目前還沒搞起來Jenkins,我推薦嘗試用gitlab實現ci/cd流水線,因為可以減少很多配置和插件的安裝。相對來說實現成本更低一些,從目前我用gitlab ci的情況來看,基本上Jenkins能實現的gitlab ci都能滿足。