背景
近期团队内进行了一次项目代码警告清理工作,对项目里各种触发警告的代码进行优化。人工改费时费力,效率低,同时也没有合适的清理警告的自动化工具,因此只是清理了一部分便告一段落。事后对这次工作进行总结,提出下面三个问题:
- 代码警告对我们有什么影响?
- 为什么会有这么多的警告代码?
- 如何避免后续开发过程的代码质量问题?
第一个问题,喵神有句话这里引用一下
一个有节操的程序员会在乎自己的代码的警告,就像在乎饭碗边上有只死蟑螂那样。 ——@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
的整体搭建过程,一些细节问题没有完全列出,还需要在实践过程中,具体项目具体分析。建立起质量监控平台不是最终目的,保证代码质量才是我们的目标。所以后续还需要将这个平台真正的使用起来,在项目的迭代过程中,将平台的分析数据作为监控指标,根据分析结果不断的修复相关问题,以此提升我们的代码质量,同时在解决问题的过程中,也能获得技术上的成长。