自动化 Code Review

Posted ZH952016281

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自动化 Code Review相关的知识,希望对你有一定的参考价值。

为了保证代码质量,Code Review 是非常重要的一环。细到*的位置是否正确,大到代码的结构是否符合了软件开发的一些基本原则,都在这项工作的范围内。

受限于现实情况,大多数团队没有足够的时间进行 Code Review,那么只能把一部分 CR 工作交给计算机去完成了。我们只需要定下合理的流程,用代码告诉计算机需要做什么,剩下的就交给我们可靠的伙伴吧。

应用了自动化 Code Review 后,如果你的代码写得不好,Xcode 会表示不开心。

如果你忽略 Xcode 的心情,那么质量管理平台会默默地记录这一切。

这套东西既帮助开发们写出更高质量的的代码,也给经理们对工程质量的评估提供了一个切面的支持,同时只需要花费较少的人力维护,听起来是不是跃跃欲试了呢 : )

流程

整体的工作流程非常简单,如图:

关键点在于本地 Review远端 Review这两步。前者是提供给开发者一个即时的代码质量反馈,以便开发者修改,从而避免在接下来的远端 Review 中得到一个较低的得分。后者则是为了生成相关报表,为项目管理人员跟踪项目质量提供依据。在很多大公司里,这也是开发者们绩效的参考之一。

剩下的就是一些胶水步骤了,如何让过程更自动化,就是胶水步骤要做的事。例如利用 WebHook 自动触发远端 Review,利用 Git 的钩子进行增量校验而不是全量校验等。这些我们放在后面聊,先来看看本地校验的流程。

本地 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) 的一层封装,使我们不必对抽象语法树进行解析,只需要专注规则相关的逻辑开发即可。从其提供的接口中我们可以很明显地看出这一点。

// 遇到一元操作符
bool VisitUnaryOperator(UnaryOperator *node)

// 遇到二元操作符
bool VisitBinaryOperator(BinaryOperator *node)

// 遇到 Objective-C 的函数声明
bool VisitObjCMethodDecl(ObjCMethodDecl *node)  

在开发好相关的规则后,打包成 dylib,就可以在分析的时候加载我们自己的 Rule 了

compile_commands.json

compile_commands.json 是 Clang 定义的一个规范,里面存放了一组工作目录目标文件需要被执行的命令,帮助相关工具可以独立于编译系统来将源代码文件转换为 AST 并做对应的事。

看文件内容会更直观一些:

[

  "directory": "/path/to/project/", 
  "command": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x ...", 
  "file": "/path/to/project/XXXViewController.m"
,
...
]

OCLint 可以根据 compile_commands.json 中的内容,批量检查源代码文件。

xcpretty

还有一个点需要关注的是,如何生成 compile_commands.json 文件?

最便捷的方式是使用 oclint-xcodebuild 来生成。首先,利用xcodebuild 生成 xcodebuild.log 文件。

xcodebuild | tee xcodebuild.log  

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

oclint-xcodebuild  

截至 Xcode 8.1,这种做法可以正确生成 json 文件。由于 OCLint 团队已经声称不再维护 oclint-xcodebuild , 因此可能在未来的某个 Xcode 版本中这个方法将不再适用。

另一个推荐的方法是利用 xcpretty 。

xcpretty 可以一句话生成 json 文件。

xcodebuild | xcpretty -r json-compilation-database --output /path/to/compile_commands.json  

使用本地 Review

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

  1. 首先在电脑本地安装好 OCLint 并拿到公司自定义的 Rules 文件 
  2. 在 Xcode 上配置好工程
  3. build 工程,等待结果显示在 Xcode 上。

附一个我们团队的配置脚本供参考:

source ~/.bash_profile  
cd $SRCROOT  
xcodebuild clean  
xcodebuild | tee xcodebuild.log  
oclint-xcodebuild  
oclint-json-compilation-database \\  
-e Vendor \\
-e Pods \\
-- \\
-max-priority-1 100000 \\
-max-priority-2 100000 \\
-max-priority-3 100000 \\
-report-type xcode \\
-R /path/to/rules

远端 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 格式

oclint -report-type pmd -o ./report.xml  

Rules in Sonar

SonarQube 有一套规则,将代码问题按照严重程度分为 5 个等级,不同等级的问题会以不同权重影响到项目质量评分。这套规则和 OCLint 生成的报告中的 Rule name 必须要一一对应,SonarQube 才能正确将报告中的问题归类并评分。

如果你使用 OCLint 原生的 Rules 来检查代码,只需要在 SonarQube 上安装 SonarQube Plugin for Objective C 插件,相关的报告就会被正确识别了。

如果是使用了自行开发的 Rules ,只需要 Clone 上述插件,并在profile-oclint.xml 和 rules.txt 中添加相关的 rule name ,然后打包并将这个插件安装到 SonarQube 上即可。

举个例子:

