自动 __CAPGO_KEEP_0__ iOS 构建使用 __CAPGO_KEEP_1__ 动作证书
CI/CD

使用 Capacitor 和 GitHub 证书的自动 iOS 构建

如何在 5 分钟内使用 fastlane 和 GitHub Actions 为您的 iOS ionic 应用设置 CI/CD pipeline (2024)

Martin Donadieu

Martin Donadieu

内容营销专家

自动 Capacitor IOS 构建与 GitHub 动作证书

为 Capacitor 应用设置 CI/CD 可能会很复杂和耗时。以下是您需要了解的内容:

我们现在建议使用 Capgo 构建与 Capgo CLI 用于本地 Capacitor 构建。 本 Fastlane 指南保留用于维护现有 GitHub 动作管道的团队,但新 iOS 构建应使用 Capgo CLI 以免自己维护 Fastlane、Xcode 运行器、证书和上传脚本。

Capgo 构建由 Capgo 提供

跳过 Fastlane、Xcode 运行器、证书、分发配置文件和上传脚本的维护。 Capgo Build 从您的现有CI/CD管道中生成签名的原生iOS构建:

  • 与您的管道兼容: 在Capgo Build从GitHub Actions、GitLab CI、Jenkins或本地脚本中触发后,触发web构建 npx cap sync.
  • 从CI密钥中签名: 在您的CI密钥中保留App Store Connect密钥、证书、分发配置文件、密码和团队ID
  • 无原生运行器维护: Capgo Build提供维护的Apple构建环境,因此您不需要管理macOS运行器、Xcode图像或Fastlane通道
  • 工件和提交: 下载签名工件用于QA或通过Capgo CLI提交发布构建

定价

  • Capgo 计划从$12/月开始
  • 包括OTA更新和每月约15个原生构建
  • 额外的构建分钟通过信用额度按分钟计费

设置Capgo构建在CI/CD中

手动设置指南

以下是您需要做的事情:

使用Fastlane和GitHub Actions和证书的iOS持续交付

先决条件

继续教程之前:

  • 确保您在开发机器上安装了Fastlane 确保您是iOS开发者计划成员之一。 iOS使用Fastlane和__CAPGO_KEEP_0__ Actions和证书的持续交付
  • iOS使用Fastlane和__CAPGO_KEEP_0__ Actions和证书的持续交付

关于价格的重要信息

价格GitHub动作

https://github.com/features/actions

该服务是‘免费的” 取决于所选机器的限制,
我们将使用一个 macOS 机器,屏幕截图中显示了其价格和限制(截至教程创建时的价格,可能会在未来发生变化)

一旦警告了要求和价格,我们就可以继续了。

注意:在文章中,我假设您已经在 App Store Connect 中创建了应用。重要信息将由 Fastlane 复制!

本教程中您将学习的内容

步骤

  1. 使用 App Store Connect API 与 Fastlane
    • 要求:
      • 创建 App Store Connect API 密钥
      • 使用 App Store Connect API 密钥
  2. 复制 Fastlane 文件
  3. 配置 GitHub 动作

1. 使用 App Store Connect API 与 Fastlane

从 2021 年 2 月开始,所有用户必须使用两步验证或两步验证登录 App Store Connect。这种额外的安全层对于您的 Apple ID 有助于确保您是唯一可以访问帐户的人。
来自 Apple 支持

要求:

为了让Fastlane能够使用App Store Connect API上传您的应用程序,您需要提供以下内容: 三个 事项:

  1. 发行者ID
  2. 密钥ID
  3. 密钥文件或密钥内容

获取App Store Connect API密钥

要生成密钥,您必须在App Store Connect中具有管理员权限。如果您没有该权限,请将相关人员指向此文章。

  1. 登录到 App Store Connect.

  2. 选择 用户和访问权限.

App Store Connect 用户访问权限

3 — 选择 Integration 选项卡。

App Store Connect API Integration

  1. 点击生成 API 密钥或添加 (+) 按钮。

App Store Connect API 密钥创建

  1. 输入密钥的名称。该名称仅供您参考,不是密钥本身的一部分。

App Store Connect API 密钥创建名称

6 — 在 Access 下,选择密钥的角色。密钥的角色与您的团队成员的角色相同。请参阅 角色权限。我们建议选择 应用管理者.

  1. 点击生成。

一个 API 密钥的访问权限无法被限制到特定的应用程序。

新密钥的名称、密钥ID、下载链接和其他信息会在页面上显示。

