为 Capacitor 应用配置 CI/CD 可能既复杂又耗时。你需要了解这些:
新构建推荐方案:使用 Capgo Build
我们现在推荐使用 配合 Capgo CLI 使用 Capgo Build 用于原生 Capacitor 构建。 此 Fastlane 指南仅供维护现有 GitHub Actions 流水线的团队参考,但新的 iOS 构建应使用 Capgo CLI,这样您就无需自行维护 Fastlane、Xcode 运行器、证书和上传脚本。
Capgo 通过 Capgo 进行 CI/CD 构建
免去维护 Fastlane、Xcode 运行器、证书、描述文件和上传脚本的麻烦。 Capgo 构建 通过现有的 CI/CD 流水线运行已签名的原生 iOS 构建:
- 适配你的流水线:在 Web 构建完成后,通过 GitHub Actions、GitLab CI、Jenkins 或本地脚本触发 Capgo Build,并
npx cap sync. - 通过 CI secrets 签名:将 App Store Connect 密钥、证书、provisioning profile、密码和 Team ID 保存在你自己的 CI secrets 中。
- 无需维护原生 runner: Capgo Build 提供维护良好的 Apple 构建环境,因此您无需自行管理 macOS runner、Xcode 镜像或 Fastlane lanes。
- 产物与提交:下载已签名的产物用于 QA,或通过 Capgo CLI 提交发布构建。
定价
- Capgo 套餐 $12/月起
- 包含 OTA 更新和每月约 15 次 Native Build
- 额外的构建分钟数通过 credits 按分钟计费
手动配置指南
你需要做的是:
使用 Fastlane 和 GitHub Actions 及证书实现 iOS 持续交付
前提条件
继续本教程之前:
- 请确保你已安装 Fastlane 已安装 在你的开发机上。
- 请确保你已加入 iOS 开发者计划。
关于价格的重要信息

https://github.com/features/actions
该服务是“免费”, 最高可达上限,具体取决于所选机器。
我们将使用一台 macOS 机器,你可以在截图中看到其价格和限制(价格以教程编写时为准,未来可能会有变动)
了解了要求和价格后,我们继续。
注意:本文假设你已在 App Store Connect 中创建了 App。Fastlane 会复制关键信息!
你将在本教程中学到什么
本文的操作步骤
- 配合 Fastlane 使用 App Store Connect API
- 前提条件:
- 创建 App Store Connect API 密钥
- 使用 App Store Connect API 密钥
- 前提条件:
- 复制 Fastlane 文件
- 配置 GitHub Actions
1. 配合 Fastlane 使用 App Store Connect API
自 2021 年 2 月起,所有用户都必须使用双重认证或两步验证登录 App Store Connect。为 Apple ID 增加这层额外安全保护,有助于确保只有您本人能访问自己的账户。
来自 Apple 支持
要求
为了让 Fastlane 能够使用 App Store Connect API 来上传您的 App,您需要提供以下 三 项内容:
- 发行者 ID
- 密钥 ID
- Key 文件或 Key 内容
获取 App Store Connect API 密钥
要生成 Key,您必须拥有 App Store Connect 的 Admin 权限。如果您没有该权限,可以将这篇文章发给相关人员。
-
选择 用户与访问权限.

3 — 选择“集成”选项卡。

- 点击“生成 API 密钥”或“添加(+)”按钮。

- 为密钥输入一个名称。该名称仅供您参考,不属于密钥本身的一部分。

6 — 在“访问权限”下,选择该密钥的角色。密钥适用的角色与团队中用户适用的角色相同。请参阅 角色权限。我们建议选择 App 管理员.
- 点击“生成”。
无法将 API 密钥的访问权限限定到特定 App。
新密钥的名称、密钥 ID、下载链接及其他信息将显示在页面上。

