iOS 持續集成

iOS 持續集成系列 - 開篇

前言

iOS 開發在通過這幾年的野蠻生長以後,慢慢地趨於穩定。不管開發語言是 Objective-C 仍是 Swift,工程類型是 Hybird 仍是原生,開發思想是 OOP 仍是函數式,隨着項目逐漸變大都在面臨相同的問題: 測試、發佈等重複性工做佔了很大一部分時間,迴歸成本愈來愈高。持續集成不可避免地被提上了日程。html

本文主要闡述 iOS 下的持續集成,以目標、內容、流程、工具入手,但願能夠爲你們描繪一幅 iOS 持續集成的藍圖。這可能不是一篇可讓你 Step by Step 跟着作的文章,希望能夠在你腦海中創建相關概念,以便在實操時走對方向。node

咱們會在後面幾篇內容中,詳細闡述 是什麼 和 如何作 。ios

目標

對於咱們來講,減小重複工做、提高團隊效率。 對於公司來講,省錢!git

狹義上持續集成指早集成早測試儘早早發現問題早修復,並輔以一些自動化的手段,其目標是減小修復的成本。經過其目的,咱們能夠發現,其實經過自動化減小重複工做和經過早發現問題下降成本是持續集成的核心理念。所以,我把自動化 Code Review 也放到這裏講。github

內容

iOS 下的持續集成內容包括自動化 Code Reivew、自動化單元測試、自動化打包和自動化分發。自動化 Code Review 保證團隊遵照代碼規範,在編碼階段減小 BUG 的產生。在人員流動較大的公司裏,用同一套代碼規範,也能夠保證項目交接更流暢。自動化單元測試在集成階段檢測出 BUG ,以減小回歸成本。自動化打包和自動化分發則是減小重複勞動,畢竟,工程師的時間就是金錢啊。objective-c

自動化 Code Review

顧名思義,自動化 Code Review 既採用自動化的手段,對團隊成員得代碼進行 Review,以保證代碼質量。從現實角度來講,自動化的 Code Review 更多地是對代碼進行靜態分析,經過掃描代碼並對比制定的規則,產出所須要的結果。這個所須要的結果,能夠是工程整體的量化的質量報告,也能夠是顯示在 Xcode 中的一條警告⚠️。這取決於用戶是什麼角色。json

在實際實踐中,通常會有兩種角色會關注這份結果 -- 工程師和管理層。工程師須要在開發的過程當中及時瞭解代碼錯誤,以便及時糾正。管理層須要瞭解工程的整體代碼質量,以掌握項目的相關風險。同時,也能夠做爲工程師績效的依據之一。swift

要完成這一塊內容,咱們須要這些工具:xcode

經過這三者協做,咱們能夠實現如下流程:bash

工程師經過 `Git Commit` 提交代碼 → Web Hook 觸發 `Jenkins` 構建 → `OCLint` 掃描代碼生成PMD格式報告 → `Sonar-runner` 讀取報告並展示到 `SonarQube`。

關於 Code Review 須要指出的是,自動化工具是有侷限性的。其沒法分析出較爲"智能"的規則。好比說下面這條

4.7 方法名以小寫字母動詞做爲開頭

還有下面這種代碼,儘管含有多個容易形成閃退的 BUG,也是能夠順利逃過度析器的眼睛的

if result?.bindPhone == "true" {  
    let range = (result?.loginId?.startIndex.advancedBy(3))!...(result?.loginId?.endIndex.advancedBy(-5))!
    let phoneNumber: String? = result?.loginId?.stringByReplacingCharactersInRange(range, withString: "****")
    self.phoneNumLabel.text = phoneNumber!
}

所以,想要達到 「保證代碼質量」 的目的,還須要人工 Review 和自動化 Review 相結合。

自動化單元測試

自動化地執行單元測試,在測試失敗的狀況下中斷集成,並通知相關人員,就是這一塊工做的內容。

爲此,咱們須要以下的工具:

咱們能夠實現如下的流程:

`Git Merge`  → Web Hook 觸發 `Jenkins` 構建 → xctool 執行單元測試 → 若是失敗則發郵件給相關人員。