当我们用自行开发的 Rule 检查完代码后,生成了report.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>  
<pmd version="oclint-0.11">  
    <file name="/path/to/TerribleCode.m">
        <violation rule="binary operator space (HT_iOS_Coding_style 2.8)" begincolumn="9" endcolumn="157" beginline="73" endline="73" priority="3" ruleset="HT_iOS_rules" >
            多元运算符和他们的操作数之间至少需要一个空格
        </violation>
    </file>
</pmd>  

其中 binary operator space (HT_iOS_Coding_style 2.8) 是我们定义的错误rule name。在 SonarQube 上,也必须对应有这么一条 rule 的 name,才能正确识别这个错误。

此时我们只需要在上述插件的 rules.txt 中添加一段

binary operator space (HT_iOS_Coding_style 2.8)  
----------

Summary:多元运算符和他们的操作数之间至少需要一个空格。 

Severity: 2  
Category: Hengtian iOS Coding Standard  

在上述插件的 profile-oclint.xml 中添加另外一段代码

     <rule>
      <repositoryKey>OCLint</repositoryKey>
      <key>binary operator space (HT_iOS_Coding_style 2.8)</key>
    </rule>

然后将这个插件打包并安装到 SonarQube 上,SonarQube 就可以正确识别我们的问题并分类了。

使用远端 Review

在使用前,一定要确保你的 macOS 服务器已经安装好了最新版的 Xocde、OCLint、Jenkins、sonnar-runner,安装好 Jenkins 的相关插件,并将自定义的 Rule 放置在服务器上(如果有的话)。

检查并生成报告

在 Jenkins 上新建工程并配置好Git、构建触发器等其他内容。在构建步骤中添加一步 Execute Shell ,填入下述脚本

cd YourProjectDir  
xcodebuild clean  
xcodebuild -workspace MyProject.xcworkspace -scheme HTMarket -sdk iphonesimulator | tee xcodebuild.log | xcpretty  
oclint-xcodebuild  
oclint-json-compilation-database -e Pods \\  
-v \\
-- \\
-max-priority-1 100000 \\
-max-priority-2 100000 \\
-max-priority-3 100000 \\
-report-type pmd \\
-R /path/to/diy-rules \\
-o /path/to/report.xml 

脚本大致和本地 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 相关的插件。)

sonar.projectKey=YOUR_PROJECT_NAME  
sonar.projectName=YOUR_PROJECT_NAME  
sonar.projectVersion=1.0  
sonar.language=objc  
sonar.projectDescription=YOUR_PROJECT_DESCRIPTION

# Path to source directories 
sonar.sources=/path/to/source/directories

# Xcode project configuration (.xcodeproj or .xcworkspace)
# -> If you have a project: configure only sonar.objectivec.project
# -> If you have a workspace: configure sonar.objectivec.workspace and sonar.objectivec.project
# and use the later to specify which project(s) to include in the analysis (comma separated list)
sonar.objectivec.project=YOUR_PROJECT_NAME.xcodeproj  
sonar.objectivec.workspace= YOUR_PROJECT_NAME.xcworkspace

# Scheme to build your application
sonar.objectivec.appScheme=YOUR_PROJECT_NAME

sonar.sourceEncoding=UTF-8

# OCLint report generated by run-sonar.sh is stored in sonar-reports/oclint.xml
# Change it only if you generate the file on your own
 sonar.objectivec.oclint.report=YOUR_REPORT_FILE_PATH

注意看注释并修改 YOUR_PROJECT_NAME 、YOUR_PROJECT_DESCRIPTION、和 YOUR_REPORT_FILE_PATH为你项目的值。

一切顺利的话,在 Jenkins 上立即构建,你就可以在你的 Sonar 平台上看到代码质量报告了。

配合好构建触发器 和 Git 平台的 WebHook 功能,就可以在开发提交代码或者合并分支等关键点自动触发构建了。

Troubleshooting

为什么生成的 compile_commands.json 为空

检查 log 是否为空,如果 log 为空则代表 build 失败。排除失败原因后即可正常生成。

Jenkins 构建遇到了如下问题

❌  Code signing is required for product type 'Application' in SDK 'iOS 10.0'

遇到这样的情况,是因为构建了 Release 版本,且项目在 Xcode8+ 上开启了 Automatic Code Sign。解决方法如下:

  1. 如果只需要检查代码规范,则在 xcodebuild 命令后添加 -sdk iphonesimulator 参数指明以 Debug 方式构建即可。 
  2. 如果希望构建 Release 版本,那么关闭自动签名,在 CI 系统上手动配置证书和Proversion Profile。或者保留自动签名,参考这个回答用 sed 命令在构建前修改相关配置。

参考链接

以上是关于自动化 Code Review的主要内容,如果未能解决你的问题,请参考以下文章

自动化 Code Review

自动化 Code Review

使用OCLint进行Code Review

代码Bug太多?给新人Code Review头都大了?快来试试SpotBugs

从code review到Git commit log

我的天哪!字节跳动是这样做 Code Review 的