App Store Connect下载密钥

您可以在这里获取所有三个必要的信息。
<1> Issue ID。 (APPLE_ISSUER_ID secret)
<2> Key ID。 (APPLE_KEY_ID secret)
<3> 点击“下载 API 密钥”以下载您的 API 私钥。下载链接只有在私钥尚未下载时才会出现。Apple 不会保留私钥的副本。因此,您只能下载一次。

🔴 请将您的私钥存储在安全的地方。您不应共享您的密钥、将密钥存储在 code 仓库中或将密钥包含在客户端 code 中。

使用 App Store Connect API 密钥

API 密钥文件(您下载的p8文件)、密钥ID和发行者ID是创建授权令牌(JWT令牌)所需的信息。有多种方式可以将此信息传递给Fastlane。 我选择使用Fastlane的新动作 app_store_connect_api_key. 您可以学习其他方法 Fastlane 文档. 我展示这个方法是因为我认为这是与大多数 CI 工作最容易的方式,

Please 将您下载的 p8 文件转换为 Base64 并将其存储为一个密钥 (APPLE_KEY_CONTENT).

base64 -i APPLE_KEY_CONTENT.p8 | pbcopy

现在我们可以使用 API 密钥来管理 App Store Connect 与 Fastlane,太棒了!

2. 证书

打开 XCode 并前往 设置 &gt; 账户 &gt; Apple ID &gt; 团队 选择您的团队。

Code 签名身份

点击 管理证书.

如果您尚未创建证书,请创建新证书。

点击 + 并选择 Apple Distribution

Apple Distribution

然后您需要前往钥匙串下载证书 .p12 file.

为了实现此功能,您需要切换到 login keychain,进入 My Certificates.

My Certificates

然后您可以选择要下载的证书(按证书的日期排序)

然后右键单击证书中的私钥,选择 Export.

选择文件格式 Personal Information Exchange (.p12).

这将下载证书为 .p12 file.

请在终端中打开文件并使用以下命令将其转换为Base64:

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

将成为您的 BUILD_CERTIFICATE_BASE64 密钥。另外,当被问及时,请提供证书的密码。这将成为您的 P12_PASSWORD 密钥。

3. 配置文件

打开 Apple Developer 并选择正确的团队。

然后创建一个新配置文件,点击 +

创建新配置文件

并选择 App Store Connect.

选择 App Store Connect

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

选择正确的应用程序

选择你之前创建的正确证书(注意查看有效期,应该与今天的日期和月份相同),并点击 继续.

选择正确的证书

最后输入配置文件的名称并点击 生成.

配置文件的名称将用于在 Fastlane 中识别配置文件,值为 APPLE_PROFILE_NAME.

生成配置文件

你可以下载配置文件 .mobileprovision 文件.

下载配置文件

请将配置文件转换为Base64并将其存储为密钥(BUILD_PROVISION_PROFILE_BASE64).

base64 -i BUILD_PROVISION_PROFILE.mobileprovision | pbcopy

4. 复制Fastlane文件

Fastlane是一款用于自动化移动开发任务的Ruby库。使用Fastlane,您可以配置自定义的“道线”,这些道线包含一系列的“动作”,这些动作执行您通常使用Android Studio执行的任务。您可以在Fastlane中做很多事情,但在本教程中,我们将仅使用核心动作的一小部分。

在您的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. 配置密钥

GitHub Actions 使用您在下一步中配置的仓库密钥。您只需要一个本地文件,如果您想从自己的机器上运行或测试 Fastlane。 .env 如果需要本地测试,请在

.不要将此文件提交。将 <project-root>/fastlane/.env 添加到您的 Fastfile首次(或验证它已经被忽略)。以下是示例: fastlane/.env 获取 APP_STORE_CONNECT_TEAM_ID .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=

开发者中心

并滚动到底部到 部分。 __CAPGO_KEEP_0__ Actions 使用您在下一步中配置的仓库密钥。您只需要一个本地文件,如果您想从自己的机器上运行或测试 Fastlane。 Membership details 如果需要本地测试,请在本地文件夹中创建一个名为 .lanes 的文件。不要将此文件提交。将 .lanesignore 添加到您的 .gitignore 文件中(或验证它已经被忽略)。以下是示例: Team ID 是您需要在 APP_STORE_CONNECT_TEAM_ID 中设置的值。

app-store-connect-team-id

