搭建iOS代碼質量監控平臺 - SonarQube

背景

近期團隊內進行了一次項目代碼警告清理工作,對項目里各種觸發警告的代碼進行優化。人工改費時費力,效率低,同時也沒有合適的清理警告的自動化工具,因此只是清理了一部分便告一段落。事后對這次工作進行總結,提出下面三個問題:

  1. 代碼警告對我們有什么影響?
  2. 為什么會有這么多的警告代碼?
  3. 如何避免后續開發過程的代碼質量問題?

第一個問題,喵神有句話這里引用一下

一個有節操的程序員會在乎自己的代碼的警告,就像在乎飯碗邊上有只死蟑螂那樣。 ——@onevcat

代碼警告實際帶來的風險具體有:代碼可讀性差、難以維護、Crash、邏輯錯誤等,同時也是一個團隊開發人員技術水平的體現。

代碼警告意味著編譯器發現某段代碼有風險,雖然可能對實際邏輯功能不會產生影響,但是對嚴謹的程序員來說,始終有這樣一個、甚至上萬個警告提示在那里不去管,或者簡單的關閉警告,是不應該的。

第二個問題,歸其原因,可以分為兩個方面:一方面是之前項目將警告關閉了,我們看不到大部分的代碼警告;另一方面是我們缺少代碼質量分析的機制,僅僅通過人工CodeReview的方式進行代碼審查,能發現的問題是很有限的,缺少一些工具的幫助。

第三個問題,重點是需要建立起代碼質量監控的機制,通過一些自動化的工具,完成代碼質量的分析、匯總。同時要把提升代碼質量作為一個開發團隊的日常工作任務,定期的根據分析結果,解決相關的問題,清理飯碗邊上的蟑螂。

鑒于上述總結,調研了一些工具后,發現業內常用的的比較完善的代碼質量監控平臺是SonarQube。同時我們的測試團隊也已經在使用SonarQube對公司內的項目代碼進行檢測,由于iOS的工程支持需要一些配置,還沒有跑通,所以我們配合測試團隊完成iOS項目支持SonarQube掃描,下面進行相關工作的介紹。

工具介紹

SonarQube

SonarQube是一個代碼質量監控平臺,能夠匯總各類代碼分析工具的檢測報告,從Bug問題代碼重復代碼單測覆蓋度技術債等維度展示項目代碼的健康程度,綜合各個維度的評價,為代碼質量進行評級。

SonarQube有社區版和多種付費版本,主要差別是對語言種類的支持有差異。SonarQube同時也支持插件開發,通過插件的方式可以擴展對語言的支持,所以可以使用社區版+插件的方式支持ObjCSwift的質量檢測,也是下面介紹的Sonar-Swift的實現方式。

Sonar-Swift

Sonar-Swift是一個面向ObjCSwift的開源靜態代碼分析工具集,通過SonarScannerSwiftLintOCLintTailorLizard等代碼分析工具的結果提交給SonarQube,完成iOS項目的代碼質量監控。對于ObjC的分析,由于OCLint存在很多問題,國內有團隊在Sonar-Swift的基礎上進行二次開發,引入了Infer掃描工具,來替代OCLint

SwiftLint

SwiftLint是用于進行Swift靜態代碼分析的工具,通過HookClang獲取代碼的AST數據,進行分析后,輸出報告,同時使用SourceKit,將提示信息展示在Xcode編輯器內。SwiftLint支持自定義檢測規則,這一點為制定適用于自己團隊的開發規則比較友好。

OCLint

OCLint是用于進行ObjC靜態代碼分析的工具,也是基于Clang提供的工具,獲取AST數據進行分析,輸出報告。主要工作流程如下:

  1. xcodebuild或者xcrun生成構建日志
  2. 結合xcpretty,輸出一個符合JSONCompilationDatabase標準的json文件,文件包含的主要是此次構建的每個源碼文件、編譯命令和文件路徑文件路徑。
  3. OCLint根據該文件進行二次編譯,在二次編譯過程中,獲取AST數據進行分析。

OCLint也支持自定義檢測規則,但是在大型項目的實際接入過程中,問題較多,個別文件編譯失敗時導致整體工作流程失敗,使用不夠友好。

Infer

Infer也是可以用于進行ObjC靜態代碼分析的工具,由FaceBook發布,開源。工作流程與OCLint類似,也是根據構建日志進行二次編譯,輸出分析報告。比較好的一點是即使個別文件編譯失敗,Infer仍然可以繼續執行,不會打斷整體工作流程失敗,也是我們選擇Infer的一個主要原因。

SonarScanner

SonarScannerSonarQube提供的一個工具,能夠根據配置文件將指定工程及掃描報告上傳到SonarQube平臺中。可以理解為是一個數據收集器,收集、上報代碼分析的數據。

工作流程

