跳过主要内容
CI/CD

自动Capacitor IOS构建与GitHub动作

如何在5分钟内使用fastlane和GitHub Actions为您的IOS Ionic应用设置CI/CD管道(2024)

马丁·多纳迪厄

马丁·多纳迪厄

内容营销人员

自动Capacitor IOS构建与GitHub动作

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

我们现在建议使用 Capgo Build与CapgoCLI 为原生 Capacitor 构建。 本指南是为维护现有 GitHub Actions pipeline的团队保留的Fastlane指南,但新iOS构建应使用 Capgo CLI 以免自己维护Fastlane、Xcode runner、证书和上传脚本。

Capgo 构建由 Capgo 提供

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

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

定价

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

在CI/CD中设置Capgo构建

手动设置指南

以下是您需要做的事情:

使用Fastlane和GitHub动作进行iOS的持续交付

先决条件

继续教程之前:

  • 确保你在开发机器上安装了Fastlane 已安装 在开发机器上安装了Fastlane
  • 确保你是iOS开发者计划成员

关于价格的重要信息

价格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 ConnectAPI上传您的应用程序,您需要提供以下内容: 三个 事项:

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

获取App Store ConnectAPI密钥

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

  1. 登录到 App Store Connect.

  2. 选择 用户和访问.

App Store Connect 用户访问

3 — 选择 Integration 标签。

App Store Connect API Integration

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

App Store Connect API keys 创建

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

App Store Connect API keys 创建名称

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

  1. 点击生成。

一个API密钥的访问权限不能被限制到特定的应用。

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

App Store Connect下载密钥

您可以在这里获取所有三个必要的信息。
1. 问题ID。 (APPLE_ISSUER_ID 2. 密钥ID。 (
3. 密钥名称。 (APPLE_KEY_ID secret)
点击“下载 API 密钥”即可下载您的 API 私钥。下载链接仅在私钥尚未下载时才会出现。苹果公司不会保留私钥的副本。因此,您只能下载一次。

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

使用 App Store Connect API 密钥

下载的 API 密钥文件(p8 文件)、密钥 ID 和颁发者 ID 是创建授权 JWT 令牌所需的信息。有多种方法可以将此信息传递给 Fastlane。 我选择使用 Fastlane 的新动作 app_store_connect_api_key您可以在 Fastlane 文档中学习其他方法。 我展示此方法,因为我认为它是与大多数 CI 工作最方便的方式,即您可以设置环境变量。

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

base64 -i APPLE_KEY_CONTENT.p8 | pbcopy

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

2. 证书

打开 XCode 并前往 设置 > 账号 > Apple ID > 团队 并选择您的团队。

Code 签名身份

点击 管理证书.

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

点击 + 并选择 苹果分发

苹果分发

然后您需要前往钥匙串下载证书作为 .p12 文件。

要做到这一点,您需要前往钥匙串切换到 登录 钥匙串 我的证书.

我的证书

然后您可以选择要下载的证书。 (通过证书的日期查找)

然后右键单击证书上的私钥并选择 导出.

选择文件格式 个人信息交换 (.p12).

将会下载证书作为 .p12 文件。

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

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

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

3. 配置文件

打开 苹果开发者 然后选择正确的团队。

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

创建一个新的配置文件

并选择 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,您可以配置自定义的“道线”(lanes),这些道线包含一系列的“动作”(actions),这些动作执行您通常使用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 文件,如果您想从本地机器上运行或测试 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 密钥

中设置的值。

app-store-connect-team-id

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

6. 构建处理

在GitHub Actions中 您根据运行 CI/CD 工作流的分钟数而被收费。根据我的经验,需要约 10–15 分钟才能在 App Store Connect 中处理一个构建。 对于私有项目,估算的每个构建成本可能高达

$0.08/分钟 x 15 分钟 = $1.2 ,或更多,取决于您的项目的配置和依赖项。如果您对私有项目的成本有所担忧,可以设置

7. 构建 skip_waiting_for_build_processingtrue通过这种方式,可以节省构建分钟数,因为不需要等待App Store Connect处理构建完成。

然而,这种优化方式会带来一个折衷——您需要手动更新应用程序的合规信息在App Store Connect中,才能将构建分发给用户。

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. Setup GitHub Actions

Configure GitHub secrets

请从文件中复制密钥并将其粘贴到 .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 🔺发行者 ID。

7. APPLE_KEY_CONTENT —— App Store Connect API Key 🔺 Key content of .p8, 查看它

8. 配置 GitHub 工作流文件

创建一个 GitHub 工作流目录

cd .github/workflows

在文件夹内,创建一个名为 workflow 并添加以下内容。 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 后触发,如果您需要自动化标签,请参阅自动化 __CAPGO_KEEP_0__ 动作的构建和发布 Automatic build and release with GitHub actions 首先.

然后,这个工作流程将拉取您的 NodeJS 依赖项,安装它们并构建您的 JavaScript 应用程序.

每次您发送新的提交时,测试版将在 TestFlight 中构建一个发布.

您的 App 不需要使用 Ionic,只需 Capacitor 基础是必须的,它可以有旧的 Cordova 模块,但 Capacitor JS 插件应该是首选.

8. 触发工作流程

创建一个提交

制作一个 提交, 您应该在仓库中看到正在运行的工作流程.

触发工作流程

推送新的提交到分支 main 或者 development 触发工作流程。

使用以下提交开始

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

Testflight 控制台

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

是的,您可以,而且非常方便。

您可以使用 Xcode 构建和签名您的应用,正如往常一样。

CI/CD 设置指南

CI/CD 平台的替代方案

实时更新和部署

感谢

本博客基于以下文章:

Keep going from Automatic Capacitor IOS build with GitHub actions with certificate

如果您正在使用 Automatic Capacitor IOS build with GitHub actions with certificate 为了计划CI/CD自动化,连接它与 Capgo CI/CD 为产品工作流程在Capgo CI/CD中 Capgo 原生构建 为产品工作流程在Capgo 原生构建中 Capgo 集成 for the product workflow in Capgo Integrations, 为产品工作流程在__CAPGO_KEEP_0__ 集成中 CI/CD集成 GitHub Actions Integration GitHub 动作集成

实时更新Capacitor应用

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

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

立即开始

最新博客

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