Flutter 里的持续部署

通过 Flutter 持续交付的最佳实践,确保你的应用程序交付给你的 Beta 版本测试人员并能够频繁予以验证,而无需借助手动工作流程。

CI/CD 选择

#

有许多持续集成 (CI) 和持续交付 (CD) 的工具,帮助自动发布你的应用。

内置 Flutter 的多合一 (All-in-one) 选择:

#

使用 Fastlane 与现有工作流程集成

#

你可以通过下面的工具使用 fastlane:

这份指南展示了如何让设置 fastlane 以及将其集成到现有应用的测试和持续集成 (CI) 工作流当中去。更多相关的内容,请参考上面这部分的内容。

fastlane

#

fastlane 是一个开源工具套件,帮助你自动的打包正式版以及部署你的应用。

本地设置

#

建议在迁移到基于云计算的系统之前,先在本地测试其构建和部署流程。你还可以使用本地机器执行连续交付。

  1. 安装 fastlane gem install fastlanebrew install fastlane。访问 fastlane docs 以获得更多信息。

  2. 创建一个名为 FLUTTER_ROOT 的环境变量,并将其设置为 Flutter SDK 的根目录。(这是为 iOS 部署的脚本所必需的。)

  3. 创建你的 Flutter 项目,准备就绪后,确保通过如下途径构建项目:

    • Android flutter build appbundle; and
    • iOS flutter build ipa.
  4. 初始化各平台的 fastlane 项目:

    • Android:在 [project]/android 目录中,运行 fastlane init 命令。

    • iOS:在 [project]/ios 目录下,运行 fastlane init 命令。

  5. 编辑 Appfile 以确保它有应用程序的基本数据配置:

    • Android 检查在 [project]/android/fastlane/Appfile 文件中的 package_name 是否匹配在 AndroidManifest.xml 中的包名。

    • iOS 检查在 [project]/ios/fastlane/Appfile 中的 app_identifier 是否匹配 Info.plist 文件中的 bundle identifier。将相应的 apple_iditc_team_idteam_id 输入进去。

  6. 设置应用商店的本地登录凭据。

    • Android 按照 Supply setup steps 文档操作,并且确保 fastlane supply init 成功同步了你在 Google Play 商店控制台中的数据。 .json 文件与密码一样重要,切勿将其公开在任何公共源代码控制存储库。

    • iOS iTunes Connect 用户名已经存在于你的 Appfileapple_id 字段中,你需要将你的 iTunes 密码设置到 FASTLANE_PASSWORD 这个环境变量里。否则,上传到 iTunes/TestFlight时会提示你。

  7. 设置代码签名:

    • Android 参考文档 为应用签名

    • iOS 在iOS上,当你准备使用 TestFlight 或 App Store 进行测试和部署时,使用分发证书而不是开发证书进行创建和签名。

      • Apple Developer Account console 创建并下载一个分发证书。

      • 打开 [project]/ios/Runner.xcworkspace/ 在你的项目设置里选择一个分发证书。

  8. 给每个不同的平台创建一个 Fastfile 脚本。

    • Android 在 Android 上按照 fastlane Android beta deployment guide 指引操作。你可以简单的编辑一下文件,加一个名叫 upload_to_play_storelane。为了使用 flutter build 命令编译 aab,要把 apk 参数设置为 ../build/app/outputs/bundle/release/app-release.aab

    • iOS 在 iOS 上,按照 fastlane iOS beta 部署指南 指引操作。你可以指定 archive 的路径以避免重复构建。例如:

      ruby
      build_app(
        skip_build_archive: true,
        archive_path: "../build/ios/archive/Runner.xcarchive",
      )
      upload_to_testflight

你现在已准备好在本地执行部署或将部署过程迁移到持续集成(CI)系统。

在本地运行部署

#
  1. 构建发布模式的应用:

    • Android flutter build appbundle.
    • iOS flutter build ipa.
  2. 在每个平台上运行 Fastfile 脚本。

    • Android cd android 然后 fastlane [你创建的 lane 名称].

    • iOS cd ios 然后 fastlane [你创建的 lane 名称].

云构建和部署设置

#

首先,按照“本地设置”中描述的本地设置部分,确保在迁移到 Travis 等云系统之前,该过程有效。

需要考虑的主要事项是,由于云实例是短暂且不可信的,因此你不能在服务器上保留你的凭据,如 Play Store 服务帐户 JSON 或 iTunes 分发证书。

持续集成 (CI) 系统通常支持加密的环境变量来存储私有数据。你可以使用 --dart-define MY_VAR=MY_VALUE 在构建应用时传递环境变量。

