DevOps的旅程-第1部分

我最近荣幸地加入了Pillar ,这是他们DevOps团队的一个令人兴奋的新项目。 作为一个相对初级的开发人员,这始终是发现的旅程,而且就是黑桃白! ♠️

我们的任务是接受一个React-native项目,并创建一个可以利用自动化测试框架的持续集成/持续交付(CI / CD)管道。 此博客涉及过程的CD部分。 另一个博客将详细介绍管道的“测试自动化”部分。 所以我们的任务听起来很容易,呵呵。 但是为什么听到我问的为什么要建立CD / CI管道呢? 这将增加什么好处? 首先,如果您不确定这些术语的含义和区别,请查看这篇非常有用的文章(https://www.atlassian.com/continuous-delivery/ci-vs-ci-vs-cd)。

CI / CD管道有何用处? 简而言之,它使开发人员的生活更加轻松。 它节省了开发人员的时间 ,为他们提供了快速的反馈,简化了整个部署过程,包括构建应用程序, 测试应用程序,将应用程序归档在二进制存储库中以及将两者都部署到必要的测试平台。 所有这些都可以通过自动CI / CD管道来处理,从而节省了开发人员的时间。 这使开发人员感到高兴,并使他们腾出精力去做自己最擅长的事情,从而开发和创造了魔术。 ✨

因此,自动化的管道可以节省时间。 真好! 但是,那并不是它的亮点。 它的亮点在于它可以设置可靠,重复地发布代码的过程 ☀️作为开发人员的智能和能力,他们是人类。 这意味着他们会感到疲倦并可能犯错,而自动化的流程可以帮助减轻这种情况。 这是描述CI / CD的引号。 确实是一个简单的报价,但要点。

创建可重复,可靠的过程来发布软件

因此,这些是我们需要通过React Native项目实现的总体目标:

  • 依赖管理
  • 建立业务流程并发布
  • 结合以上内容并使用CircleCI自动化我们的管道

我们从一个具有许多依赖关系的React Native项目开始。 以下是针对我们项目的主要问题的简要列表:

  • 我们的应用程序需要许多独特的依赖项,例如Signal Server
  • 我们有自己的存储库,需要包含在Artifactory中存档的存储库
  • iOS特定的依存关系:cocoapods

依赖性管理问题#1:管理许多npm依赖性

要解决此问题,我们使用了CircleCIArtifactory 。 CircleCI用于预订我们的应用程序的构建。 我们使用它来开始我们的应用程序构建,正如我们稍后将要看到的,我们也使用它来关闭我们的应用程序构建。 我们通过以下方式使用Artifactory:

  • 作为私有npm注册表:我们使用它来存储作为依赖项导入到应用程序中的npm程序包,例如各种SDK
  • 作为私有Docker注册表:我们使用它来存储要部署到AWS的资产
  • 要存储一般工件:在撰写本文时,我们将其用于存储Signal Server可部署项目。

这是config.yml文件的小快照,该文件是CircleCI用来协调管道构建的配置文件。 它显示了我们如何使用CircleCI与Artifactory进行交互来管理我们的依赖关系。

让我们看一些步骤:

  • 使用Artifactory进行身份验证:此curl命令将用户名/密码发送到人工项目,检索身份验证令牌并将其存储在.npmrc文件中
  • 将注册表设置为使用Artifactory:此行将链接添加到Artifactory,以便所有npm install调用都默认为我们的Artifactory注册表。
  • 运行yarn命令以安装节点依赖项,并验证yarn.lockpackage.json中的版本是否已同步。

因此,通过使用CircleCI和Artifactory,我们能够可靠地下拉保存在注册表中的许多依赖项,并将它们安装在CircleCI框中。

依赖性管理问题2:Cocoapods

在iOS世界中,依赖项由Cocoapods管理。 CocoaPod或简称为“ pod”,是使用CocoaPods添加到您的项目中的库或框架的总称。

这是config.yml另一个快照,显示了我们如何安装应用程序所需的各种iOS依赖项( config.yml )。

让我们看一下其中的一些步骤:

  • 获取Cocoapods规格:这是一个curl命令,可下载并安装CocoaPods信息库缓存中保存的各种库和框架的规格(大约每30分钟更新一次)。
  • 安装Cocoapods:在已安装的cocoapods规格之上,这会将来自我们应用程序的所有Pod安装在CircleCI盒子上。
  • restore_cache和save_cache:我们使用这些步骤来节省时间。 我们不想每次构建应用程序时都安装cocoapods。 因此, save_cache命令将我们的Pod保存在CircleCI缓存中, restore_cache命令在下次构建应用程序时从缓存中恢复了这些保存的Pod。

好的,很好,我们能够解决依赖问题。 下一步是协调我们的构建管道,并将应用程序发布到iOS和Android的测试平台。 与依赖项一样,我们的构建流程也需要解决一些问题,引入了代码签名的黑暗世界……

构建业务流程问题1:iOS中的CodeSigning

在iOS移动世界中,代码签名很困难。 代码签名 是一项安全措施,可让iOS / Google Play识别谁签署了您的应用,确认自您签署应用以来,您的应用尚未被修改。 它涉及处理各种证书,配置文件,并且传统上,团队中的每个开发人员都需要手动设置这些证书。 但是我们需要一种自动执行代码签名的方法,以便可以在我们选择的CI工具CircleCI中使用它。 要解决此问题,我们使用了Fastlane Match。 我将在以后的博客中更详细地讨论Fastlane Match,但就目前而言,可以说Fastlane Match是成功的并节省了我们很多时间。 Fastlane Match是团队协同设计指南的实现

我们要做的第一件事是创建一个私有的Github存储库,以保存将在团队之间共享的证书配置文件 。 使用Fastlane Match,开发人员只需安装Fastlane (请参阅入门部分) ,被授予对私有GitHub存储库的访问权限,然后运行以下命令: fastlane match development--readonly 。这将下载现有证书并进行配置在开发阶段要签名的应用程序的配置文件。 以相同的方式,开发人员将需要运行以下命令来下载要在appstore阶段进行签名的应用程序的现有证书和配置文件: fastlane match appstore--readonly 。 从本质上讲,Fastlane Match生成了配置文件和证书,以方便在开发和生产阶段对我们的应用程序进行签名,并将这些文件保存在我们的私人github repsoitory中。 这些被用来在整个团队中创建一个单一的签名身份,可以很容易地下载。 成功!

构建业务流程问题2:Android中的CodeSigning

与iOS相比,整理Android中协同设计(在Android中 称为App Signing)要容易得多,但它本身也面临很多挑战。我们的第一步是选择Google推荐的Android应用开发建议的签名过程,即App签名 。 下一步是设置debug.keystorerelease.keystore 。 它涉及使用keytool(JDK cli工具)。 查看有关签署应用程序的 Android文章 了解更多信息。 这是该密钥的示例:

  keytool -genkey -v -release.keystore my-release-key.jks 
-keyalg RSA -keysize 2048-有效性10000 -alias my-alias

此命令将生成一个单个私钥作为名为release.keystore的文件,并将其保存在当前目录中(可以将其移动到您喜欢的任何位置!)。该私钥的有效期为10,000天。

生成业务流程问题3:自动化应用程序的生成

能够使用Fastlane Match和Google的应用程序签名来完成所有这些代码签名非常好,现在我们需要实际构建应用程序。 我们使用两种工具来管理此问题:用于构建iOS应用程序的Fastlane GymGradle cli工具 用于构建Android应用程序。

  • Fastlane Gym :此工具是本机iOS cli工具xcodebuild的替代方法它可能很长且难以理解。 健身工具通常在称为Fastfile的特殊Fastlane文件中使用。 Fastfile包含称为泳道的代码块,它们代表单独的作业(可以针对各种任务(例如,代码签名,构建或两者)进行自定义)。 以下代码段是正在使用的健身房的示例(来自fastlane健身房文档页面):
  build_ios_app( 
工作区:“ MyApp.xcworkspace”,
配置:“调试”,
方案:“ MyApp”,
沉默:是的,
干净:正确,
output_directory:“路径/到/目录”, #目标目录
output_name:“ my-app.ipa”, #指定 .ipa 的名称
sdk:“ iOS 11.1” #开发项目时使用的SDK
  • Gradle CLI工具:此工具可让您使用Gradle wrapper命令行工具从命令行构建android应用程序,并可从我们Android项目的根目录进行访问。 以下代码片段显示了我们如何使用此cli工具构建登台应用程序。
  ./gradlew clean assembleProdStaging —无守护程序— stacktrace — max-workers = 2 -PBUILD_NUMBER = $ CIRCLE_BUILD_NUM -x bundleProdStagingJsAndAssets 

构建业务流程问题4:从同一源代码构建多个变体

那么为什么我们要构建多个构建变体? 目标是为Android和iOS使用相同的源代码创建两个独立的应用程序(这样就不必为整个新应用程序复制代码)。 因此总共有四个构建变体。 每个构建变体都指向其自己的独立环境。 成功看起来像这样:

为此,我们必须动手使用本机源代码库。 对于iOS,我们需要在XCode IDE中进行配置,对于Android,我们需要在app / build.gradle文件中进行配置:

i0S构建变体配置步骤:

  • 在AppStore Connect上创建了单独的bundleID。 这为完全独立的iOS应用程序设置了唯一ID,该应用程序称为bulletwallet.staging。
  • 在XCode中添加了一个名为Staging.Release的登台构建配置
  • 创建了一个单独的构建方案,称为暂存
  • 在XCode中使用暂存值配置了“构建设置”部分,尤其是以下部分: 打包搜索 路径,用户定义的。
  • 在XCode中使用临时值配置了“构建阶段”部分

Android构建配置步骤:

  • 我们需要一个完全独立的Android应用程序的唯一ID,该应用程序称为columnwallet.staging在Google Play控制台上创建的单独的applicationId。
  • Android Staging应用程序需要一个新的Firebase项目,主要用于通知目的。 这产生了google-services.json的更新版本(现在包括生产和登台应用程序的详细信息)。 我们下载了此文件并将其添加到Android项目的根目录中。
  • 要从app / build.gradle文件中的Configured 3个主要部分创建带有阶段值的构建变体: signingConfigs,productFlavors,buildTypes。

建立业务流程问题5:将应用程序上载到TestFlight和Google Play商店

将我们的源代码分离出来以生产独立的应用程序是一个里程碑。 下一步是将这些应用构建版本上传到iOS和Android的Beta测试平台。 为了简化此过程,我们使用了另一个Fastlane工具Pilot 。 该工具使我们可以使用一个名为upload_to_testflight的附加关键字来扩展Fastfile。 它打包了适用于iOS(ipa文件)和Android(apk文件)的打包应用程序,并将其分别上传到Testflight和Google Play控制台。 以下是显示此用法的代码段:

的iOS

  desc“发布到TestFlight的生产” 
泳道:deploy_prod do
...
upload_to_testflight(更改日志:commit [:message],
skip_waiting_for_build_processing:是的,

结束

安卓

  desc“将新版本部署到Google Play登台测试轨道” 
泳道:deploy_staging do
...
upload_to_play_store(
跟踪:“内部”,
apk:“ app / build / outputs / apk / app-prod-staging.apk”,
json_key_data:ENV ['GOOGLE_JSON_DATA'],
package_name:“ com.pillarproject.wallet.staging”,
skip_upload_aab:是的,
skip_upload_images:是的,
skip_upload_screenshots:是

结束

我们使用CircleCI来自动化各种构建工件和应用程序的构建。 以下是stage_ios作业的摘要代码段(为清晰起见,省略了许多运行步骤),以显示我们如何配置此代码。 您将在此处看到执行本博客中已提到的各种任务的各种run步骤。 例如,名为“ Upload to TestFlight的运行步骤引用了称为deploy_staging的Fastlalne通道。 该特定通道经过了定制,以构建登台应用程序并将其上传到TestFlight。 您还将看到它们的步骤名为prepare to archive ipa filestore_artifacts 。 这些步骤将保存ipa文件,并将其存储在CircleCI的Artifacts目录中,该目录位于名为app_build的目标目录中。

  stage_ios: 
working_directory:〜/支柱
苹果系统:
xcode:“ 9.4.0”
环境:
FL_OUTPUT_DIR:输出
shell:/ bin / bash --login -o pipefailsteps:
- 查看
...
- 跑:
名称:上传到TestFlight
命令:cd ios && bundle exec fastlane deploy_staging
- 跑:
名称:宣布部署
命令:
chmod + x .circleci / announceDeployment.sh
.circleci / announceDeployment.sh“ {应用程序名称}”
“暂存TestFlight”
- 跑:
名称:准备存档ipa文件
命令:
mkdir -p ./to存档
cp ./ios/pillarwallet-staging.ipa ./toArchive
-store_artifacts:
路径:./ toArchive
目的地:app_build

CircleCI自动连接到Github,因此我们使用它来安排各种应用程序构建的触发。 CircleCI具有工作流的概念,该概念使我们能够整理流水线,以便将各种作业配置在一起。 根据CircleCI文档…

工作流程是一组用于定义作业及其运行顺序的规则。

下面是CircleCI / config.yml文件的摘录,显示了我们如何利用工作流并根据提交代码的分支对它们进行了自定义以构建应用程序。 例如,如果作业build已成功运行,则构建了stage_ios作业,并且只有在从master分支提交了代码的情况下,才能构建作业stage_ios

 工作流程: 
版本:2
build_and_deploy:
工作:
-建立
-stage_ios:
要求:
-建立
筛选器:
分支机构:
只要:
-大师
-stage_android:
要求:
-建立
筛选器:
分支机构:
只要:
-大师
-release_to_prod:
型式认证
要求:
-stage_ios
-stage_android
-prod_ios:
要求:
-release_to_prod
-prod_android:
要求:
-release_to_prod

世博会旁注…

当我们开始时,react-native应用程序使用包围我们应用程序的Expo包装器。 世博会将自己描述为构建应用程序的最快方法 。 之所以有用,有几个原因:

  • 引导程序:允许团队快速构建应用程序的基础,使它们起步。
  • 原型制作: Expo使用各种库来提高应用程序实时重新加载的速度。 这种改进的反馈使开发人员可以更有效地构建和进行更改。

但是,我们在处理世博会时遇到了一些痛点。 这里是此类问题的简短提及:

  • 性能:博览会包装器包含许多库,因此使我们的应用程序变得非常大。 事后看来,它使我们的应用程序比实际需要大40%。 因此,构建和运行我们的应用程序的速度降低了。
  • 锁定到Expo API中:使用Expo包装器时,您的应用程序可以处于3种状态之一。 1. Expo处于锁定状态,允许开发人员完全访问所有Expo库和Expo API。 但是,这会阻止使用某些React Native库/节点模块。 2.弹出ExpoKit:这允许 使用Expo API构建的应用程序和工作流程与普通的React Native应用程序相似。 这给了我们更大的控制权,但是我们仍然需要使用某些Expo API,并且仍然无法访问某些第三方库。 3.简而言之,React Native:在这里您无法访问Expo API,并退出Expo环境,但是现在您在访问第三方库时没有任何限制。
  • 发布:使用Expo构建应用程序时,我们需要使用exp publish命令捆绑源代码,这使得它可以从Expo Sever上的单个URL中获得。 但是,运行此命令会将所有代码提交到单个Expo服务器通道。 这意味着当我们构建登台时 ,它将登台代码提交给服务器,随后构建生产将覆盖Staging并保存Production 。 Expo服务器正在TestFlightGoogle Play控制台上提供已提交的代码。 可以想象,这导致无法控制Beta Testing平台上提供的内容的破坏。 有一个潜在的解决方案,称为“ 世博发布渠道”,但出于各种原因,我们决定反对。

由于上述原因,我们决定从Expo退出我们的应用程序。 我们最初将其弹出解决了某些问题的ExpoKit,然后将其弹出到普通的React Native。

为了帮助我们实现上述目标,我们使用了以下工具:

  • CircleCI :完成构建和部署应用程序的工作
  • Fastlane Match,Gym和Pilot :自动对应用程序进行签名,构建和部署到TestFlight(iOS)和Google Play Store(Android)的过程
  • Artifactory :二进制存储库,用于保存各种已编译的工件,以在多个环境中重复使用

参与该项目是一次发现之旅,它使我接触到许多新工具和技术。 它涉及到很多胡须刮擦和撞击我的头靠在墙上。 但是没有痛苦就没有收获! 而所有痛苦的收获就是进步,学习和经验 。 简要列出一些痛点…

  • 掌握Xcode (史诗般的比例的学习经验)
  • 了解如何使用React-Native构建移动应用程序并将其编译为iOS和Android各自的代码库
  • 了解所有移动应用程序如何需要通过称为CodeSigning的过程……这是一个挑战
  • 了解如何使用CircleCI之类的工具来管道应用程序
  • 使用Fastlane自动构建和部署应用程序

我希望您发现此博客有用,并且如果您遇到类似的情况,它可能会派上用场。 在本文之后,我将在博客的第2部分中进行博客。DevOps系列的旅程-第2部分。本文将重点关注管道中的“测试自动化”部分,并将包括有关Cucumber Appium的一些很棒的新知识