搭建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

SonarScanner是SonarQube提供的一个工具,能够根据配置文件将指定工程及扫描报告上传到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-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-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的整体搭建过程,一些细节问题没有完全列出,还需要在实践过程中,具体项目具体分析。建立起质量监控平台不是最终目的,保证代码质量才是我们的目标。所以后续还需要将这个平台真正的使用起来,在项目的迭代过程中,将平台的分析数据作为监控指标,根据分析结果不断的修复相关问题,以此提升我们的代码质量,同时在解决问题的过程中,也能获得技术上的成长。

你可能感兴趣的:(搭建iOS代码质量监控平台 - SonarQube)