跳过主要内容
__CAPGO_KEEP_10__

自动化 Capacitor IOS 构建使用 GitHub 动作

如何在 5 分钟内 (2022 年) 使用 fastlane 和 GitHub 动作设置 IOS ionic 应用程序的 CI/CD pipeline

Martin Donadieu

Martin Donadieu

内容营销人员

自动化 Capacitor IOS 构建使用 GitHub 动作

使用 Match 的 GitHub 动作进行自动化 iOS 构建

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

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

Capgo CI/CD 构建由 Capgo 提供

跳过 Fastlane Match、Xcode 运行器、证书、分发配置文件和上传脚本的维护 Capgo 构建 从现有的 CI/CD pipeline 运行签名的本机 iOS 构建:

  • 与您的 pipeline 一起工作:在 web 构建后从 GitHub Actions、GitLab CI、Jenkins 或本地脚本中触发 Capgo 构建 npx cap sync.
  • 从 CI 秘密中签名:将 App Store Connect 密钥、证书、分发配置文件、密码和团队 ID 保留在您的 CI 秘密中
  • 无本机运行器维护:Capgo 构建提供维护的 Apple 构建环境,因此您不必管理 macOS 运行器、Xcode 图像、Fastlane 或 Match 仓库
  • 工件和提交: 下载经过签名的 artifact 用于 QA 或提交发布构建通过 Capgo CLI。

定价

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

在 CI/CD 中设置 Capgo 构建

手动设置指南

以下是您需要做的事情:

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

前提条件

继续教程之前,请确保您有 Fastlane

  • 确保您有 Fastlane 已安装 在您的开发机器上。
  • iOS开发者计划会员资格.
  • 想看😆…
  • 如果您不是团队中很多开发者,我们建议使用 fastlane cert 简化工作流程.

关于价格的重要信息.

价格GitHub Action

https://github.com/features/actions

服务的费用取决于所选机器的限制,免费的上限.
我们将使用一个 macOS 截图中显示的价格和限制(截止教程创建时间,价格可能会随时间变化)

🔴 已知需求和价格后,如果您喜欢,我们继续…

📣 在文章中,我们假设我们已经在 iTunes Connect 中创建了应用,拥有 Apple 生态系统的证书,Fastlane 将复制所有内容!

让我们开始探索!

文章中需要遵循的步骤

  1. 使用 App Store Connect API 和 Fastlane Match
  2. 需求
  3. 创建 App Store Connect API 密钥
  4. 使用 App Store Connect API 密钥
  5. 复制 Fastlane 文件
  6. 配置 Fastlane match

1. 使用 App Store Connect API 与 Fastlane Match

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

使用 match 开始需要撤销现有的证书。但是,不用担心,您将直接获得新的证书。

要求

要使用 App Store Connect API,Fastlane 需要 三个 事项。

  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

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

App Store Connect API 密钥创建

5 — 为密钥输入一个名称。该名称仅供您参考,不是密钥的一部分。

App Store Connect API keys 创建名称

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

7 — 点击生成。

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

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

App Store Connect 下载密钥

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

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

使用App Store ConnectAPI密钥

创建JWT令牌进行授权所需的API密钥文件(下载的p8文件)、密钥ID和发行者ID。这些信息可以通过Fastlane的新动作输入Fastlane中,有多种方法可以实现。 app_store_connect_api_key您可以在Fastlane文档中学习其他方法。 我展示这个方法是因为我认为它是与大多数CI一起工作最容易的方法,通过设置环境变量。现在我们可以使用App Store Connect__CAPGO_KEEP_0__密钥管理Fastlane了,太棒了!

Now we can manage Fastlane with the App Store Connect API key, great!

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

在您的项目根目录创建一个Fastlane文件夹,并复制以下文件: Fastfile

Appfile

default_platform(:ios)

