iOS 持續整合系列 - 自動化 Code Review
iOS 持續整合系列 - 自動化 Code Review
- iOS 持續整合 - 開篇
- iOS 持續整合 - 自動化 Code Review
- [iOS 持續整合 - 自動化單元測試]
- [iOS 持續整合 - 自動化打包與分發]
為了保證程式碼質量,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 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。解決方法如下:
- 如果只需要檢查程式碼規範,則在 xcodebuild 命令後新增
-sdk iphonesimulator
引數指明以 Debug 方式構建即可。 - 如果希望構建 Release 版本,那麼關閉自動簽名,在 CI 系統上手動配置證照和Proversion Profile。或者保留自動簽名,參考這個回答用
sed
命令在構建前修改相關配置。
相關文章
- iOS 持續整合系列 – 自動化 Code ReviewiOSView
- iOS 持續整合系列 – 開篇iOS
- 自動化專案Jenkins持續整合Jenkins
- jenkins介面、UI自動化持續整合JenkinsUI
- 持續整合(CI)、自動化構建和自動化測試--初探 .
- iOS 持續整合iOS
- vuepress與travis-cli持續整合自動化部署Vue
- 思考如何將自動化測試加入持續整合中
- iOS 持續整合方案,傻瓜式一鍵全自動化打包、上傳!—— PythoniOSPython
- Gitlab Runner實現NetCore自動化持續整合GitlabNetCore
- [譯] 自動化持續整合/持續分發,以節省更多時間編寫程式碼
- iOS持續整合(一)——fastlane 使用iOSAST
- 新夢想幹貨分享——持續整合的自動化測試
- 用 Travis CI 打造大前端持續整合和自動化部署前端
- 知物由學 | SDK API自動化測試與持續整合API
- SoapUI實踐:自動化測試、壓力測試、持續整合UI
- 3 分鐘輕鬆搭建 Ruby 專案自動化持續整合
- 本地Jenkins.war+SVN實現全自動化持續整合Jenkins
- iOS使用fastlane實現持續整合iOSAST
- [Jenkins]IOS持續整合外掛JenkinsiOS
- Jenkins+Python自動化測試持續整合詳細教程JenkinsPython
- jenkins+ant+jmeter介面自動化的持續整合測試框架JenkinsJMeter框架
- 微服務下的持續整合-Jenkins自動化部署GitHub專案微服務JenkinsGithub
- 基於 flow.ci 實現 PHP 專案自動化持續整合PHP
- 8 步搭建 Node.js+MongoDB 專案的自動化持續整合Node.jsMongoDB
- 「持續整合實踐系列 」Jenkins 2.x 構建CI自動化流水線常見技巧Jenkins
- Windows 桌面應用自動和 Jenkins 持續整合WindowsJenkins
- 持續整合、持續部署、持續交付、持續釋出
- 持續整合持續部署持續交付_持續整合與持續部署之間的真正區別
- 使用 flow.ci 實現 Android 自動化測試與持續整合Android
- iOS專案的持續整合與管理iOS
- 持續整合、持續交付、持續部署簡介
- 整合持續整合工具
- Jenkins+SVN+fir.im+BugHD搭建持續整合,實現IOS自動化打包、Bug實時動態監測JenkinsiOS
- 微服務化的基石——持續整合微服務
- 《轉載》Jenkins持續整合-自動化部署指令碼的實現《python》Jenkins指令碼Python
- 使用 Xcode Server 持續整合 & 打包測試XCodeServer
- iOS持續整合(三)——fastlane 自定義外掛iOSAST