iOS 持續整合系列 – 自動化 Code Review

發表於2016-12-07

為了保證程式碼質量,Code Review 是非常重要的一環。細到*的位置是否正確,大到程式碼的結構是否符合了軟體開發的一些基本原則,都在這項工作的範圍內。

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

應用了自動化 Code Review 後,如果你的程式碼寫得不好,Xcode 會表示不開心。

xcodec8b3b515ccf3

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

sonarc8b3b515ccf3

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

流程

整體的工作流程非常簡單,如圖:

flow-1c8b3b515ccf3

自動化 Code Review 總體流程

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

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

本地 Review

local-flow-1c8b3b515ccf3

本地自動化 Code 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) 的一層封裝,使我們不必對抽象語法樹進行解析,只需要專注規則相關的邏輯開發即可。從其提供的介面中我們可以很明顯地看出這一點。

在開發好相關的規則後,打包成 dylib,就可以在分析的時候載入我們自己的 Rule 了

compile_commands.json

compile_commands.json 是 Clang 定義的一個規範,裡面存放了一組工作目錄目標檔案需要被執行的命令,幫助相關工具可以獨立於編譯系統來將原始碼檔案轉換為 AST 並做對應的事。

看檔案內容會更直觀一些:

OCLint 可以根據 compile_commands.json 中的內容,批量檢查原始碼檔案。

xcpretty

還有一個點需要關注的是,如何生成 compile_commands.json 檔案?

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

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

截至 Xcode 8.1,這種做法可以正確生成 json 檔案。由於 OCLint 團隊已經聲稱不再維護 oclint-xcodebuild , 因此可能在未來的某個 Xcode 版本中這個方法將不再適用。

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

xcpretty 可以一句話生成 json 檔案。

使用本地 Review

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

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

附一個我們團隊的配置指令碼供參考:

遠端 Review

remote-flow-1c8b3b515ccf3

遠端自動化 Code 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 格式

Rules in Sonar

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

如果你使用 OCLint 原生的 Rules 來檢查程式碼,只需要在 SonarQube 上安裝 SonarQube Plugin for Objective C 外掛,相關的報告就會被正確識別了。

如果是使用了自行開發的 Rules ,只需要 Clone 上述外掛,並在profile-oclint.xmlrules.txt 中新增相關的 rule name ,然後打包並將這個外掛安裝到 SonarQube 上即可。

舉個例子:

當我們用自行開發的 Rule 檢查完程式碼後,生成了report.xml,內容如下:

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

此時我們只需要在上述外掛的 rules.txt 中新增一段

在上述外掛的 profile-oclint.xml 中新增另外一段程式碼

然後將這個外掛打包並安裝到 SonarQube 上,SonarQube 就可以正確識別我們的問題並分類了。

使用遠端 Review

在使用前,一定要確保你的 macOS 伺服器已經安裝好了最新版的 Xocde、OCLint、Jenkins、sonnar-runner,安裝好 Jenkins 的相關外掛,並將自定義的 Rule 放置在伺服器上(如果有的話)。

檢查並生成報告

在 Jenkins 上新建工程並配置好Git、構建觸發器等其他內容。在構建步驟中新增一步 Execute Shell ,填入下述指令碼

指令碼大致和本地 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 相關的外掛。)

注意看註釋並修改 YOUR_PROJECT_NAMEYOUR_PROJECT_DESCRIPTION、和 YOUR_REPORT_FILE_PATH為你專案的值。

一切順利的話,在 Jenkins 上立即構建,你就可以在你的 Sonar 平臺上看到程式碼質量報告了。

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

Troubleshooting

為什麼生成的 compile_commands.json 為空

檢查 log 是否為空,如果 log 為空則代表 build 失敗。排除失敗原因後即可正常生成。

Jenkins 構建遇到了如下問題

遇到這樣的情況,是因為構建了 Release 版本,且專案在 Xcode8+ 上開啟了 Automatic Code Sign。解決方法如下:

  1. 如果只需要檢查程式碼規範,則在 xcodebuild 命令後新增 -sdk iphonesimulator 引數指明以 Debug 方式構建即可。
  2. 如果希望構建 Release 版本,那麼關閉自動簽名,在 CI 系統上手動配置證照和Proversion Profile。或者保留自動簽名,參考這個回答sed 命令在構建前修改相關配置。

參考連結

相關文章