您可以在此处获取全部三项必要信息。
<1> Issue ID。(APPLE_ISSUER_ID secret)
<2> Key ID。(APPLE_KEY_ID secret)
<3> 点击 “Download API Key” 下载您的 API 私钥。下载链接仅在私钥尚未下载时显示。Apple 不会保留私钥副本,因此您只能下载一次。
🔴 请妥善保管私钥。切勿分享密钥,不要将密钥存储在 code 仓库中,也不要在客户端 code 中包含密钥。
使用 App Store Connect API 密钥
为了创建用于授权的 JWT token,您需要 API Key 文件(下载的 p8 文件)、Key ID 和 Issuer ID。有多种方式可以将这些信息传递给 Fastlane。我选择使用 Fastlane 的新 action app_store_connect_api_key。您可以在 Fastlane 文档中了解其他方式。我介绍这种方法,因为我认为对于大多数支持设置环境变量的 CI 来说,这是最简单的方式。
请将下载的 p8 文件转换为 Base64,并作为 secret 存储 (APPLE_KEY_CONTENT).
base64 -i APPLE_KEY_CONTENT.p8 | pbcopy
现在我们可以使用 API 密钥通过 Fastlane 来管理 App Store Connect 了,太棒了!
2. 证书
打开 Xcode 并前往 设置 > 账户 > Apple ID > 团队 并选择你的团队。

点击 管理证书.
如果还没有创建证书,可以创建一个新证书。
点击 + 并选择 Apple Distribution

然后,你需要前往 Keychain 将证书下载为 .p12 文件。
为此,你需要打开 Keychain 并切换到 登录 钥匙串,然后点击 我的证书.

然后选择要下载的证书。(按证书日期查找)
然后右键点击证书上的私钥,并选择 导出.
选择文件格式 个人信息交换 (.p12).
这会将证书下载为 .p12 文件。
请在终端中打开该文件,并使用以下命令将其转换为 Base64:
base64 -i BUILD_CERTIFICATE.p12 | pbcopy
这将成为你的 BUILD_CERTIFICATE_BASE64 secret。此外,系统提示时请输入证书密码。该密码将成为你的 P12_PASSWORD secret。
3. 描述文件
打开 Apple Developer 并选择正确的团队。
然后,点击 +

并选择 App Store Connect.

然后,你需要选择正确的应用。请注意,不能使用通配符,否则签名会失败。

选择你之前创建的正确证书(查看过期日期,它应该与今天的日期和月份相同),然后点击 继续.

最后输入 profile 名称并点击 生成.
该名称将用于在 Fastlane 中标识描述文件,对应值为
APPLE_PROFILE_NAME.

你可以将描述文件下载为 .mobileprovision 文件。