固然,若是你公司的項目託管在 GitHub 上,業界有兩個很是優秀的 Jenkins 替代產品 travis-ci 和 circle-ci。他們對 GitHub 的支持完美,且對開源項目是免費的。私有項目則須要付費試用。

在單元測試這一塊,最高的成本仍是寫單元測試的時間成本。腦洞大開地說,若是能實現一款工具,能夠自動地爲業務代碼生成測試代碼,那纔是生產力的大大提高。

自動化打包與分發

一次完整的打包,須要經歷配置證書、切換環境、調整參數(構建版本號等)、Archive、導出。 根據工程的複雜程度,以上過程快則五分鐘,慢則半小時。當須要頻繁發版的狀況下,浪費了工程師很是多的時間。

咱們其實能夠利用一些工具來實現這樣的一個流程:

Git Merge 到 Master(一般這意味着一個 Release)→ 觸發 Jenkins 構建生成 ipa → 自動化分發。

自動分發包含如下工做:

  • 自動上傳 App Store
  • 自動使用 TestFlight 分發
  • 自動上傳到 蒲公英/Fir 等平臺
  • 自動上傳到企業 App Store

咱們能夠分別配置相關的腳本,來實現測試的分發,和發佈等。

要實現這樣的流程,咱們須要這些工具:

Fastlane 是由一個個小組件組成的工具,功能包括上傳截圖到 iTunes Connect,建立 provisioning file, 管理 TestFlight 的測試員,分發等。這個工具幾乎能夠用命令行來實現打包上傳分發相關的全部功能,而且愈來愈有成爲默認的業界持續集成標準工具的樣子。就像 CocoaPods 在依賴管理方面同樣。

總結

持續集成,能夠看作是經過版本控制系統、CI平臺(如Jenkins)和相關工具鏈來完成一套工做流,以減小團隊重複性工做,並保證軟件能夠不停地迭代而不出太大的差錯。iOS 下的持續集成大致就是上述幾塊內容,咱們在實現的過程當中遇到了很多坑,後面的文章分塊詳細講。

 

 

 

iOS 持續集成系列 - 自動化 Code Review

爲了保證代碼質量,Code Review 是很是重要的一環。細到*的位置是否正確,大到代碼的結構是否符合了軟件開發的一些基本原則,都在這項工做的範圍內。

受限於現實狀況,大多數團隊沒有足夠的時間進行 Code Review,那麼只能把一部分 CR 工做交給計算機去完成了。咱們只須要定下合理的流程,用代碼告訴計算機須要作什麼,剩下的就交給咱們可靠的夥伴吧。

應用了自動化 Code Review 後,若是你的代碼寫得很差,Xcode 會表示不開心。

若是你忽略 Xcode 的心情,那麼質量管理平臺會默默地記錄這一切。

這套東西既幫助開發們寫出更高質量的的代碼,也給經理們對工程質量的評估提供了一個切面的支持,同時只須要花費較少的人力維護,聽起來是否是躍躍欲試了呢 : )

流程

總體的工做流程很是簡單,如圖:

關鍵點在於本地 Review和遠端 Review這兩步。前者是提供給開發者一個即時的代碼質量反饋,以便開發者修改,從而避免在接下來的遠端 Review 中獲得一個較低的得分。後者則是爲了生成相關報表,爲項目管理人員跟蹤項目質量提供依據。在不少大公司裏,這也是開發者們績效的參考之一。

剩下的就是一些膠水步驟了,如何讓過程更自動化,就是膠水步驟要作的事。例如利用 WebHook 自動觸發遠端 Review,利用 Git 的鉤子進行增量校驗而不是全量校驗等。這些咱們放在後面聊,先來看看本地校驗的流程。

本地 Review

在本地 Review 環節,開發者只須要像往常同樣按下 CMD + B,而後只要靜靜地等待進度條讀完,滿屏的⚠️就會精確地指示出某一行的代碼違反了哪條規則。此時開發者就能夠根據代碼規範進行對應修改。

從按下按鍵到產生警告主要發生了這麼幾件事情:

  • 生成 compile_commands.json 文件
  • OCLint 讀取相關的 Rules,逐個掃描 compile_commands.json 中的 .m 文件
  • OCLint 將生成的報告展現在 Xcode 上

