跳过主要内容
CI/CD

自动 Capacitor IOS 构建使用 GitHub 动作和证书

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

文章来源

马丁·多纳迪

作者

瓦莱里亚

审稿人

乔丹

编辑

自动 Capacitor IOS 构建与 GitHub 动作

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

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

Capgo 构建由 Capgo 提供

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

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

定价

  • Capgo 计划从每月 12 美元起
  • Capgo计划从$12/月开始
  • 通过分钟计费的额外构建分钟将使用积分

设置Capgo构建在CI/CD

手动设置指南

以下是您需要做的事情:

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

前提条件

继续教程之前,请确保您已安装Fastlane

重要信息

价格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 上传您的应用程序,您需要提供以下信息 三件事: Issuer ID

  1. Key ID
  2. Key 文件或 Key 内容
  3. 获取 App Store Connect __CAPGO_KEEP_0__ Key

获取 App Store Connect API Key

登录到

  1. App Store Connect 选择.

  2. 用户和访问 App Store Connect 用户访问.

three

3 — 选择集成选项卡。

App Store Connect API 集成

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

App Store Connect API 密钥创建

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

App Store Connect API 密钥创建名称

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

  1. 点击生成。

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

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

App Store Connect下载密钥

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

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

使用App Store ConnectAPI密钥

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 帮为会起约为一四为

请在终端中打开文件并使用以下命令将其转换为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,您可以配置自定义的“道线”,这些道线包含一系列的“动作”,这些动作执行您通常使用Android Studio执行的任务。您可以在Fastlane中做很多事情,但为了完成本教程,我们将仅使用核心动作的一小部分。

在您的Capacitor/Ionic项目的根目录下创建Fastlane文件夹,并在其中添加Fastfile文件:

  • 文件夹: <project-root>/fastlane/
  • 文件: <project-root>/fastlane/Fastfile

这是与 package.json, capacitor.config.*,和同级的文件夹。请勿将其创建在 ios/ 5. 配置机密 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

__CAPGO_KEEP_0__ Actions使用您在下一步中配置的仓库机密。您只需在本地

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

本地测试时,创建 <project-root>/fastlane/.env 文件夹,并将其放在 Fastfile请勿将此文件添加到版本控制中。将其添加到 fastlane/.env 到您的 .gitignore 获取APP_STORE_CONNECT_TEAM_ID

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=

前往

开发者中心 并滚动到底部到 部分。 Membership details 是您需要在中设置的值 Team ID 需要在中设置的值 APP_STORE_CONNECT_TEAM_ID iOS自动构建

app-store-connect-team-id

获取BUNDLE_IDENTIFIER

  1. 打开Xcode
  2. 双击项目导航器中的 App 然后点击
  3. 复制 Signing and Capabilities
  4. 。这是您需要在 Bundle identifierbundle-identifier-xcode BUNDLE_IDENTIFIER iOS自动构建
6. 构建处理

secret.

在GitHub Actions中 根据您提供的信息,翻译结果如下: 您根据使用的分钟数进行计费 对于私有项目,估算的每个构建成本可以达到

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

。这样可以节省构建分钟,不必等待App Store Connect处理构建。 skip_waiting_for_build_processing to true这种优化主要适用于私有项目,因为构建分钟会花费钱。对于公共/免费项目,构建分钟是免费的,所以不需要启用此设置。请参见__CAPGO_KEEP_0__的

价格页面

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 Cloudflare 查看更多详细信息。

7. 设置GitHub Actions

配置GitHub机密

请从文件中复制机密并将其粘贴到__CAPGO_KEEP_0__仓库中的机密和变量 .env 文件并将它们粘贴到GitHub仓库密钥中。

设置 &gt; 机密和变量 Actions 机密和变量 Secrets 机密和变量 新建仓库密钥

github-密钥

2. BUILD_CERTIFICATE_BASE64 - 基于 Base64 的证书。

3. BUILD_PROVISION_PROFILE_BASE64 - 基于 Base64 的分发配置文件。

4. BUNDLE_IDENTIFIER - 你的应用程序的包标识符。

5. APPLE_KEY_ID — App Store Connect API 密钥 🔺密钥 ID.

6. APPLE_ISSUER_ID — App Store Connect API Key 🔺发行者 ID。

7. APPLE_KEY_CONTENT — App Store Connect API Key 🔺密钥内容。 .p8, 检查它

8. 配置 GitHub 工作流文件

创建一个 GitHub 工作流目录。

cd .github/workflows

Inside 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

本工作流程应在每次GitHub后触发 标签如果您需要自动化标签,请参见 自动构建和发布与GitHub动作 首先。

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

每次你发送一个新的提交,测试版将在 TestFlight 中构建。

您的 App 不需要使用 Ionic,只需使用 Capacitor 基础即可,且可以包含老版 Cordova 模块,但建议使用 Capacitor JS 插件。

8. 触发工作流程

创建一个提交

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

触发工作流

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

创建一个提交

开始

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

Testflight控制台

可以的,而且非常简单。

您可以使用 Xcode 来构建和签名您的应用,依然如此。

CI/CD自动化配置指南

其他CI/CD平台

实时更新和部署

感谢

本博客基于以下文章:

从自动 Capacitor IOS 构建中继续使用 GitHub 动作和证书

如果您正在使用 自动 Capacitor IOS 构建使用 GitHub 动作和证书 计划 CI/CD 自动化时,连接它到 Capgo 自动化CI/CD 在 Capgo CI/CD 中的产品工作流程 在 Capgo Native Builds 中的产品工作流程 在 Capgo Native Builds 中的产品工作流程 Capgo 集成 为产品工作流程在 Capgo 集成中 CI/CD 集成 为 CI/CD 集成的实现细节 GitHub 动作集成 为 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__ 将修复推送给用户,而不是等待 App Store 审批几天。用户在后台接收更新,而原生变化保持在正常审批路径中。

上下文:Capgo 营销网站。角色:支持描述段落或元描述。见于组件 GetStarted.astro。保留 Capgo 产品/品牌和开发者术语的原始形式。消息键 `instant_updates_for_capacitor_apps_description` (Capacitor 应用实时更新描述)。

来自 Martin 的人工支持服务

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