请将描述文件转换为 Base64,并将其作为 secret 存储 (BUILD_PROVISION_PROFILE_BASE64).
base64 -i BUILD_PROVISION_PROFILE.mobileprovision | pbcopy
4. 复制 Fastlane 文件
Fastlane 是一个 Ruby 库,用于自动化常见的移动开发任务。使用 Fastlane,你可以配置自定义的 “lanes”,这些 lanes 会打包一系列 “actions”,用来执行你通常会在 Android Studio 中完成的任务。Fastlane 能做很多事,但本教程只会用到少数几个核心 actions。
在 Capacitor/Ionic 项目根目录下创建 Fastlane 文件夹,并在其中添加 Fastfile:
- 文件夹:
<project-root>/fastlane/ - 文件:
<project-root>/fastlane/Fastfile
这与 ... 同级 package.json, capacitor.config.*,以及 ios/ 文件夹。不要将其创建在 ios/App/.
platform :ios do
desc 'Export ipa and submit to TestFlight'
lane :beta do
keychain_info = { keychain_name: "ios-build-#{Time.now.to_i}.keychain", keychain_password: SecureRandom.uuid }
begin
setup_signing(keychain_info)
bump_build_number
build_app_with_signing(keychain_info)
submit_to_testflight
ensure
cleanup_keychain(keychain_info)
end
end
private_lane :setup_signing do |options|
create_keychain(
name: options[:keychain_name],
password: options[:keychain_password],
unlock: true,
timeout: 0,
lock_when_sleeps: false,
add_to_search_list: true
)
import_cert(options)
install_profile
update_project_settings
end
lane :bump_build_number do
file = File.read('../package.json')
data_hash = JSON.parse(file)
api_key = app_store_connect_api_key(
key_id: ENV['APPLE_KEY_ID'],
issuer_id: ENV['APPLE_ISSUER_ID'],
key_content: ENV['APPLE_KEY_CONTENT'],
is_key_content_base64: true,
duration: 1200,
in_house: false
)
build_num = app_store_build_number(
api_key: api_key,
app_identifier: ENV['BUNDLE_IDENTIFIER'],
live: false
)
build_num = build_num + 1
UI.message("Bumped build number to #{build_num}")
increment_build_number(
build_number: build_num,
xcodeproj: "./ios/App/App.xcodeproj",
skip_info_plist: true
)
end
private_lane :import_cert do |options|
cert_path = "#{Dir.tmpdir}/build_certificate.p12"
File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64']))
import_certificate(
certificate_path: cert_path,
certificate_password: ENV['P12_PASSWORD'] || "",
keychain_name: options[:keychain_name],
keychain_password: options[:keychain_password],
log_output: true
)
File.delete(cert_path)
end
private_lane :cleanup_keychain do |options|
delete_keychain(
name: options[:keychain_name]
)
end
private_lane :install_profile do
profile_path = "#{Dir.tmpdir}/build_pp.mobileprovision"
File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64']))
UI.user_error!("Failed to create provisioning profile at #{profile_path}") unless File.exist?(profile_path)
ENV['PROVISIONING_PROFILE_PATH'] = profile_path
install_provisioning_profile(path: profile_path)
File.delete(profile_path)
end
private_lane :update_project_settings do
update_code_signing_settings(
use_automatic_signing: false,
path: "./ios/App/App.xcodeproj",
code_sign_identity: "iPhone Distribution",
profile_name: ENV['APPLE_PROFILE_NAME'],
bundle_identifier: ENV['BUNDLE_IDENTIFIER'],
team_id: ENV['APP_STORE_CONNECT_TEAM_ID']
)
update_project_team(
path: "./ios/App/App.xcodeproj",
teamid: ENV['APP_STORE_CONNECT_TEAM_ID']
)
end
private_lane :build_app_with_signing do |options|
unlock_keychain(
path: options[:keychain_name],
password: options[:keychain_password],
set_default: false
)
build_app(
workspace: "./ios/App/App.xcworkspace",
scheme: "App",
configuration: "Release",
export_method: "app-store",
output_name: "App.ipa",
export_options: {
provisioningProfiles: {
ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME']
}
},
xcargs: "-verbose",
buildlog_path: "./build_logs",
export_xcargs: "-allowProvisioningUpdates",
)
end
private_lane :submit_to_testflight do
api_key = app_store_connect_api_key(
key_id: ENV['APPLE_KEY_ID'],
issuer_id: ENV['APPLE_ISSUER_ID'],
key_content: ENV['APPLE_KEY_CONTENT'],
is_key_content_base64: true,
duration: 1200,
in_house: false
)
pilot(
api_key: api_key,
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
notify_external_testers: false,
ipa: "./App.ipa"
)
end
end
5. 配置 Secrets
GitHub Actions 会使用你在下一步配置的仓库 Secrets。你只需要一个本地 .env 文件(如果你想在自己的机器上运行或测试 Fastlane)。
如需本地测试,请创建 <project-root>/fastlane/.env 在 ... 旁边 Fastfile。不要提交此文件。请将 fastlane/.env 其添加到你的 .gitignore 中(或确认它已被忽略)。示例如下:
APP_STORE_CONNECT_TEAM_ID=UVTJ336J2D
BUNDLE_IDENTIFIER=ee.forgr.testfastlane
# See previous section for these secrets
BUILD_CERTIFICATE_BASE64=
BUILD_PROVISION_PROFILE_BASE64=
APPLE_KEY_ID=
APPLE_ISSUER_ID=
APPLE_KEY_CONTENT=
P12_PASSWORD=
APPLE_PROFILE_NAME=
获取 APP_STORE_CONNECT_TEAM_ID
前往 开发者中心 并向下滚动到 Membership details 部分。
Team ID 是您需要设置的值 APP_STORE_CONNECT_TEAM_ID secret。
获取 BUNDLE_IDENTIFIER
- 打开 Xcode
- 双击
App在项目导航栏中 - 然后点击该标签页
Signing and Capabilities - 复制
Bundle identifier的值。这是你需要在BUNDLE_IDENTIFIERsecret。
6. 构建处理
在 GitHub Actions 中, 费用按你运行 CI/CD 工作流所用的 分钟数计算。根据我的经验,构建在 App Store Connect 中处理完大约需要 10–15 分钟。
对于私有项目,每次构建的预估成本可能高达 $0.08/分钟 x 15 分钟 = $1.2,甚至更高,具体取决于你项目的配置和依赖项。
如果你担心私有项目的成本,可以设置 skip_waiting_for_build_processing 改为 true。这样无需等待 App Store Connect 处理完构建,就能节省构建分钟数。
不过,这也有取舍——在把构建分发给用户之前,你需要手动在 App Store Connect 中更新应用的合规信息。
这个优化主要适用于构建分钟数计费的私有项目。对于公开/免费项目,构建分钟数免费,因此无需启用此设置。请参阅 GitHub 的 定价页面 以获取更多详情。
7. 设置 GitHub Actions
配置 GitHub secrets
请从以下位置复制 secrets: .env 文件,并将其粘贴到 GitHub 仓库的 secrets 中。
前往 设置 > Secrets 和变量 > 操作 > 新建仓库 secret
2. BUILD_CERTIFICATE_BASE64 - Base64 编码的证书。
3. BUILD_PROVISION_PROFILE_BASE64 - Base64 编码的 provisioning profile。
4. BUNDLE_IDENTIFIER - 您应用的 bundle identifier。
5. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID。
6. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID。
7. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Key 内容 .p8, 检查一下
8. 配置 GitHub workflow 文件
创建一个 GitHub workflow 目录。
cd .github/workflows
在 workflow 文件夹中,创建一个名为 build-upload-ios.yml的文件并添加以下内容。
name: Build source code on ios
on:
push:
tags:
- '*'
jobs:
build_ios:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v6
- name: Set Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- name: Install dependencies
id: install_code
run: npm ci
- name: Build
id: build_code
run: npm run build
- uses: actions/cache@v5
with:
path: ios/App/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Sync
id: sync_code
run: npx cap sync
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
bundler-cache: true
- uses: maierj/fastlane-action@v3.1.0
env:
APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
BUNDLE_IDENTIFIER: ${{ secrets.BUNDLE_IDENTIFIER }}
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }}
with:
lane: ios beta
- name: Upload release bundle
uses: actions/upload-artifact@v6
with:
name: ios-release
path: ./App.ipa
retention-days: 10
此 workflow 应在每次 GitHub 后触发 标签,如果需要自动打 tag,请参考 使用 GitHub actions 自动构建与发布 首先。
接着,这个 workflow 会拉取你的 NodeJS 依赖、安装它们并构建你的 JavaScript 应用。
每次你提交新的 commit,都会在 TestFlight 中构建一个 release。
你的 App 不需要使用 Ionic,只需 Capacitor 基础即可。它可以包含旧的 Cordova 模块,但应优先使用 Capacitor JS 插件。
8. 触发 workflow
创建一个 commit
提交一个 提交,你应该能在仓库中看到正在运行的 workflow。
触发 workflow
把新的 commit 推送到分支 main 或 development 来触发工作流。