實現本地 Review 的核心就是 OCLint 和 compile_commands.json文件

OCLint

工欲善其事,必先利其器

OCLint 是一個開源的,基於 Clang 用 C++ 編寫而成的,能夠用於 C、C++ 和 Objective-C 的靜態代碼分析器。它能夠在掃描的過程當中動態加載規則文件(Rules),所以能夠實現很是靈活的,高度可自定義的代碼分析方案。它幾乎能夠和大多數系統無縫集成,例如 Cmake、Bear、xcodebuild、xctool、Xcode、xcpretty、Jenkins CI、Travis CI 等。你能夠在這裏找到如何將其和 Xcode 配合使用。

最新版本的 OCLint 已經自帶了 71 條 Rules,基本上都是先人寶貴的經驗,好比這條禁用 goto 語句的 Rule,就是來源於 Edsger W. Dijkstra 1968 年的一篇手稿

這 71 條 Rules 已經能夠幫助咱們避免一部分因書寫習慣和語言誤區而致使的問題,可是對於有完整編碼規範的公司來講顯然是不夠的。咱們必需要本身開發 Rules。

幸運的是,OCLint 已經爲咱們準備好了一切。

OCLint 提供了 Clang 和 AST (Abstract Syntax Tree) 的一層封裝,使咱們沒必要對抽象語法樹進行解析,只須要專一規則相關的邏輯開發便可。從其提供的接口中咱們能夠很明顯地看出這一點。

// 遇到一元操做符
bool VisitUnaryOperator(UnaryOperator *node)

// 遇到二元操做符
bool VisitBinaryOperator(BinaryOperator *node)

// 遇到 Objective-C 的函數聲明
bool VisitObjCMethodDecl(ObjCMethodDecl *node)  

 

在開發好相關的規則後,打包成 dylib,就能夠在分析的時候加載咱們本身的 Rule 了

compile_commands.json

compile_commands.json 是 Clang 定義的一個規範,裏面存放了一組工做目錄、目標文件、須要被執行的命令,幫助相關工具能夠獨立於編譯系統來將源代碼文件轉換爲 AST 並作對應的事。

看文件內容會更直觀一些:

[
{
  "directory": "/path/to/project/", 
  "command": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x ...", 
  "file": "/path/to/project/XXXViewController.m"
},
...
]

 

OCLint 能夠根據 compile_commands.json 中的內容,批量檢查源代碼文件。

xcpretty

還有一個點須要關注的是,如何生成 compile_commands.json 文件?

最便捷的方式是使用 oclint-xcodebuild 來生成。首先,利用xcodebuild 生成 xcodebuild.log 文件。

xcodebuild | tee xcodebuild.log

而後利用 oclint-xcodebuild 生成 compile_commands.json

oclint-xcodebuild

截至 Xcode 8.1,這種作法能夠正確生成 json 文件。因爲 OCLint 團隊已經聲稱再也不維護 oclint-xcodebuild , 所以可能在將來的某個 Xcode 版本中這個方法將再也不適用。

另外一個推薦的方法是利用 xcpretty 。

xcpretty 能夠一句話生成 json 文件。

xcodebuild | xcpretty -r json-compilation-database --output /path/to/compile_commands.json

使用本地 Review

瞭解了這些工具後就很容易明白本地自動化 Code Review 是如何工做的,使用方式也很是容易理解了:

  1. 首先在電腦本地安裝好 OCLint 並拿到公司自定義的 Rules 文件 
  2. 在 Xcode 上配置好工程
  3. build 工程,等待結果顯示在 Xcode 上。

附一個咱們團隊的配置腳本供參考:

source ~/.bash_profile  
cd ${SRCROOT}  
xcodebuild clean  
xcodebuild | tee xcodebuild.log  
oclint-xcodebuild  
oclint-json-compilation-database \  
-e Vendor \
-e Pods \
-- \
-max-priority-1 100000 \
-max-priority-2 100000 \
-max-priority-3 100000 \
-report-type xcode \
-R /path/to/rules

 

遠端 Review

 

