iOS 持续集成系列 - 自动化 Code Review

更新时间:2016-12-01 10:27:48 点击次数:1926次

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

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

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

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

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

流程

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

自动化 Code Review 总体流程

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

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

本地 Review

本地自动化 Code Review

在本地 Review 环节,开发者只需要像往常一样按下 CMD + B,然后只要静静地等待进度条读完,满屏的⚠️就会精确地指示出某一行的代码违反了哪条规则。此时开发者就可以根据代码规范进行对应修改。

从按下按键到产生警告主要发生了这么几件事情:

实现本地 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

远端自动化 Code 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 命令在构建前修改相关配置。

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

回到顶部
嘿,我来帮您!