DEVELOPER_APP_IDENTIFIER = ENV["DEVELOPER_APP_IDENTIFIER"]
DEVELOPER_APP_ID = ENV["DEVELOPER_APP_ID"]
PROVISIONING_PROFILE_SPECIFIER = ENV["PROVISIONING_PROFILE_SPECIFIER"]
TEMP_KEYCHAIN_USER = ENV["TEMP_KEYCHAIN_USER"]
TEMP_KEYCHAIN_PASSWORD = ENV["TEMP_KEYCHAIN_PASSWORD"]
APPLE_ISSUER_ID = ENV["APPLE_ISSUER_ID"]
APPLE_KEY_ID = ENV["APPLE_KEY_ID"]
APPLE_KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]

def delete_temp_keychain(name)
  delete_keychain(
    name: name
  ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end

def create_temp_keychain(name, password)
  create_keychain(
    name: name,
    password: password,
    unlock: false,
    timeout: 0
  )
end

def ensure_temp_keychain(name, password)
  delete_temp_keychain(name)
  create_temp_keychain(name, password)
end

platform :ios do
  lane :build do
    build_app(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )
  end
  lane :refresh_profiles do
    match(
      type: "development",
      force: true)
    match(
      type: "adhoc",
      force: true)
  end
  desc "Register new device"
  lane :register_new_device do  |options|
      device_name = prompt(text: "Enter the device name: ")
      device_udid = prompt(text: "Enter the device UDID: ")
      device_hash = {}
      device_hash[device_name] = device_udid
      register_devices(
                       devices: device_hash
                       )
    refresh_profiles
  end
  lane :closed_beta do
    keychain_name = TEMP_KEYCHAIN_USER
    keychain_password = TEMP_KEYCHAIN_PASSWORD
    ensure_temp_keychain(keychain_name, keychain_password)

    api_key = app_store_connect_api_key(
      key_id: APPLE_KEY_ID,
      issuer_id: APPLE_ISSUER_ID,
      key_content: APPLE_KEY_CONTENT,            
      duration: 1200,            
      in_house: false
    )

    match(
      type: 'appstore',
      git_basic_authorization: Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"),
      readonly: true,
      keychain_name: keychain_name,
      keychain_password: keychain_password,
      api_key: api_key
    )

    gym(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )

    pilot(
      apple_id: "#{DEVELOPER_APP_ID}",
      app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./App.ipa"
    )

    delete_temp_keychain(keychain_name)
  end
  lane :submit_review do
    version = ''
    Dir.chdir("..") do
      file = File.read("package.json")
      data = JSON.parse(file)
      version = data["version"]
    end
    deliver(
      app_version: version,
      submit_for_review: true,
      automatic_release: true,
      force: true, # Skip HTMl report verification
      skip_metadata: false,
      skip_screenshots: false,
      skip_binary_upload: true
    )
  end
end

使用App Store Connect__CAPGO_KEEP_0__密钥

app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"])
apple_id(ENV["FASTLANE_APPLE_ID"])
itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"])
team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"])

配置 Fastlane match

Fastlane match is a new approach to iOS’s code signing. Fastlane match makes it easy for teams to manage the required certificates and provisioning profiles for your iOS apps.

创建一个名为 certificates, for example on your GitHub personal account or organization.

上。

fastlane match init

初始化 Fastlane match 为您的 iOS 应用。

[01:00:00]: fastlane match supports multiple storage modes, please select the one you want to use:1. git2. google_cloud3. s3?

然后选择选项 #1 (Git 存储)。

[01:00:00]: Please create a new, private git repository to store the certificates and profiles there[01:00:00]: URL of the Git Repo: <YOUR_CERTIFICATES_REPO_URL>

将新创建仓库的 URL assignments。 现在您在 Fastlane 文件夹内有一个名为 的文件, _git_url_应设置为证书仓库的 HTTPS URL。可选地,您也可以使用 SSH,但这需要一个不同的步骤来运行。

# ios/Matchfilegit_url("https://github.com/gitusername/certificates")storage_mode("git")type("appstore")

接下来,我们去生成证书并在被询问时输入您的凭据使用 Fastlane Match。

您将被要求输入一个密语。正确地记住它,因为它将在后续的 GitHub Actions 中用来解密您的证书仓库。

fastlane match appstore

如果一切顺利,您应该看到类似这样的内容:

[01:40:52]: All required keys, certificates and provisioning profiles are installed 🙌

如果您在 GitHub 和必要的权限方面遇到任何问题,可能这个 帖子 会帮助您生成 Git 的认证令牌。

生成的证书和配置文件上传到证书仓库资源中

App Store Connect 证书

最后,打开您的 project 在 Xcode 中,更新您的应用的发布配置的分发配置文件。

XCode 证书

注意事项:

匹配

为了让CI/CD导入证书和配置文件,需要访问证书仓库。您可以通过生成一个个人访问令牌(应在此之前使用),该令牌具有访问或读取私有仓库的权限来实现。

在GitHub中, 设置开发者设置个人访问令牌 → 点击 Generate New Token → 勾选 repo 权限 → 然后点击 Generate token.

创建个人访问令牌

复制生成的个人访问令牌。您将在后续步骤中使用它 GIT_TOKEN.

然后将Fastlane文件夹中生成的匹配文件替换为 Matchfile

CERTIFICATE_STORE_URL = ENV["CERTIFICATE_STORE_URL"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
FASTLANE_APPLE_ID = ENV["FASTLANE_APPLE_ID"]

git_url(CERTIFICATE_STORE_URL)
storage_mode("git")
type("appstore")
git_basic_authorization(Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"))
username(FASTLANE_APPLE_ID)

此文件将被GitHub Actions用于导入证书和配置文件。 并且GitHub Secrets中的变量将被设置,而不是在文件中硬编码它们。

构建处理

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

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

如果您对私有项目的定价有同样的担忧,就像我一样,您可以保留 skip_waiting_for_build_processingtrue.

是什么陷阱呢?您必须手动更新您的应用程序在App Store Connect中的合规性,以便将构建分发给您的用户。

如果您想在私有项目中节省构建分钟数,则可以选择更新此参数。对于免费项目来说,这根本不是问题。 查看.

3. 配置GitHub

配置GitHub

您曾经想知道这些值是从哪里来的吗?答案就是您的项目的密钥了。 ENV 配置__CAPGO_KEEP_0__

Set GitHub secrets

1. APP_STORE_CONNECT_TEAM_ID - 在 App Store Connect 中,转到应用 →

2. DEVELOPER_APP_ID 应用信息 → 滚动到底部找到应用的 应用信息 General Information section Apple ID.

3. DEVELOPER_APP_IDENTIFIER - 你的应用程序的包标识符。

4. DEVELOPER_PORTAL_TEAM_ID - 如果您属于多个团队,则是您的开发者门户团队 ID。

5. FASTLANE_APPLE_ID - 您用来管理应用程序的 Apple ID 或开发者电子邮件。

6. GIT_USERNAME & GIT_TOKEN - 你的 Git 用户名和你的个人访问令牌。

7. MATCH_PASSWORD - 初始化匹配时分配的密钥串,将用于解密证书和配置文件。

8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>eg. match AppStore com.domain.blabla.demo.

9. TEMP_KEYCHAIN_USER & TEMP_KEYCHAIN_PASSWORD - 为您的工作流分配一个临时密钥链用户和密码。

10. APPLE_KEY_ID — App Store Connect API 密钥 🔺密钥 ID。

11. APPLE_ISSUER_ID — App Store Connect API 密钥 🔺颁发者 ID。

12. APPLE_KEY_CONTENT — App Store Connect API 密钥 🔺密钥文件或密钥内容。 .p8, 去检查一下

13. CERTIFICATE_STORE_URL — 你的 Match keys 的仓库 URL(例如: https://github.com/***/fastlane_match.git)

4. 配置 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: 2.7.2
      - uses: maierj/fastlane-action@v2.3.0
        env:
          DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          DEVELOPER_APP_ID: ${{ secrets.DEVELOPER_APP_ID }}
          PROVISIONING_PROFILE_SPECIFIER: match AppStore ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          TEMP_KEYCHAIN_USER: ${{ secrets.TEMP_KEYCHAIN_USER }}
          TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          CERTIFICATE_STORE_URL: https://github.com/${{ secrets.CERTIFICATE_STORE_REPO }}.git
          GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
          GIT_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
          DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
        with:
          lane: closed_beta
      - name: Upload release bundle
        uses: actions/upload-artifact@v2
        with:
          name: ios-release
          path: ./App.ipa
          retention-days: 60

这个工作流应该在每次 GitHub 打标签后触发,如果您需要自动化标签,请参阅相关文档 自动构建和发布与 GitHub 动作 首先。

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

每次您发送新的提交,测试飞行中的发布将被构建。

您的应用程序不需要使用Ionic,只需 Capacitor 基础即可,它可以具有旧的Cordova模块,但 Capacitor JS插件应优先使用。

5. 触发工作流程

创建一个提交

做一个 提交您应该在仓库中看到正在活动的工作流程。

触发工作流程

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

使用

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

Testflight 控制台

是否可以从本地机器部署?

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

假设您有一个私有仓库,您已经用完了免费计划的分钟数,并且您不想为新发布支付费用,或者您更喜欢手动提交应用程序。

让我们开始吧

好的,我们需要在 __CAPGO_KEEP_0__ path 中创建一个名为 .env, 在同一路径下 Fastfile, 才能创建相同的 机密 在我们的 _GitHub 中找到以下属性:

.env 文件用于从本地机器部署

现在,您可以进入 终端 并从您的机器上启动 Fastlane 从本地机器部署

fastlane closed_beta

❌ 关于的基本信息 .env 文件,因为我们不想暴露此数据,我们必须在我们的 .gitignore,类似于❌

fastlane/*.env

它应该与发生在GitHub Actions的远程机器上一样,但是在我们的本地机器上。 🍻

本地Fastlane运行

终端执行:$ Fastlane closed_beta

如果您已经走到这一步,我恭喜您,现在您已经拥有了使用Fastlane和GitHub Actions来自动化您的iOS应用的完全自动化过程。

每次您发送新的提交,Google Play Console中的beta频道将会构建一个新的发布。 martin@capgo.app

martin@__CAPGO_KEEP_0__.app

如果您仍然需要在设备上构建,需要手动将它们添加到配置中。 连接您的设备到您的Mac并打开设备菜单 查找设备iOS菜单 然后复制您的标识符 查找标识符iOS 然后启动命令: fastlane register_new_device 它会要求您设置设备名称和标识符: 如果您遇到问题

如果您遇到开发设备无法测试等问题,通常会解决它。

有一个神奇的命令可以拯救您:

然后: 清除项目,按住Shift(⇧)+Command(⌘)+K或选择Product > Clean(它可能标记为“清理构建文件夹”)

fastlane match nuke development
fastlane match development

然后再次尝试在设备上运行应用。

如果您遇到问题

感谢

本博客基于以下文章:

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

如果您正在使用 自动Capacitor IOS构建使用GitHub动作和match 来规划CI/CD自动化,将其与 Capgo CI/CD Capgo CI/CD中的产品工作流程 Capgo原生构建 为产品工作流程在Capgo原生构建中 Capgo集成 为产品工作流程在Capgo集成中 CI/CD集成 CI/CD集成的实现细节,以及 GitHub动作集成 for the implementation detail in GitHub Actions Integration.

Capacitor实时更新

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

立即开始

最新博客

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