遠端 Review 和 本地 Review 大致類似,區別在與引用構建的腳本的對象從 Xcode 變成了 Jenkins CI ,報告的展現者從 Xcode 變成了 SonarQube 。其流程是這樣的:

工程師經過 git push 提交代碼 → Web Hook 觸發 Jenkins 構建 → OCLint 掃描代碼生成PMD格式報告 → Sonar-runner 讀取報告並展示到 SonarQube。

CI 環境

爲了實現遠端 Review ,服務端必須首先有一套 CI 環境。鑑於 iOS 的特殊性,服務器必須是 macOS 系統。CI 咱們直接選擇開源的 Jenkins,質量管理平臺則選用開源的 SonarQube。Jenkins 大名鼎鼎你們都很是熟悉了,SonarQube 則相對少的人瞭解。

SonarQube 是一個質量管理平臺,在 SonarQube 上,你能夠看到一個項目的代碼行數、文件數量、代碼重複率、違反的代碼規範、技術債時間等等指標。SonarQube 對 Java 的支持極度友好,提供了 SonarScanner 能夠直接對 Java 源代碼進行掃描。Objective-C 就沒有這麼幸運了。雖然 SonarQube 也提供了 Objective-C 的報告展現的支持,但靜態分析仍是得依靠 OCLint 。

Sonnar-Runner

咱們在 Jenkins 上運行 OCLint 生成了報告。須要一箇中間人將報告解析成 SonarQube 能夠理解的格式並傳輸到 SonarQube 平臺。這個中間人就是 Sonnar-Runner。Sonnar-Runner 在咱們的系統中也僅僅扮演這個搬運工的角色。你能夠從這裏瞭解到如何在 Jenkins 上安裝和使用 Sonnar-Runner。

Sonnar-Runner 只能解析 PMD 格式的報告,所以咱們在使用 OCLint 分析代碼後,須要將報告格式輸出爲 PMD 格式

oclint -report-type pmd -o ./report.xml

Rules in Sonar

SonarQube 有一套規則,將代碼問題按照嚴重程度分爲 5 個等級,不一樣等級的問題會以不一樣權重影響到項目質量評分。這套規則和 OCLint 生成的報告中的 Rule name 必需要一一對應,SonarQube 才能正確將報告中的問題歸類並評分。

若是你使用 OCLint 原生的 Rules 來檢查代碼,只須要在 SonarQube 上安裝 SonarQube Plugin for Objective C 插件,相關的報告就會被正確識別了。

若是是使用了自行開發的 Rules ,只須要 Clone 上述插件,並在profile-oclint.xml 和 rules.txt 中添加相關的 rule name ,而後打包並將這個插件安裝到 SonarQube 上便可。

舉個例子:

當咱們用自行開發的 Rule 檢查完代碼後,生成了report.xml,內容以下:

<?xml version="1.0" encoding="UTF-8"?>  
<pmd version="oclint-0.11">  
    <file name="/path/to/TerribleCode.m">
        <violation rule="binary operator space (HT_iOS_Coding_style 2.8)" begincolumn="9" endcolumn="157" beginline="73" endline="73" priority="3" ruleset="HT_iOS_rules" >
            多元運算符和他們的操做數之間至少須要一個空格
        </violation>
    </file>
</pmd>

 

其中 binary operator space (HT_iOS_Coding_style 2.8) 是咱們定義的錯誤rule name。在 SonarQube 上,也必須對應有這麼一條 rule 的 name,才能正確識別這個錯誤。

此時咱們只須要在上述插件的 rules.txt 中添加一段

binary operator space (HT_iOS_Coding_style 2.8)  
----------

Summary:多元運算符和他們的操做數之間至少須要一個空格。 

Severity: 2  
Category: Hengtian iOS Coding Standard  

 

在上述插件的 profile-oclint.xml 中添加另一段代碼

 <rule>
      <repositoryKey>OCLint</repositoryKey>
      <key>binary operator space (HT_iOS_Coding_style 2.8)</key>
    </rule>

 

而後將這個插件打包並安裝到 SonarQube 上,SonarQube 就能夠正確識別咱們的問題並分類了。

使用遠端 Review

