|
| 1 | +iOS项目的持续集成与管理 |
| 2 | +--- |
| 3 | + |
| 4 | +> * 原文链接 : [Continuous Integration & Deployment for iOS Projects](https://medium.com/ribot-labs/continuous-integration-deployment-for-ios-projects-7358b72ca2e9) |
| 5 | +* 原文作者 : [Matt Oakes](https://medium.com/@matto1990) |
| 6 | +* [译文出自 : 开发技术前线 www.devtf.cn](http://www.devtf.cn) |
| 7 | +* 译者 : [Sam Lau](https://github.com/samlaudev) |
| 8 | +* 校对者: [Mr.Simple](https://github.com/bboyfeiyu) |
| 9 | +* 状态 : 校正完 |
| 10 | + |
| 11 | +当实现新功能时,如果忽略可维护性而引入[技术债务](http://en.wikipedia.org/wiki/Technical_debt),那将会需要延迟解决它或导致增加维护成本。 |
| 12 | + |
| 13 | +最近我们已经思考通过哪些方式来提高代码的质量: |
| 14 | + |
| 15 | +* 当代码的质量下降时,通过设置一些工具来马上提醒开发者 |
| 16 | +* 文档化一些编码规范和思考在过去的几个项目中如何避免维护性差的问题 |
| 17 | + |
| 18 | +**我将会简单地概括我们需要设置什么才能自动监控代码质量.** |
| 19 | + |
| 20 | +#基础 |
| 21 | +我们选择一个持续集成工具[Jenkins](https://jenkins-ci.org/),让它运行在一台放在我们工作室的Mac Mini。其实我不怎么喜欢Jenkins,但到目前为止,它是最稳定和最适合的工具来完成这些工作。 |
| 22 | + |
| 23 | +我们已经通过[Homebrew](https://brew.sh/)和[rbenv](https://github.com/sstephenson/rbenv)来分别安装Jenkins和Ruby,而rbenv能够为我们提供一个最新和稳定的[Ruby Gems](https://rubygems.org/)环境。有个Homebrew和Ruby Gems两个包管理工具之后,我们就几乎能够安装所有我们需要的工具,但很少会破坏与原有OS X系统更新提供的Ruby。 |
| 24 | + |
| 25 | +#单元测试 |
| 26 | +我们使用[Specta](https://github.com/specta/specta)和[Expecta](https://github.com/specta/expecta)来测试我们的iOS项目。 |
| 27 | + |
| 28 | +Specta让我们采用行为驱动开发(BDD)风格的语法来编写测试,相比于*XCTest*的语法,它更加易读。它还有一个强大的分组测试功能,在测试之前或之后运行一些代码块,这样的话,能够极大地减少重复代码。 |
| 29 | + |
| 30 | +Expecta是一个匹配器框架,我们可以在测试中使用它来创建断言。它的语法非常强大,与此同时,它比内建的*XCAssert*套件更加易读。例如: |
| 31 | + |
| 32 | +``` |
| 33 | +expect(@"foo").to.equal(@"foo"); |
| 34 | +expect(foo).notTo.equal(1); |
| 35 | +expect([bar isBar]).to.equal(YES); |
| 36 | +expect(baz).to.equal(3.14159); |
| 37 | +``` |
| 38 | +我们在开发时,通过XCode来运行测试;而使用通过Homebrew来安装的Jenkins时,会借助[XCTool](https://github.com/facebook/xctool)。XCTool是一个可代替的选择来*xcodebuild*,它能让你通过命令行的方式来非常轻松地运行测试套件和生成JUnit风格的测试报告。 |
| 39 | + |
| 40 | +``` |
| 41 | +$ xctool -workspace Project.xcworkspace -scheme Project -reporter junit:junit-report.xml test |
| 42 | +
|
| 43 | +``` |
| 44 | + |
| 45 | +这些测试报告会发布在Jenkins上,而Jenkins会使用[JUnit Plugin](https://wiki.jenkins-ci.org/display/JENKINS/JUnit+Plugin)来根据时间的推移提供单元测试结果的图表,同时会向我们显示我们的测试是否稳定。 |
| 46 | + |
| 47 | +<div align="center"> |
| 48 | + |
| 49 | +<img src="https://d262ilb51hltx0.cloudfront.net/max/1600/1*YXbjsC43-eVvMPPw2CXn6A.png"/> |
| 50 | +</div> |
| 51 | + |
| 52 | +#Pull Request测试 |
| 53 | +我们想我们的测试尽可能运行以至于如果我们破坏什么东西,我们就会马上知道。我们在[feature branches](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow/)做些修改,然后提交一个pull request到Github,那么代码就会被另一个开发者审查。只要被打开,我们就能运行所有的测试来确保没有任何东西被破坏。 |
| 54 | + |
| 55 | +当新的pull requst是开放状态时,为了管理这些,我们安装[Github Pull Request plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin)来将信息从Github发送到Jenkins。如果有任何测试失败,它将会显示在Github,然后我们就不将代码合并,直到代码被修复为止。 |
| 56 | + |
| 57 | + |
| 58 | +#代码覆盖率 |
| 59 | +我们也会用[Gcovr](http://gcovr.com/)工具来生成代码覆盖率报告,Gcovr的安装方式也是Homebrew。你需要针对main target的debug congfiguration改变两个构建设置来配置项目。将*Generate Test Coverage Files*和*Instrument Program Flow*都设置为*YES*。 |
| 60 | + |
| 61 | +<div align="center"> |
| 62 | + |
| 63 | +<img src="https://d262ilb51hltx0.cloudfront.net/max/1200/1*Aq8ClxmME0tcpf747uPguQ.png"/> |
| 64 | +</div> |
| 65 | + |
| 66 | +当我们运行单元测试来生成代码覆盖率报告时,我们需要将*OBJROOT=./build*添加到XCTool命令行的尾部。 |
| 67 | + |
| 68 | +``` |
| 69 | +$ gcovr -r . — object-directory build/Project.build/Debug-iphonesimulator/Project.build/Objects-normal/x86_64 — exclude ‘.*Tests.*’ — xml > coverage.xml |
| 70 | +
|
| 71 | +``` |
| 72 | +Gcovr输出的代码覆盖率报告也会被插件[Cobertura Jenkins plugin](https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin)发布,这个插件会提供一种可视化的方式来根据时间的推移来显示代码覆盖率。 |
| 73 | + |
| 74 | +现在我们不仅可以看到测试是否通过,还可以看到代码的测试覆盖范围。 |
| 75 | + |
| 76 | +#静态分析 |
| 77 | +在工具集中,其中一个强大并能够保持高质量的代码的工具就是静态分析工具。这些工具会扫描你的代码,然后生成一个报告,这个报告会告诉你破坏代码风格规则的代码位置。举几个规则的例子: |
| 78 | + |
| 79 | +* 未使用的变量或参数 |
| 80 | +* 长变量名,方法名或代码行 |
| 81 | +* 覆盖一个方法,但没有在这个方法调用*super* |
| 82 | +* 方法太长或方法过于复杂 |
| 83 | +* 还更多的规格... |
| 84 | + |
| 85 | +我们使用[OCLint](http://oclint.org/)静态分析工具,这个工具能够支持C,C++和Objective-C语言。OCLint通过结合XCTool使用来生成**json-compilation-database** reporter |
| 86 | +,从而提供[great integration](http://docs.oclint.org/en/dev/guide/xctool.html)特性。我们首先添加另一个reporter到我们的XCTool命令行,然后将那个report传递到OCLint来执行静态分析。 |
| 87 | + |
| 88 | +``` |
| 89 | +$ xctool -workspace Project.xcworkspace -scheme Project -reporter junit:junit-report.xml -reporter json-compilation-database:compile_commands.json test |
| 90 | +$ oclint-json-compilation-database -e Pods -report-type pmd -o oclint-pmd.xml |
| 91 | +
|
| 92 | +``` |
| 93 | +这个report以[PMD](http://pmd.sourceforge.net/)的方式来生成,然后使用[PMD Plugin](https://wiki.jenkins-ci.org/display/JENKINS/PMD+Plugin)被发布到Jenkins。有了这些插件之后,你也可以在测试失败之前,设置每个警告的优先级(底,中,高)中一些限制。最初,我们设置这些限制为低,那么只要我们引入代码,就会被提醒,从而提高代码质量。 |
| 94 | + |
| 95 | +<div align="center"> |
| 96 | + |
| 97 | +<img src="https://d262ilb51hltx0.cloudfront.net/max/1600/1*fkHdHqA_WGoWATdAs6or0A.png"/> |
| 98 | +</div> |
| 99 | + |
| 100 | + |
| 101 | +#自动部署 |
| 102 | +最后一个问题不是如何提高代码质量,而是如何节省时间。开发者通常都会将编译好的代码通过[Crashlytics](https://www.crashlytics.com/)发送到设计师来设计审查,或在sprint结束演示时发给用户。发送一个已经编译好的app通常花一个开发者的10分钟左右时间,但它需要他们来切换任务和干扰他们的心流。 |
| 103 | + |
| 104 | +最近我们已经配置一个在夜晚构建系统,它会在早上自动发送一个新版本的app给每个人。 |
| 105 | + |
| 106 | +为了做到这样,我们使用[fastlane](http://fastlane.tools/)。fastlane是一个定义**lanes**的一些操作来执行的强大工具集。现在我们有三个已经定义好的lanes,一个是用来发布给ribot开发者,一个是用来发布给在ribot的每个人,最后一个是发布给用户。 |
| 107 | + |
| 108 | + |
| 109 | +``` |
| 110 | +before_all do |lane| |
| 111 | + cert |
| 112 | + sigh |
| 113 | +end |
| 114 | +desc “Deploy a new build to ribot iOS developers over crashlytics” |
| 115 | +lane :dev do |
| 116 | + ipa |
| 117 | + crashlytics({ groups: ‘ribot-developers’ }) |
| 118 | +end |
| 119 | +desc “Deploy a new build to people at ribot over crashlytics” |
| 120 | +lane :internal do |
| 121 | + ensure_git_status_clean |
| 122 | + append_build_time |
| 123 | + ipa |
| 124 | + crashlytics({ groups: ‘ribot’ }) |
| 125 | + reset_git_repo |
| 126 | +end |
| 127 | +desc “Deploy a new build to everyone over crashlytics” |
| 128 | +lane :external do |
| 129 | + ensure_git_status_clean |
| 130 | + increment_build_number |
| 131 | + ipa |
| 132 | + crashlytics({ groups: [‘ribot’, ‘client’] }) |
| 133 | + commit_version_bump |
| 134 | + add_git_tag |
| 135 | + push_to_git_remote |
| 136 | +end |
| 137 | +after_all do |lane| |
| 138 | + clean_build_artifacts |
| 139 | +end |
| 140 | +
|
| 141 | +``` |
| 142 | + |
| 143 | +通过使用**fastlane**工具(通过Ruby Gems来安装)来运行一个lane。 |
| 144 | + |
| 145 | +``` |
| 146 | +fastlane internal |
| 147 | +
|
| 148 | +``` |
| 149 | +在开始使用所有的*lanes*之前,我们应该自动确保我们有一个有效的signing certificate和最新的provisioning profile。所有我们的配置都放在一个*.env*文件,它让我们有些默认配置,但当我们运行*fastlane*根据需要来覆盖它们。 |
| 150 | + |
| 151 | +在将来,我们会通过使用*deliver*操作来自动化app store提交过程。 |
| 152 | + |
| 153 | + |
| 154 | +#最后总结 |
| 155 | +到目前为止,我们已经尝试这些过程,并在工程中呈现出好的结果。我们期望看到只要适当地使用这些工具,就能提高代码的质量,这些报告将会让我们随着时间推移来量化代码质量。我们期待在下一个工程中适当地使用这些工具会发生什么。 |
0 commit comments