获取 BUNDLE_IDENTIFIER

  1. 打开 Xcode
  2. 双击项目导航器中的 App 然后单击标签
  3. 复制的值是 Signing and Capabilities
  4. 。这是您需要在 Bundle identifier中设置的值。 BUNDLE_IDENTIFIER bundle-identifier-xcode
.

6. 构建处理

在 GitHub 动作中, 您按分钟数计算的CI/CD工作流程运行时间来收费。从我的经验来看,需要约10-15分钟才能在App Store Connect中处理一个构建。 对于私有项目,估计每个构建的成本最高可达

$0.08/分钟 x 15分钟 = $1.2 ,或更多,取决于您的项目的配置和依赖项。

如果您对私有项目的成本有所担忧,可以设置 skip_waiting_for_build_processing 来节省构建分钟数,不必等待App Store Connect处理构建。 true然而,这会带来一个权衡 - 您需要手动更新您的应用程序的合规信息在App Store Connect中,以便可以将构建分发给用户。

这种优化主要适用于私有项目,构建分钟数会产生费用。对于公共/免费项目,构建分钟数是免费的,因此无需启用此设置。见__CAPGO_KEEP_0__’s

This optimization is mainly useful for private projects where build minutes cost money. For public/free projects, the build minutes are free so there’s no need to enable this setting. See GitHub’s 价格页面 查看更多详细信息。

7. 配置GitHub动作

配置GitHub机密

请从文件中复制机密并将其粘贴到__CAPGO_KEEP_0__仓库中的机密和变量中。 .env file and paste them into the GitHub repository secrets.

设置 &gt; 机密和变量 &gt; 动作 &gt; > 新建仓库密钥

github-secrets

2. BUILD_CERTIFICATE_BASE64 Base64编码的证书。

3. BUILD_PROVISION_PROFILE_BASE64 Base64编码的配置文件。

4. 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工作流文件

创建一个GitHub工作流目录。

cd .github/workflows

文件夹内,创建一个名为__CAPGO_KEEP_0__的文件,并添加以下内容。 workflow 此工作流应在每个__CAPGO_KEEP_0__标签之后触发,如果您需要自动化标签,请参阅 build-upload-ios.yml使用__CAPGO_KEEP_0__动作进行自动化构建和发布

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

This workflow should be triggered after each GitHub 然后,这个工作流将拉取您的NodeJS依赖项,安装它们并构建您的JavaScript应用。每次您发送新的提交时,测试版将会构建一个发布。 您的应用程序不需要使用Ionic,只需使用GitHub基础即可,它可以包含旧的Cordova模块,但__CAPGO_KEEP_1__JS插件应优先使用。 Inside the __CAPGO_KEEP_0__ folder, create a file named __CAPGO_KEEP_0__ and add the following.

This workflow should be triggered after each __CAPGO_KEEP_0__ tag, if you need to automatize tag please, refer to Automatic build and release with __CAPGO_KEEP_0__ actions first.

Then this workflow will pull your NodeJS deps, install them and build your JavaScript app.

Each time you send a new commit, a release will be built in TestFlight. Your App doesn’t need to use Ionic, only Capacitor base is mandatory., it can have old Cordova module, but Capacitor JS plugin should be preferred.

8. 触发工作流程

创建提交

进行 提交, 您应该在仓库中看到正在活动的工作流程。

触发工作流程

将新提交推送到分支 maindevelopment 以触发工作流程。

使用

开始

几分钟后,构建应该在您的 App Store Connect 控制台中可用。

9. 我可以从本地机器部署吗?

是的,您可以,而且这很容易.

您可以使用 Xcode 构建和签署您的应用程序,正如往常一样.

CI/CD 设置指南

CI/CD 平台的替代方案

实时更新和部署

感谢

This blog is based on the following articles:

从自动CapacitorIOS构建中继续前进,使用GitHub动作和证书

如果您正在使用 自动CapacitorIOS构建,使用GitHub动作和证书 来规划CI/CD自动化,连接它到 Capgo CI/CD 在Capgo CI/CD中, Capgo原生构建 为产品工作流程中的Capgo原生构建 Capgo集成 为产品工作流程中的Capgo集成 CI/CD集成 为CI/CD集成的实现细节, GitHub动作集成 为GitHub动作集成的实现细节。

实时更新 Capacitor 应用

当 web 层 bug 活跃时,通过 Capgo 将修复推送,而不是等待几天的应用商店审批。用户在后台接收更新,而本机更改保持在正常审批路径中。

立即开始

博客最新文章

Capgo 为您提供创建真正专业的移动应用所需的最佳见解。