Github Actions 免费构建 Flutter iOS 包
在之前的文章中,有写过一篇《在 Github 上部署一个 Flutter Web 应用》 (opens new window),每次提交代码,就会触发 Github Actions 的构建流程,完成 Flutter Web 的编译部署,省心省力,一劳永逸。那么是否也能自动够构建出 iOS 包?答案是肯定的,本文将阐述其构建步骤。
# 步骤概述
要实现构建出 Flutter iOS 包的目标,必须执行以下步骤:
- 安装 Apple 证书和配置文件
- 指定正确的 Flutter 版本
- 拉取 flutter packages
- 构建应用程序
- 使用正确的 Apple 证书对其进行签名
- 生成
.xarchive
- 从
.xarchive
中生成.ipa
- 分享给你的用户
为了完成顺利构建,我们必须提供:
- 可以苹果打包证书(
.p12
),以及导出证书的密码 - 证书对应的描述文件(
.mobileprovision
)
如果您不知道如何获取证书和描述文件,可以参考这篇文章:《iOS 打包证书制作》 (opens new window)
准备就绪,那我们开始吧!
# GitHub Actions 是什么
GitHub Actions (opens new window) 是 GitHub 的持续集成服务 (opens new window),于 2018 年 10 月推出。
大家知道,持续集成由很多操作组成,比如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions。
很多操作在不同项目里面是类似的,完全可以共享。GitHub 注意到了这一点,想出了一个很妙的点子,允许开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用。
如果你需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,整个持续集成过程,就变成了一个 actions 的组合。
基本概念
(1)workflow(工作流程):持续集成一次运行的过程,就是一个 workflow。
(2)job(任务):一个 workflow 由一个或多个 jobs 构成,含义是一次持续集成的运行,可以完成多个任务。
(3)step(步骤):每个 job 由多个 step 构成,一步步完成。
(4)action(动作):每个 step 可以依次执行一个或多个命令(action)。
快速了解 GiHub Actions,可以参阅《GitHub Actions 入门教程》 (opens new window)
我们将创建一个GitHub Action
,让您只需单击一下即可生成您的ipa
。
# 配置 GitHub Actions
让我们创建我们的第一个工作流程(workflow)!在您的项目中,您需要在.github
文件夹中创建一个workflows
文件夹,然后创建一个名为:ios-release.yml
的新文件。
ios-release.yml
文件将包含我们的第一个作业(job)build_ios
:
name: Flutter_iOS
on:
push:
branches: [master]
jobs:
build_ios:
runs-on: macos-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
当您在master
分支上推送新更改时,将触发此作业(job)。我们要做的第一步(step)是检出我们分支的代码。
# 安装苹果证书
因为项目是开源的,但是打包又需要证书、密码和描述文件,而这些我们并不想公开出去。那这个时候我们可以借助 GitHub 提供的secrets
功能,它可以安全的存放私密内容。
因为证书和描述文件都是文件类型,而 secrets 存放的是字符串,所以这个时候我们需要将文件进行base64
成字符串。具体步骤,可以阅读这篇《How to use environment variables and secrets using GitHub Actions》(https://damienaicheh.github.io/github/actions/2021/04/15/environment-variables-secrets-github-actions-en.html)。
例如:
base64 build_certificate.p12 | pbcopy
我们将定义以下 key:
.p12
证书的 key 为P12_BASE64
- 关联密码的 key 为
P12_PASSWORD
- 描述文件的 key 为
PROVISIONING_PROFILE_BASE64
下一步是安装您的Apple
证书,为此我们将使用来自社区的名为apple-actions/import-codesign-certs@v1
的操作并使用我们之前定义的secrets
- name: Install Apple Certificate
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.P12_BASE64 }}
p12-password: ${{ secrets.P12_PASSWORD }}
要对应用进行签名,我们还需要安装我们的描述文件:
- name: Install the provisioning profile
env:
PROVISIONING_CERTIFICATE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
run: |
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
echo -n "$PROVISIONING_CERTIFICATE_BASE64" | base64 --decode --output $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
正如你在上面看到的,这个脚本有 3 个操作:
- 创建
secrets
变量 - 从
secrets
导入描述文件 - 配置描述文件
# 构建 Flutter 代码
为了能够在我们的工作流程中使用 Flutter,我们需要安装它。为了实现这一目标,我们将使用社区的另一个action
:
- name: Install and set Flutter version
uses: subosito/flutter-action@v1.4.0
with:
flutter-version: "2.10.0"
我们需要添加这个 action 并指定我们要使用的 Flutter 版本。建议指定确切的 flutter 版本,而不是使用stable
作为值,以避免在发布新的stable
版本时发生潜在的重大更改。
现在我们可以为我们的应用程序拉取 packages:
- name: Restore packages
run: flutter pub get
检索到它们后,我们可以在release
模式下构建应用程序而无需对其进行签名:
- name: Build Flutter
run: flutter build ios --release --no-codesign
我们将在下一步应用之前安装的证书!
# 生成 xArchive
首先,使用 XCode 打开您的 iOS 项目并选择目标,并在Signing & Capabilities
中确保未选中Automatically manage signing
,这样我们就可以使用所需的证书对其进行签名,而无需编辑 XCode 项目。
接下来,当您签署 iOS 应用程序时,您不会签署与其关联的 pod,因此您需要在 Podfile
中指定它,如下所示:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
end
end
end
在生成xarchive
之前,我们需要解决项目的Swift
依赖项。当你有一些用Swift
编写的 Flutter iOS 插件时,这特别有用。
- name: Build resolve Swift dependencies
run: xcodebuild -resolvePackageDependencies -workspace ios/Runner.xcworkspace -scheme Runner -configuration Release
现在要创建一个xarchive
,您需要找到一些可以在您的描述文件或 Apple 证书中找到的信息:
- 开发团队标识符
- UUID,它是您的 Provisioning Profile 的标识符
- 代码签名标识
完成所有这些后,我们可以这样使用:
- name: Build xArchive
run: |
xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner -configuration Release DEVELOPMENT_TEAM=YOUR_TEAM_ID -sdk 'iphoneos' -destination 'generic/platform=iOS' -archivePath build-output/app.xcarchive PROVISIONING_PROFILE=YOUR_UUID clean archive CODE_SIGN_IDENTITY="Apple Distribution: Damien Aicheh"
# 生成 ipa
使用生成的 xarchive,我们可以将其导出为 ipa。为此,我们需要在项目中添加一个名为ExportOptions.plist
的新文件来指定导出选项。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string> <!-- app-store, ad-hoc, enterprise, development -->
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>YOUR_BUNDLE_ID</key>
<string>YOUR_UUID</string>
</dict>
</dict>
</plist>
根据您的项目配置,您可能需要向此文件添加更多选项。如果需要,您可以为项目的每个环境创建一个ExportOptions.plist
文件。
然后只需运行此命令行,您的ipa
就会生成:
- name: Export ipa
run: xcodebuild -exportArchive -archivePath build-output/app.xcarchive -exportPath build-output/ios -exportOptionsPlist ios/ExportOptions.plist
# 发布产物
要从 GitHub 界面访问生成的 ipa,让我们添加最后一个操作:
- name: Publish iOS Artefacts
uses: actions/upload-artifact@v1
with:
name: release-ios
path: build-output/ios
这将发布包含我们 ipa 的 ios 文件夹。然后,您可以将其安装在您的设备上。
# 最后
现在,您可以根据项目的上下文与用户共享您的应用程序了!您将在此 Github 中找到示例代码:flutter_best_practice (opens new window)。