跳过主要内容
CI/CD

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

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

马丁·多纳迪尤

马丁·多纳迪尤

内容营销人员

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

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

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

Capgo Build for CI/CD by Capgo

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

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

定价

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

在 CI/CD 中设置 Capgo Build

手动设置指南

您需要做的就是:

使用 Fastlane 和 GitHub 动作以及证书的方式进行 iOS 的持续交付

前提条件

在继续教程之前:

https://__CAPGO_KEEP_0__.com/features/actions

Price GitHub Action

Continuous Delivery for iOS using Fastlane and github Actions and certificate

Prerequisites免费' 根据选择的机器,最高可达限制。
我们将使用一个 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。这种额外的安全层有助于确保您是唯一可以访问帐户的人。
来自 苹果支持

要求

为了让 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 tab。

App Store Connect API Integration

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

App Store Connect API keys create

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

App Store Connect API keys create name

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

  1. 点击生成。

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

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

App Store Connect 下载密钥

您可以在这里获取所有三个必要的信息。
<1> 问题 ID。 (APPLE_ISSUER_ID <2> 密钥 ID。 (
<3> 点击 “下载 __CAPGO_KEEP_0__ 密钥”以下载您的 __CAPGO_KEEP_1__ 私钥。下载链接仅在私钥尚未下载时才会出现。苹果不会保留私钥的副本。因此,您只能下载一次。APPLE_KEY_ID 请将您的私钥存储在安全的地方。您不应共享您的密钥,存储密钥在 __CAPGO_KEEP_0__ 仓库中,或者将密钥包含在客户端 __CAPGO_KEEP_1__ 中。
<3> Click “Download API Key” to download your API private key. The download link appears only if the private key has not yet been downloaded. Apple does not keep a copy of the private key. So, you can download it only once.

🔴 Store your private key in a safe place. You should never share your keys, store keys in a code repository, or include keys in client-side code.

Using an App Store Connect API Key

The API Key file (p8 file that you download), the key ID, and the issuer ID are required in order to create the JWT token for authorization. There are multiple ways that this information can be passed into Fastlane. I chose to use the Fastlane’s new action app_store_connect_api_key。我展示此方法,因为我认为它是与大多数 CI 工作最方便的方法,其中您可以设置环境变量。 请将下载的 p8 文件转换为 Base64 并将其存储为一个密钥(__CAPGO_KEEP_0__

__CAPGO_KEEP_1__APPLE_KEY_CONTENT).

base64 -i APPLE_KEY_CONTENT.p8 | pbcopy

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

2. 证书

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

Code签名身份

点击 管理证书.

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

点击 + 并选择 Apple Distribution

Apple Distribution

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

为了实现此操作,请前往钥匙串切换到 __CAPGO_KEEP_1__ 并切换到 __CAPGO_KEEP_2__.

__CAPGO_KEEP_2__

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

然后右键单击证书中的私钥并选择 __CAPGO_KEEP_0__.

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

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

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

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

__CAPGO_KEEP_0__ 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,您可以配置自定义的“道”(lanes),这些道包含一系列的“动作”(actions),这些动作执行您通常使用Android Studio执行的任务。您可以在Fastlane中做很多事情,但在本教程中,我们将仅使用核心动作的少部分。

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

  • 文件夹: <project-root>/fastlane/
  • File: <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 使用您在下一步中配置的仓库机密。您只需要一个本地 .env 文件,如果您想在本地机器上运行或测试 Fastlane。

本地测试时,请创建 <project-root>/fastlane/.envFastfile。不要提交此文件。将 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

获取 BUNDLE_IDENTIFIER

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

6.处理

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

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

If you’re concerned about costs for private projects, you can set __CAPGO_KEEP_0__ to . This will save build minutes by not waiting for App Store Connect to finish processing the build. skip_waiting_for_build_processingtrueThis will save build minutes by not waiting for App Store Connect to finish processing the build.

However, there is a tradeoff - you’ll need to manually update your app’s compliance information in App Store Connect before you can distribute the build to users.

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 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

Configure GitHub secrets

pricing page .env file and paste them into the GitHub repository secrets.

for more details. 7. Setup __CAPGO_KEEP_0__ Actions Configure __CAPGO_KEEP_0__ secrets Please copy the secrets from the file and paste them into the __CAPGO_KEEP_0__ repository secrets. Go to 设置 > 密钥和变量 > 操作 > 新仓库密钥

github-密钥

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 🔺密钥内容 .p8, 检查它

8. 配置GitHub工作流文件

创建一个GitHub工作流目录。

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

这个工作流应该在每个GitHub 标签之后触发,如果您需要自动化标签,请参阅使用__CAPGO_KEEP_0__动作进行自动化构建和发布 Automatic build and release with GitHub actions 然后这个工作流将拉取您的NodeJS依赖项,安装它们并构建您的JavaScript应用程序。

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

每次您发送一个新的提交,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 触发工作流程

使用__CAPGO_KEEP_1__ JS插件

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

Testflight控制台

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

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

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

CI/CD设置指南

其他 CI/CD 平台

实时更新和部署

感谢

本博客基于以下文章:

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

如果您正在使用 自动 Capacitor IOS 构建,使用 GitHub 动作和证书 来规划 CI/CD 自动化,连接它到 Capgo CI/CD 对于产品工作流程在Capgo CI/CD中 Capgo 本地构建 对于产品工作流程在Capgo 本地构建中 Capgo 集成 对于产品工作流程在Capgo 集成中 CI/CD 集成 对于CI/CD集成的实现细节 GitHub 动作集成 对于GitHub 动作集成的实现细节

实时更新 Capacitor 应用

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

立即开始

博客最新文章

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