2015 年 4 月 6 日
关注 @segiddins几晚前,我发布了一个 CocoaPods 版本,但它无法实际下载新的 pod。希望从那个错误中吸取教训,我转向自动化,以确保团队永远不会再犯同样的错误。
在 CocoaPods 中,我们自动化了很多事情。每个 gem 存储库都有一个 Gemfile
和 Gemfile.lock
(Podfile
和 Podfile.lock
的 Bundler 等效项),以确保所有开发人员都使用完全相同的 RubyGems 依赖项版本。每个存储库都启用了 RuboCop,以确保我们保持一致的编码风格。每个存储库都设置为在 Travis CI 上运行,并将测试结果报告回 GitHub,以及向 Code Climate 发送代码覆盖率和质量信息。每个存储库都有一个 Rakefile
(Ruby 的超级 Makefile
),用于定义常见的开发任务,例如运行测试、生成数据和运行 RuboCop。那是很多自动化,对吧?是的,但还有更多。
为了开发 CocoaPods,我们不仅为不同的站点和不同的 gem 拥有许多存储库,而且还拥有两个元存储库,Rainforest 和 Strata。它们分别管理 gem 和 web 存储库,并提供一系列常用的功能,例如更新 RuboCop 配置、填充数据库和引导它们管理的所有存储库。
现在,回到失败发布的故事(CocoaPods 0.36.2,如果你好奇的话)。Rainforest 有一个可爱的 release
任务,它远远超出了 Bundler 默认发布任务,通过验证版本更改、运行 bundle install
以及 CHANGELOG
具有正确的标题。它还确保没有丢失的远程提交,并且所有规范都通过。正是最后一点让我出了问题——CocoaPods Gemfile
指向其内部依赖项的 master
分支的各种提交,即使是在发布前的最后一次规范运行中也是如此。这通常不成问题,因为我们首先发布依赖的 gem,最后以 cocoapods
gem 结束。然而,这一次,我忘记发布 cocoapods-downloader
,它负责实际下载 Pod。
在 CocoaPods 0.36.1 和 0.36.2 之间,我重构了一个内部 API 以使用数组而不是字符串,而恰好这个 API 与下载器交互。由于 CocoaPods Gemfile
指向 cocoapods-downloader
的较新提交,所以正常运行规范不会显示不兼容性。因此我发布了 0.36.2,它在 0.36.3 推出之前“破坏”了数百个用户的 CocoaPods。总而言之,这不是一场太大的危机,但它让我迫切地希望确保不再发生类似的事情。我决心加强发布任务以防止此类错误再次发生。
因此,第二天我拼凑了一些东西,以确保发布前的规范的最后一次运行仅针对已发布的 gem 运行。在 CocoaPods 团队中,我绝对属于黑客一类,但我们都相信任何类型的流程都需要自动化(因此由机器执行)才能被接受。如果没有自动化,我们随机工作在晚上和周末的分布式团队将更像一群杂乱无章的狂野编码员,而不是一支有能力构建可靠软件的团队。有很多说法试图触及自动执行简单任务的成本效益分析,但我认为当您谈论多人使用的产品时,它们都不适用:当然,我可能在一个任务上花费五个小时,但如果它能为每个用户节省三十秒,那将是净收益。
此外,自动化的优点是与操作员无关。作为一个开源项目,贡献者不断流失。我们希望展望未来,并确保我们不会将分类知识留在团队中的任何一个成员中,以便任何未来的成员都可以无缝地担任任何角色。基本上,当我说“自动化一切”时,我是认真的——当我自动化某事时,我可以把它抛在脑后,确信它会起作用。(对于那些编写单元测试的人来说,听起来很熟悉吗?)