標籤:tab swift 開篇 指示 lan ext hone ati tin
iOS 持續整合系列 - 開篇前言
iOS 開發在經過這幾年的野蠻生長之後,慢慢地趨於穩定。無論開發語言是 Objective-C 還是 Swift,工程類型是 Hybird 還是原生,開發思想是 OOP 還是函數式,隨著項目逐漸層大都在面臨相同的問題: 測試、發布等重複性工作佔了很大一部分時間,迴歸成本越來越高。持續整合不可避免地被提上了議程。
本文主要闡述 iOS 下的持續整合,以目標、內容、流程、工具入手,希望可以為大家描繪一幅 iOS 持續整合的藍圖。這可能不是一篇可以讓你 Step by Step 跟著做的文章,但願可以在你腦海中建立相關概念,以便在實操時走對方向。
我們會在後面幾篇內容中,詳細闡述 是什麼 和 如何做 。
目標
對於我們來說,減少重複工作、提升團隊效率。 對於公司來說,省錢!
狹義上持續整合指早整合早測試儘早早發現問題早修複,並輔以一些自動化的手段,其目標是減少修複的成本。通過其目的,我們可以發現,其實通過自動化減少重複工作和通過早發現問題降低成本是持續整合的核心理念。因此,我把自動化 Code Review 也放到這裡講。
內容
iOS 下的持續整合內容包括自動化 Code Reivew、自動化單元測試、自動化打包和自動化分發。自動化 Code Review 保證團隊遵守代碼規範,在編碼階段減少 BUG 的產生。在人員流動較大的公司裡,用同一套代碼規範,也可以保證項目交接更流暢。自動化單元測試在整合階段檢測出 BUG ,以減少迴歸成本。自動化打包和自動化分發則是減少重複勞動,畢竟,工程師的時間就是金錢啊。
自動化 Code Review
顧名思義,自動化 Code Review 既採用自動化的手段,對團隊成員得代碼進行 Review,以保證代碼品質。從現實角度來說,自動化的 Code Review 更多地是對代碼進行靜態分析,通過掃描碼並對比制定的規則,產出所需要的結果。這個所需要的結果,可以是工程總體的量化的品質報告,也可以是顯示在 Xcode 中的一條警告??。這取決於使用者是什麼角色。
在實際實踐中,一般會有兩種角色會關注這份結果 -- 工程師和管理層。工程師需要在開發的過程中及時瞭解代碼錯誤,以便及時糾正。管理層需要瞭解工程的總體代碼品質,以掌握項目的相關風險。同時,也可以作為工程師績效的依據之一。
要完成這一塊內容,我們需要這些工具:
- Jenkins
- SonarQube
- OCLint OR SwiftLint
通過這三者協作,我們可以實現以下流程:
工程師通過 `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 是如何工作的,使用方式也非常容易理解了:
- 首先在電腦本地安裝好 OCLint 並拿到公司自訂的 Rules 檔案
- 在 Xcode 上配置好工程
- 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 一致,有三個地方需要注意一下。
xcodebuild 命令添加了 -sdk iphonesimulator參數,以避免 build 需要 Code Sign 的問題。
-report-type pmd 輸出格式必須為 pmd 格式
-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 applicationsonar.objectivec.appScheme=YOUR_PROJECT_NAMEsonar.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。解決方案如下:
- 如果只需要檢查代碼規範,則在 xcodebuild 命令後添加
-sdk iphonesimulator 參數指明以 Debug 方式構建即可。
- 如果希望構建 Release 版本,那麼關閉自動簽名,在 CI 系統上手動設定認證和Proversion Profile。或者保留自動簽名,參考這個回答用
sed 命令在構建前修改相關配置。
參考連結
- OCLint
- Jenkins
- SonarQube
- Edsger W. Dijkstra
- AST (Abstract Syntax Tree)
- json compilation database format specification
- xcpretty
- PMD
- xcode8 和 ios10 升級之後的問題集中討論帖 -- TesterHome
- [iOS 持續整合 - 自動化單元測試]
- [iOS 持續整合 - 自動化打包與分發]
iOS 持續整合