背景
近期團隊內進行了一次項目代碼警告清理工作,對項目里各種觸發警告的代碼進行優化。人工改費時費力,效率低,同時也沒有合適的清理警告的自動化工具,因此只是清理了一部分便告一段落。事后對這次工作進行總結,提出下面三個問題:
- 代碼警告對我們有什么影響?
- 為什么會有這么多的警告代碼?
- 如何避免后續開發過程的代碼質量問題?
第一個問題,喵神有句話這里引用一下
一個有節操的程序員會在乎自己的代碼的警告,就像在乎飯碗邊上有只死蟑螂那樣。 ——@onevcat
代碼警告實際帶來的風險具體有:代碼可讀性差、難以維護、Crash、邏輯錯誤等,同時也是一個團隊開發人員技術水平的體現。
代碼警告意味著編譯器發現某段代碼有風險,雖然可能對實際邏輯功能不會產生影響,但是對嚴謹的程序員來說,始終有這樣一個、甚至上萬個警告提示在那里不去管,或者簡單的關閉警告,是不應該的。
第二個問題,歸其原因,可以分為兩個方面:一方面是之前項目將警告關閉了,我們看不到大部分的代碼警告;另一方面是我們缺少代碼質量分析的機制,僅僅通過人工CodeReview的方式進行代碼審查,能發現的問題是很有限的,缺少一些工具的幫助。
第三個問題,重點是需要建立起代碼質量監控的機制,通過一些自動化的工具,完成代碼質量的分析、匯總。同時要把提升代碼質量作為一個開發團隊的日常工作任務,定期的根據分析結果,解決相關的問題,清理飯碗邊上的蟑螂。
鑒于上述總結,調研了一些工具后,發現業內常用的的比較完善的代碼質量監控平臺是SonarQube。同時我們的測試團隊也已經在使用SonarQube對公司內的項目代碼進行檢測,由于iOS的工程支持需要一些配置,還沒有跑通,所以我們配合測試團隊完成iOS項目支持SonarQube掃描,下面進行相關工作的介紹。
工具介紹
SonarQube
SonarQube是一個代碼質量監控平臺,能夠匯總各類代碼分析工具的檢測報告,從Bug
、問題代碼
、重復代碼
、單測覆蓋度
、技術債
等維度展示項目代碼的健康程度,綜合各個維度的評價,為代碼質量進行評級。
SonarQube有社區版和多種付費版本,主要差別是對語言種類的支持有差異。SonarQube同時也支持插件開發,通過插件的方式可以擴展對語言的支持,所以可以使用社區版+插件的方式支持ObjC
、Swift
的質量檢測,也是下面介紹的Sonar-Swift
的實現方式。
Sonar-Swift
Sonar-Swift是一個面向ObjC
和Swift
的開源靜態代碼分析工具集,通過SonarScanner
將SwiftLint
、OCLint
、Tailor
、Lizard
等代碼分析工具的結果提交給SonarQube,完成iOS項目的代碼質量監控。對于ObjC
的分析,由于OCLint
存在很多問題,國內有團隊在Sonar-Swift
的基礎上進行二次開發,引入了Infer
掃描工具,來替代OCLint
。
SwiftLint
SwiftLint是用于進行Swift
靜態代碼分析的工具,通過HookClang
獲取代碼的AST
數據,進行分析后,輸出報告,同時使用SourceKit
,將提示信息展示在Xcode編輯器內。SwiftLint支持自定義檢測規則,這一點為制定適用于自己團隊的開發規則比較友好。
OCLint
OCLint是用于進行ObjC
靜態代碼分析的工具,也是基于Clang
提供的工具,獲取AST
數據進行分析,輸出報告。主要工作流程如下:
- xcodebuild或者xcrun生成構建日志
- 結合
xcpretty
,輸出一個符合JSONCompilationDatabase標準的json
文件,文件包含的主要是此次構建的每個源碼文件、編譯命令和文件路徑文件路徑。 - OCLint根據該文件進行二次編譯,在二次編譯過程中,獲取
AST
數據進行分析。
OCLint
也支持自定義檢測規則,但是在大型項目的實際接入過程中,問題較多,個別文件編譯失敗時導致整體工作流程失敗,使用不夠友好。
Infer
Infer也是可以用于進行ObjC
靜態代碼分析的工具,由FaceBook
發布,開源。工作流程與OCLint
類似,也是根據構建日志進行二次編譯,輸出分析報告。比較好的一點是即使個別文件編譯失敗,Infer
仍然可以繼續執行,不會打斷整體工作流程失敗,也是我們選擇Infer
的一個主要原因。
SonarScanner
SonarScanner是SonarQube
提供的一個工具,能夠根據配置文件將指定工程及掃描報告上傳到SonarQube
平臺中??梢岳斫鉃槭且粋€數據收集器,收集、上報代碼分析的數據。
工作流程
- 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-community,docker鏡像傳送門。
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-Swift
的run-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
的腳本主要是將工作流程圖中介紹的過程整合起來,方便執行。
總結
本次著重介紹了Sonar-Swift
的整體搭建過程,一些細節問題沒有完全列出,還需要在實踐過程中,具體項目具體分析。建立起質量監控平臺不是最終目的,保證代碼質量才是我們的目標。所以后續還需要將這個平臺真正的使用起來,在項目的迭代過程中,將平臺的分析數據作為監控指標,根據分析結果不斷的修復相關問題,以此提升我們的代碼質量,同時在解決問題的過程中,也能獲得技術上的成長。