几分钟后,build 应该就会出现在你的 App Store Connect dashboard 中。

9. 我能从本地机器部署吗?
可以,而且毫不费力。
你可以像往常一样使用 Xcode 来构建并签名你的 App。
相关文章
CI/CD 配置指南
- 使用 GitHub Actions 实现 Capacitor Android 自动构建 - 完成 Android CI/CD 配置
- 使用 GitHub Actions 实现自动构建与发布 - 全流程 CI/CD Pipeline 教程
- 使用 GitHub Actions 管理开发与生产构建 - 环境管理
- 使用 Match 自动完成 Capacitor iOS 构建 - 使用 Fastlane Match 的替代方案
其他 CI/CD 平台
- 使用 GitLab CI 构建 - GitLab 替代方案
- 使用 CodeMagic 构建 - CodeMagic 配置指南
Live Updates 与部署
- Capgo Live Updates 文档 - 为你的 App 添加 OTA 更新
- 使用 Capgo 实现 CI/CD 集成 - 在流水线中集成实时更新
感谢
本文参考了以下文章:
继续阅读:使用 GitHub actions 和证书自动 build Capacitor iOS 应用
如果你正在使用 使用 GitHub actions 和证书自动进行 Capacitor iOS build 规划 CI/CD 自动化,并将其与 Capgo CI/CD 集成 用于 Capgo CI/CD 中的产品工作流, Capgo Native Build 用于 Capgo Native Builds 中的产品工作流, Capgo 集成 用于 Capgo 集成中的产品工作流, CI/CD 集成 了解 CI/CD 集成的实现细节,以及 GitHub Actions 集成 有关 GitHub Actions 集成的实现细节。