在使用前,必定要確保你的 macOS 服務器已經安裝好了最新版的 Xocde、OCLint、Jenkins、sonnar-runner,安裝好 Jenkins 的相關插件,並將自定義的 Rule 放置在服務器上(若是有的話)。

檢查並生成報告

在 Jenkins 上新建工程並配置好Git、構建觸發器等其餘內容。在構建步驟中添加一步 Execute Shell ,填入下述腳本

cd YourProjectDir  
xcodebuild clean  
xcodebuild -workspace MyProject.xcworkspace -scheme HTMarket -sdk iphonesimulator | tee xcodebuild.log | xcpretty  
oclint-xcodebuild  
oclint-json-compilation-database -e Pods \  
-v \
-- \
-max-priority-1 100000 \
-max-priority-2 100000 \
-max-priority-3 100000 \
-report-type pmd \
-R /path/to/diy-rules \
-o /path/to/report.xml 

 

腳本大體和本地 Review 一致,有三個地方須要注意一下。

  1. xcodebuild 命令添加了 -sdk iphonesimulator參數,以免 build 須要 Code Sign 的問題。 
  2. -report-type pmd 輸出格式必須爲 pmd 格式 
  3. -o /path/to/report.xml 注意輸出報告的路徑,下一步sonnar-runner 讀取時會用到。

讀取到 SonarQube

在上一步的下方再添加一步 Invoke Standalone SonarQube Analysis,選擇好你的 sonnar-runner。並在 Analysis Properties 中添加以下配置:(若是沒有這一項,你可能須要安裝 SonarQube 相關的插件。)

sonar.projectKey=YOUR_PROJECT_NAME  
sonar.projectName=YOUR_PROJECT_NAME  
sonar.projectVersion=1.0  
sonar.language=objc  
sonar.projectDescription=YOUR_PROJECT_DESCRIPTION

# Path to source directories 
sonar.sources=/path/to/source/directories

# Xcode project configuration (.xcodeproj or .xcworkspace)
# -> If you have a project: configure only sonar.objectivec.project
# -> If you have a workspace: configure sonar.objectivec.workspace and sonar.objectivec.project
# and use the later to specify which project(s) to include in the analysis (comma separated list)
sonar.objectivec.project=YOUR_PROJECT_NAME.xcodeproj  
sonar.objectivec.workspace= YOUR_PROJECT_NAME.xcworkspace

# Scheme to build your application
sonar.objectivec.appScheme=YOUR_PROJECT_NAME

sonar.sourceEncoding=UTF-8

# OCLint report generated by run-sonar.sh is stored in sonar-reports/oclint.xml
# Change it only if you generate the file on your own
 sonar.objectivec.oclint.report=YOUR_REPORT_FILE_PATH

 

注意看註釋並修改 YOUR_PROJECT_NAME 、YOUR_PROJECT_DESCRIPTION、和 YOUR_REPORT_FILE_PATH爲你項目的值。

一切順利的話,在 Jenkins 上當即構建,你就能夠在你的 Sonar 平臺上看到代碼質量報告了。

配合好構建觸發器 和 Git 平臺的 WebHook 功能,就能夠在開發提交代碼或者合併分支等關鍵點自動觸發構建了。

Troubleshooting

爲何生成的 compile_commands.json 爲空

檢查 log 是否爲空,若是 log 爲空則表明 build 失敗。排除失敗緣由後便可正常生成。

Jenkins 構建遇到了以下問題

❌  Code signing is required for product type 'Application' in SDK 'iOS 10.0'

遇到這樣的狀況,是由於構建了 Release 版本,且項目在 Xcode8+ 上開啓了 Automatic Code Sign。解決方法以下:

  1. 若是隻須要檢查代碼規範,則在 xcodebuild 命令後添加 -sdk iphonesimulator 參數指明以 Debug 方式構建便可。 
  2. 若是但願構建 Release 版本,那麼關閉自動簽名,在 CI 系統上手動配置證書和Proversion Profile。或者保留自動簽名,參考這個回答用 sed 命令在構建前修改相關配置。

參考連接

 

 

 

 

  • [iOS 持續集成 - 自動化單元測試]
  • [iOS 持續集成 - 自動化打包與分發]
相關文章
相關標籤/搜索