iOS 开发在通过这几年的野蛮生长以后,慢慢地趋于稳定。不管开发语言是 Objective-C 仍是 Swift,工程类型是 Hybird 仍是原生,开发思想是 OOP 仍是函数式,随着项目逐渐变大都在面临相同的问题: 测试、发布等重复性工做占了很大一部分时间,回归成本愈来愈高。持续集成不可避免地被提上了日程。html
本文主要阐述 iOS 下的持续集成,以目标、内容、流程、工具入手,但愿能够为你们描绘一幅 iOS 持续集成的蓝图。这可能不是一篇可让你 Step by Step 跟着作的文章,希望能够在你脑海中创建相关概念,以便在实操时走对方向。node
咱们会在后面几篇内容中,详细阐述 是什么 和 如何作 。ios
对于咱们来讲,减小重复工做、提高团队效率。 对于公司来讲,省钱!git
狭义上持续集成指早集成早测试尽早早发现问题早修复,并辅以一些自动化的手段,其目标是减小修复的成本。经过其目的,咱们能够发现,其实经过自动化减小重复工做和经过早发现问题下降成本是持续集成的核心理念。所以,我把自动化 Code Review 也放到这里讲。github
iOS 下的持续集成内容包括自动化 Code Reivew、自动化单元测试、自动化打包和自动化分发。自动化 Code Review 保证团队遵照代码规范,在编码阶段减小 BUG 的产生。在人员流动较大的公司里,用同一套代码规范,也能够保证项目交接更流畅。自动化单元测试在集成阶段检测出 BUG ,以减小回归成本。自动化打包和自动化分发则是减小重复劳动,毕竟,工程师的时间就是金钱啊。objective-c
顾名思义,自动化 Code Review 既采用自动化的手段,对团队成员得代码进行 Review,以保证代码质量。从现实角度来讲,自动化的 Code Review 更多地是对代码进行静态分析,经过扫描代码并对比制定的规则,产出所须要的结果。这个所须要的结果,能够是工程整体的量化的质量报告,也能够是显示在 Xcode 中的一条警告⚠️。这取决于用户是什么角色。json
在实际实践中,通常会有两种角色会关注这份结果 -- 工程师和管理层。工程师须要在开发的过程当中及时了解代码错误,以便及时纠正。管理层须要了解工程的整体代码质量,以掌握项目的相关风险。同时,也能够做为工程师绩效的依据之一。swift
要完成这一块内容,咱们须要这些工具:xcode
经过这三者协做,咱们能够实现如下流程:bash
工程师经过 `Git Commit` 提交代码 → Web Hook 触发 `Jenkins` 构建 → `OCLint` 扫描代码生成PMD格式报告 → `Sonar-runner` 读取报告并展示到 `SonarQube`。
关于 Code Review 须要指出的是,自动化工具是有局限性的。其没法分析出较为"智能"的规则。好比说下面这条
4.7 方法名以小写字母动词做为开头
还有下面这种代码,尽管含有多个容易形成闪退的 BUG,也是能够顺利逃过度析器的眼睛的
if result?.bindPhone == "true" { let range = (result?.loginId?.startIndex.advancedBy(3))!...(result?.loginId?.endIndex.advancedBy(-5))! let phoneNumber: String? = result?.loginId?.stringByReplacingCharactersInRange(range, withString: "****") self.phoneNumLabel.text = phoneNumber! }
所以,想要达到 “保证代码质量” 的目的,还须要人工 Review 和自动化 Review 相结合。
自动化地执行单元测试,在测试失败的状况下中断集成,并通知相关人员,就是这一块工做的内容。
为此,咱们须要以下的工具:
咱们能够实现如下的流程:
`Git Merge` → Web Hook 触发 `Jenkins` 构建 → xctool 执行单元测试 → 若是失败则发邮件给相关人员。
固然,若是你公司的项目托管在 GitHub 上,业界有两个很是优秀的 Jenkins 替代产品 travis-ci 和 circle-ci。他们对 GitHub 的支持完美,且对开源项目是免费的。私有项目则须要付费试用。
在单元测试这一块,最高的成本仍是写单元测试的时间成本。脑洞大开地说,若是能实现一款工具,能够自动地为业务代码生成测试代码,那才是生产力的大大提高。
一次完整的打包,须要经历配置证书、切换环境、调整参数(构建版本号等)、Archive、导出。 根据工程的复杂程度,以上过程快则五分钟,慢则半小时。当须要频繁发版的状况下,浪费了工程师很是多的时间。
咱们其实能够利用一些工具来实现这样的一个流程:
Git Merge 到 Master(一般这意味着一个 Release)→ 触发 Jenkins 构建生成 ipa → 自动化分发。
自动分发包含如下工做:
咱们能够分别配置相关的脚本,来实现测试的分发,和发布等。
要实现这样的流程,咱们须要这些工具:
Fastlane 是由一个个小组件组成的工具,功能包括上传截图到 iTunes Connect,建立 provisioning file, 管理 TestFlight 的测试员,分发等。这个工具几乎能够用命令行来实现打包上传分发相关的全部功能,而且愈来愈有成为默认的业界持续集成标准工具的样子。就像 CocoaPods 在依赖管理方面同样。
持续集成,能够看作是经过版本控制系统、CI平台(如Jenkins)和相关工具链来完成一套工做流,以减小团队重复性工做,并保证软件能够不停地迭代而不出太大的差错。iOS 下的持续集成大致就是上述几块内容,咱们在实现的过程当中遇到了很多坑,后面的文章分块详细讲。
为了保证代码质量,Code Review 是很是重要的一环。细到*
的位置是否正确,大到代码的结构是否符合了软件开发的一些基本原则,都在这项工做的范围内。
受限于现实状况,大多数团队没有足够的时间进行 Code Review,那么只能把一部分 CR 工做交给计算机去完成了。咱们只须要定下合理的流程,用代码告诉计算机须要作什么,剩下的就交给咱们可靠的伙伴吧。
应用了自动化 Code Review 后,若是你的代码写得很差,Xcode 会表示不开心。
若是你忽略 Xcode 的心情,那么质量管理平台会默默地记录这一切。
这套东西既帮助开发们写出更高质量的的代码,也给经理们对工程质量的评估提供了一个切面的支持,同时只须要花费较少的人力维护,听起来是否是跃跃欲试了呢 : )
总体的工做流程很是简单,如图:
关键点在于本地 Review和远端 Review这两步。前者是提供给开发者一个即时的代码质量反馈,以便开发者修改,从而避免在接下来的远端 Review 中获得一个较低的得分。后者则是为了生成相关报表,为项目管理人员跟踪项目质量提供依据。在不少大公司里,这也是开发者们绩效的参考之一。
剩下的就是一些胶水步骤了,如何让过程更自动化,就是胶水步骤要作的事。例如利用 WebHook 自动触发远端 Review,利用 Git 的钩子进行增量校验而不是全量校验等。这些咱们放在后面聊,先来看看本地校验的流程。
在本地 Review 环节,开发者只须要像往常同样按下 CMD + B,而后只要静静地等待进度条读完,满屏的⚠️就会精确地指示出某一行的代码违反了哪条规则。此时开发者就能够根据代码规范进行对应修改。
从按下按键到产生警告主要发生了这么几件事情:
实现本地 Review 的核心就是 OCLint 和 compile_commands.json文件
工欲善其事,必先利其器
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 是 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 中的内容,批量检查源代码文件。
还有一个点须要关注的是,如何生成 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
了解了这些工具后就很容易明白本地自动化 Code Review 是如何工做的,使用方式也很是容易理解了:
附一个咱们团队的配置脚本供参考:
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 大致类似,区别在与引用构建的脚本的对象从 Xcode 变成了 Jenkins CI ,报告的展现者从 Xcode 变成了 SonarQube 。其流程是这样的:
工程师经过 git push
提交代码 → Web Hook 触发 Jenkins 构建 → OCLint 扫描代码生成PMD格式报告 → Sonar-runner 读取报告并展示到 SonarQube。
为了实现远端 Review ,服务端必须首先有一套 CI 环境。鉴于 iOS 的特殊性,服务器必须是 macOS 系统。CI 咱们直接选择开源的 Jenkins,质量管理平台则选用开源的 SonarQube。Jenkins 大名鼎鼎你们都很是熟悉了,SonarQube 则相对少的人了解。
SonarQube 是一个质量管理平台,在 SonarQube 上,你能够看到一个项目的代码行数、文件数量、代码重复率、违反的代码规范、技术债时间等等指标。SonarQube 对 Java 的支持极度友好,提供了 SonarScanner 能够直接对 Java 源代码进行扫描。Objective-C 就没有这么幸运了。虽然 SonarQube 也提供了 Objective-C 的报告展现的支持,但静态分析仍是得依靠 OCLint 。
咱们在 Jenkins 上运行 OCLint 生成了报告。须要一个中间人将报告解析成 SonarQube 能够理解的格式并传输到 SonarQube 平台。这个中间人就是 Sonnar-Runner。Sonnar-Runner 在咱们的系统中也仅仅扮演这个搬运工的角色。你能够从这里了解到如何在 Jenkins 上安装和使用 Sonnar-Runner。
Sonnar-Runner 只能解析 PMD 格式的报告,所以咱们在使用 OCLint 分析代码后,须要将报告格式输出为 PMD 格式。
oclint -report-type pmd -o ./report.xml
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 就能够正确识别咱们的问题并分类了。
在使用前,必定要确保你的 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 一致,有三个地方须要注意一下。
xcodebuild
命令添加了 -sdk iphonesimulator
参数,以免 build 须要 Code Sign 的问题。 -report-type pmd
输出格式必须为 pmd 格式 -o /path/to/report.xml
注意输出报告的路径,下一步sonnar-runner 读取时会用到。在上一步的下方再添加一步 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 功能,就能够在开发提交代码或者合并分支等关键点自动触发构建了。
为何生成的 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。解决方法以下:
-sdk iphonesimulator
参数指明以 Debug 方式构建便可。 sed
命令在构建前修改相关配置。