采取预防措施,不要在测试脚本中将这些变量值重新回显到控制台。 在合并之前,这些变量在拉取请求中也不可用,以确保恶意行为者无法创建打印这些密钥的拉取请求。在接受和合并的 pull 请求中,请注意与这些密钥。

  1. 临时登录凭据。

    • Android 在 Android 上:

      • Appfile 中删除 json_key_file 并将其存储在 CI 系统的加密变量里。从 Fastfile 中直接读取这些环境变量。

        upload_to_play_store(
          ...
          json_key_data: ENV['<variable name>']
        )
      • 序列化你的上传密钥(例如,使用 base64)并将其另存为加密环境变量。可以可以在安装阶段在 CI 系统上对其进行反序列化

        bash
        echo "$PLAY_STORE_UPLOAD_KEY" | base64 --decode > [path to your upload keystore]
    • iOS 在 iOS 上:

      • 将本地环境变量 FASTLANE_PASSWORD 转而使用 CI 系统的加密的环境变量。

      • CI 系统需要有权限拿到你的分发证书。建议使用fastlane 的 Match 系统在不同的机器上同步你的证书。

  2. 建议每次使用 Gemfile 而不是 gem install fastlane 以避免其在 CI 系统上使用的不确定性,以确保 fastlane 依赖关系在本地和云计算机之间稳定且可重现。但是,此步骤是可选的。

    • [project]/android[project]/ios 文件夹中,创建一个 Gemfile 包含以下内容:

      source "https://rubygems.org"
      
      gem "fastlane"
    • 在两个目录中,运行 bundle update 并将两者的 GemfileGemfile.lock 文件纳入源代码管理。

    • 当你在本地运行的时候,请使用 bundle exec fastlane 而不是 fastlane

  3. 在你的仓库根目录创建一个 CI 测试脚本,例如: .travis.yml.cirrus.yml

    • 有关特定于 CI 的设置,请参见 fastlane CI 文档

    • 分开你的脚本以便能在 Linux 和 macOS 两个平台运行。

    • 在 CI 的设置阶段,执行下列内容:

      • 通过执行 gem install bundler 确保 Bundler 可用。

      • [project]/android[project]/ios 目录下分别运行 bundle install命令。

      • 确保 Flutter SDK 已经正确了设置在了 PATH 环境变量中。

      • 在 Android 平台上,请确保已经设置正确的 ANDROID_SDK_ROOT 环境变量。

      • 在 iOS 平台上,你需要为 Xcode 指定依赖 (比如: osx_image: xcode9.2)

    • 在 CI 任务的脚本阶段:

      • 根据平台的不同可以运行 flutter build appbundle 或者 flutter build ios --release --no-codesign

      • 然后执行 cd androidcd ios 命令。

      • 最后执行 bundle exec fastlane [name of the lane] 命令。

Xcode Cloud

#

Xcode Cloud 是一项为分发 Apple 平台的持续构建集成并交付,以及测试分发的服务。

需要准备的

#

自定义构建脚本

#

Xcode Cloud 可以识别 自定义的构建脚本 用于在特定阶段执行额外的任务。同时它还包含了一系列 预定义的环境变量,例如用于你 clone 仓库地址的 $CI_WORKSPACE

Post-clone 脚本

#

利用 post-clone 运行的自定义构建脚本 Xcode Cloud 按照以下说明克隆你的 Git 仓库:

ios/ci_scripts/ci_post_clone.sh 创建一个文件,然后添加下面的内容。

sh
#!/bin/sh

# Fail this script if any subcommand fails.
set -e

# The default execution directory of this script is the ci_scripts directory.
cd $CI_PRIMARY_REPOSITORY_PATH # change working directory to the root of your cloned repo.

# Install Flutter using git.
git clone https://github.com/flutter/flutter.git --depth 1 -b stable $HOME/flutter
export PATH="$PATH:$HOME/flutter/bin"

# Install Flutter artifacts for iOS (--ios), or macOS (--macos) platforms.
flutter precache --ios

# Install Flutter dependencies.
flutter pub get

# Install CocoaPods using Homebrew.
HOMEBREW_NO_AUTO_UPDATE=1 # disable homebrew's automatic updates.
brew install cocoapods

# Install CocoaPods dependencies.
cd ios && pod install # run `pod install` in the `ios` directory.

exit 0

该文件需要加入 git 仓库管理,并给予可执行权限。

$ git add --chmod=+x ios/ci_scripts/ci_post_clone.sh

工作流配置

#

Xcode Cloud workflow 定义了你工作流触发时 CI/CD 处理进程的执行步骤。

要在 Xcode 中创建一个工作流,请参考以下步骤:

  1. 选择 Product > Xcode Cloud > Create Workflow 以打开 Create Workflow 菜单。

  2. 选择工作流需要作用的生产应用,然后点击 Next 按钮。

  3. 下一步,菜单将会展示一个 Xcode 提供的默认工作流的浮层,然后可以通过点击 Edit Workflow 按钮进行定制。

变更分支

#

默认 Xcode 建议每次分支变更后都为你仓库的默认分支开始一个全新的构建。

对于你应用的 iOS 变体,你通常会希望 Xcode Cloud 在对你的 Flutter packages 修改了 lib\ 中的 Dart 或 ios\ 中的 iOS 源文件目录之后,触发你的工作流。

这可以通过使用下列文件和文件夹条件来实现:

Xcode Workflow Branch Changes

下次构建的构建版本数字

#

Xcode Cloud 对于新的工作流来说默认的构建版本数字是 1,然后在每次成功构建后递增。如果你已经在一个已有应用中,使用了一个更高的构建版本数字,你需要配置 Xcode Cloud 使用正确的构建版本数字,只需要简单通过指定 Next Build Number 用于迭代即可。

你可以在 设置 Xcode Cloud 构建下一次的构建版本数字 查看更多信息。