在我目前的工作过程中,我不得不将构建 Android 应用程序的工作自动化。这篇文章旨在描述我遇到的痛点,以免读者在打算这样做时浪费时间。
环境如下:
- Puppet 使基础设施自动化
- 用于 CI 服务器的 Jenkins
- 安卓项目
- 用于构建它的 Gradle 构建文件
- Robolectric 作为主要测试框架
木偶和詹金斯
我的出发点确实很合理。同事们已经自动安装了 Jenkins 服务器、所需的包——包括 Java,以及为创建工作提供了可重用的 Puppet 类。 Jenkins 作业依赖于单个
config.xml
文件,该文件是不同部分的组合。每个部分都由专用模板处理。在这一点上,我认为创建一个简单的 Gradle 作业就像在公园里散步一样,最多需要几天时间,我很快就会被分配到另一项任务。
第一步非常简单:只需更新现有的 Puppet 清单,将 Gradle 插件添加到 Jenkins。
Gradle 包装器
这个博客的普通读者知道我对 Gradle 的看法。然而,我承认无论安装的工具版本如何,保证构建都能正常工作是 Maven 所缺乏的——而且应该有。为实现这一点,Gradle 通过 JAR、shell 脚本和属性文件提供所谓的包装器机制,后者包含 Gradle ZIP 分发的 URL。这三个都需要存储在 SCM 中。
这是我麻烦的开始。必须在企业环境中下载意味着要通过代理并对其进行身份验证。最简单的选择是在作业配置中设置所有内容……包括代理凭据。然而,从安全的角度来看,这样做并不是很可靠,因为任何有权访问 Jenkins 界面或文件系统的人都可以读取这些凭据。需要另一种选择。
客户已经有一个工作的 Nexus 存储库和配置的代理。在那里上传所需的 Gradle 发行版并更新 gradle.properties 以指向它非常容易。
安卓开发工具包
Android SDK 只是一个 ZIP。我重复使用相同的策略:下载然后上传到 Nexus。此时,现有的 Puppet 脚本负责下载、解压并设置正确的权限。
然而,这一步才是真正问题的开始。 Android 开发人员知道 Android SDK 只是一个管理器:必须手动检查所需的平台和工具才能将它们下载到本地文件系统。对于 Android 开发人员来说,在他的机器上执行一个简单的步骤对于自动化来说确实是一场噩梦,尽管有一个命令行相当于通过 SDK 安装/更新包(使用
--no-ui
参数)。有关完整说明,请查看
此链接
。
谷歌工程师未能提供 2 个重要参数:
- 代理凭据 – 登录名/密码
- 接受许可协议
Web 上有很多无效的答案,最诱人的是配置文件。我发现它们都不起作用。但是,我使用
expect
命令找到了
一个创造性的解决方案
。 Expect 是一个
漂亮的命令
,它读取标准输出并相应地填充标准输入。 expect 的好处是它接受正则表达式。因此,当它要求代理登录时,您输入登录名,当它要求输入密码时,您也这样做,当它要求接受许可时,您输入“y”。这非常简单——尽管我进行了很多试验和错误才能达到预期的结果。
我最初的设计是将所有必要的 Android 包与 Puppet 一起安装,作为服务器配置的一部分。对于标准操作,例如文件创建或系统包安装,Puppet 能够确定是否需要提供, 例如 ,如果文件存在,则无需创建它,如果包已安装,则同样如此。最后,Puppet 在日志中报告它执行的每个操作。起初,我试图通过告诉 Puppet 在配置期间创建了哪些包来实现这种缓存,因为 Android SDK 为每个包创建一个文件夹。第一个问题是 Puppet 只接受单个文件夹进行验证。然后,对于某些包,没有版本信息(例如,Google Play 服务就是这种情况)。
因此,一位同事想到了将此更新从 Puppet 供应转移到每项工作的预先步骤。这解决了非幂等问题。此外,它使每个作业都可以配置运行更新。
机器人电子
至此,我以为大功告成了。不幸的是,由于图书馆 – Robolectric,情况并非如此。
当时我还不知道
Robolectric
,我只知道它是一个 Android 测试库,提供了一种无需连接物理设备即可运行测试的方法。在尝试在 Jenkins 上运行构建时,我偶然发现了一个“有趣”的问题:尽管 Roboletric 提供了一个包含依赖项的 POM,但
MavenDependencyResolver
类硬编码了从何处下载的存储库。
唯一 提供的解决方法 是扩展上述类以破解您自己的实现。我的使用了上面提到的企业 Nexus 存储库。
上传和发布任务
实施的结束相对容易。只是缺少将工件上传到 Nexus 存储库和 SCM 中的发布标签。
为了实现前者,我刚刚添加了一个自定义 Gradle 任务以从
settings.xml
(由 Puppet 提供)中获取 Nexus 设置。然后我设法让
upload
任务依赖于这个。最后,对于每一种
assemble
任务执行,我都将输出文件添加到待上传的工件集中。这样,无论构建文件中配置了什么风格,以下命令将只上传风格 XXX 和 YYY:
./gradlew assembleXXX assembleYYY upload
对于发布,它更简单:唯一需要的是设置
这个
Gradle 插件,它添加了一个
release
任务,类似于 Maven 的部署。
结论
作为后端开发人员,我已经习惯了持续集成设置,并且几乎可以肯定我可以在几天内处理好 Android CI 流程。我对 Android 生态系统在 CI 方面缺乏成熟度感到非常惊讶。每一步都很痛苦,记录很糟糕(如果有的话),解决方案看起来更像是黑客而不是其他任何东西。如果你想走这条路,你已经被警告过……我祝你好运。