Sonar-Swift
Sonar-Swift的工作流程如上圖所示,分為以下幾個步驟:

  • xcodebuild構建工程,輸出xcodebuild.log
  • xcpretty處理xcodebuild.log,輸出compile_commands.json
  • infer處理compile_commands.json,輸出report.json
  • swiftlint處理swift文件,輸出swiftlint.txt
  • lizard處理ObjC文件,輸出lizard-report.xml
  • Sonar-Scanner收集report.json、swiftlint.txt、lizard-report.xml上報到SonarQube

搭建過程

上面介紹了相關工具及整體的工作流程,接下來具體記錄下在搭建平臺的步驟及相關配置。

SonarQube服務

首先我們需要搭建起SonarQube服務,官網提供多種搭建方式,我們選擇使用Docker一鍵安裝,省心省力。兼容性上需要注意兩點:

  • SonarQube目前不支持M1的芯片設備,Docker部署失敗
  • SonarQube 9.x版本不支持Sonar-Swift插件,服務啟動失敗

鑒于上述兩個問題,我們使用的是基于Intel芯片的Mac設備,SonarQube的版本是8.9.2-communitydocker鏡像傳送門

docker pull sonarqube:8.9.2-community

部署成功后,需要安裝Sonar-Swift插件,下載插件jar包,復制到docker上sonarqube的服務目錄下:

docker cp tal-sonar-swift-plugin-1.5.0.jar sonarqube:/opt/sonarqube/extensions/plugins/

啟動sonarqube服務,默認的端口是9000,本地打開localhost:9000即可看到SonarQube的頁面,默認賬號密碼都是admin,首次登錄后會提示更新密碼。

服務搭建好后,在平臺上創建一個工程,支持通過GitLab等方式,我們選擇通過手工的方式創建,完成提示步驟后即可創建工程。工程創建好后,可以進行一些自定義的設置,根據項目的實際需要,過濾不在監控范圍內的目錄、選擇Quality Profiles,在Quality Profiles里,可以看到infer的選擇,設置為默認選項即可。

參考配置

配置路徑時有兩個地方需要注意下:

  • 使用正則表達式來過濾,如果實際使用過程中有一些文件被忽略掉了,可以驗證下正則表達式是否正確。
  • 在確認正則沒問題的情況下,還是有文件沒有上報上來,可以再確認下想要上報分析的文件是不是在項目的.gitignore列表里,SonarScanner在掃描時會將.gitignore忽略的文件一起忽略掉。

對于.gitignore忽略的文件,可以通過設置sonar.scm.exclusions.disabled=true來關閉,具體設置的地方可以在SonarQube的后臺,也可以在Sonar-Swift提供的sonar-project.properties文件中配置。

SonarScanner安裝

下載SonarScanner,在全局環境變量中設置執行路徑,以zsh為例,在~/.zshrc中增加以下代碼:

export SONAR_SCANNER_PATH=your scanner bin path
export PATH=$SONAR_SCANNER_PATH:$PATH

執行source ~/.zshrc后,命令行輸入sonar-scanner確認執行路徑是否配置成功。

sonar-scanner支持通過sonar-project.properties文件的方式配置相關屬性,該文件內可以配置sonarqube服務地址、工程名稱(SonarQube上對應的工程信息)、登錄名、密碼、登錄token等。Sonar-Swift也提供了一個默認的模版,我們可以將其放到工程目錄里,執行sonar-scanner命令時,會讀取該配置,根據配置內容上報數據。

SwiftLint安裝配置

SwiftLint的安裝可以參考其Github指導進行安裝即可。需要指出的一點是SwiftLint支持自定義規則,自定義的規則通過.swiftlin.yml文件來設置,所以在后面使用Sonar-Swift提供的腳本時,可以在腳本內進行修改,使用自己提供的配置文件目錄進行檢查。

Infer安裝配置

Infer的安裝也可以參考其官網進行安裝即可。Infer執行時的一些命令行選項配置可以通過在執行目錄下設置.inferconfig文件來配置。Infer并沒有在Sonar-Swift提供的腳本中設置,所以在執行Sonar-Swiftrun-sonar-swift.sh腳本中,需要增加調用infer的相關命令。

if [ "$infer" = "on" ]; then
        runCommand /dev/stdout infer run --keep-going
fi

同時在sonar-project.properties文件中,需要指定infer的報告路徑

sonar.swift.infer.report=infer-out/report.json
Sonar-Swift配置&腳本

Sonar-Swift除了提供了一個jar包插件外,還提供了一個sonar-project.properties配置模版、一個run-sonar-swift.sh腳本。在具體接入時,可以根據自己項目的實際需要,對這兩個文件進行修改。其中run-sonar-swift.sh的腳本主要是將工作流程圖中介紹的過程整合起來,方便執行。

run-sonar-swift.sh

總結

本次著重介紹了Sonar-Swift的整體搭建過程,一些細節問題沒有完全列出,還需要在實踐過程中,具體項目具體分析。建立起質量監控平臺不是最終目的,保證代碼質量才是我們的目標。所以后續還需要將這個平臺真正的使用起來,在項目的迭代過程中,將平臺的分析數據作為監控指標,根據分析結果不斷的修復相關問題,以此提升我們的代碼質量,同時在解決問題的過程中,也能獲得技術上的成長。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容