跳过主要内容

Automatic Capacitor IOS build with GitHub actions using match

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

文章来源

马丁·多纳迪尤

作者

瓦莱里亚

审阅者

乔丹

编辑

Automatic Capacitor IOS build with GitHub actions using match

Automatic iOS Builds with GitHub Actions using Match

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

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

Capgo 构建由 Capgo 提供

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

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

定价

  • Capgo plans start at $12/month
  • : 包括 OTA 更新和约 15 个本机构建/月
  • : 额外的构建分钟通过信用额度按分钟计费

: 在 CI/CD 中设置 Capgo 构建

手动设置指南

以下是您需要做的事情:

使用Fastlane和GitHub的持续交付iOS

前提条件

继续教程之前,请确保您已经完成以下步骤:

关于价格的重要信息。

价格GitHub动作

https://github.com/features/actions

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

🔴 一旦警告了要求和价格,如果您喜欢,我们继续……

📣 在文章中,我们假设我们已经在iTunes Connect中创建了应用,我们已经拥有了苹果生态系统的证书,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 开始需要您撤销现有的证书。但是,不用担心,您将直接获得新的证书。

需求

To be able to use App Store Connect API, Fastlane needs three things

  1. Issuer ID
  2. Key ID
  3. Key file or Key content

Creating an App Store Connect API Key

To generate keys, you must have Admin permission in App Store Connect. If you don’t have that permission, you can direct the relevant person to this article and follow the following instructions.

1 — Log in to App Store Connect.

2 — Select Users and Access.

App Store Connect 用户访问权限

3 — 选择 Integration 选项卡。

App Store Connect API Integration

4 — 点击 Generate API Key 或 Add (+) 按钮。

App Store Connect API keys create

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

App Store Connect API keys create name

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

7 — 点击 Generate。

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

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

App Store Connect 下载密钥

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

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

使用 App Store Connect API 密钥

API 密钥文件(您下载的 p8 文件)、密钥 ID 和发行者 ID 是创建用于授权的 JWT 令牌所需的。这些信息可以通过多种方式输入到 Fastlane 中,使用 Fastlane 的新动作 app_store_connect_api_key您可以在此处学习其他方法。 Fastlane 文档因为我认为这是与大多数CI一起工作最简单的方法,

现在我们可以使用App Store Connect的API密钥来管理Fastlane,

2. 复制Fastlane文件

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

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

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

Appfile

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.

创建一个新的私有仓库,例如在您的__CAPGO_KEEP_0__个人账户或组织中。 certificates例如在您的GitHub个人账户或组织中。

初始化 Fastlane Match

fastlane match init

然后选择选项 #1 (Git Storage)

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

将新创建的仓库的 URL assignments

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

现在您在 Fastlane 文件夹内有一个名为 Matchfile 并且 _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.

然后将 Match 生成的文件替换为 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 秘密中设置,而不是在文件中硬编码。

构建处理

在 GitHub Actions 中 您将根据运行 CI/CD 工作流的分钟数而被计费。从经验来看,构建在 App Store Connect 中处理需要约 10–15 分钟。 在 __CAPGO_KEEP_1__ 秘密中

对于私有项目,构建的估算成本可以达到 $0.08/分钟 x 15 分钟 = $1.2,或更多,取决于您的项目的配置或依赖项。

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

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

这是一个可选参数,用于更新,如果您想为私有项目节省构建分钟数。对于免费项目,这完全不是问题。请参见 定价.

3. 设置GitHub Actions

配置GitHub 秘密

您是否曾经想知道 ENV 的值是从哪里来的?好吧,不是秘密了——它来自您的项目的秘密。 🤦

设置 GitHub 秘密

1. APP_STORE_CONNECT_TEAM_ID - 您在多个团队中属于的 App Store Connect 团队 ID。

2. DEVELOPER_APP_ID - 在 App Store Connect 中,转到应用程序 → 应用程序信息 → 滚动到底部到您的应用程序的部分并寻找 General Information - 您的应用程序的包标识符。 Apple ID.

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

4. DEVELOPER_PORTAL_TEAM_ID - 您用于管理应用程序的 Apple ID 或开发者电子邮件。

5. FASTLANE_APPLE_ID &amp;

6. GIT_USERNAME - 你的 Git 用户名和你的个人访问令牌。 GIT_TOKEN - 当您初始化 match 时分配的密钥,将用于解密证书和配置文件。

7. MATCH_PASSWORD - 在 App Store Connect 中,转到应用程序 →

8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>,例如。 match AppStore com.domain.blabla.demo.

9. TEMP_KEYCHAIN_USER &amp; 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)。 检查它。, — 您的 Match 密钥仓库 URL(例如:https://__CAPGO_KEEP_0__.com/***/fastlane_match.git)。

13. CERTIFICATE_STORE_URL — 配置 __CAPGO_KEEP_0__ 工作流文件。 — 创建 github 工作流目录。)

— App Store Connect GitHub Key 🔺 Key file or Key content of .p8

— App Store Connect GitHub Key 🔺Issuer ID.

cd .github/workflows

Inside the 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后触发 标签如果您需要自动化标签,请参见 Automatic build and release with GitHub actions 首先。

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

每次你提交新的代码变更,Capacitor会在TestFlight中自动构建一个新版本。

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

5. 触发工作流程

创建一个提交

要触发工作流程 提交你应该在仓库中看到正在运行的工作流程。

触发工作流程

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

创建一个提交

开始了一个提交

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

Testflight控制台

是的,您可以,而且这非常简单。

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

让我们开始吧

好的,首先我们需要在 my_project_path/fastlane 路径中创建一个名为 .env 的文件 仅仅在 Fastfile 的同一路径下 properties found in our _GitHub, a_s below:

本地机器的部署环境变量文件

现在,您可以转到终端 终端 并从您的机器上启动 快速通道

fastlane closed_beta

❌ 关于 .env 文件的重要信息,因为我们不想暴露这些数据,我们必须在我们的 .gitignore文件中添加它,类似这样: ❌

fastlane/*.env

它应该与在远程机器上的 GitHub Actions 一样工作,但是在本地机器上。 🍻

本地 Fastlane 运行

终端执行:$ Fastlane closed_beta

如果您已经阅读到这里,恭喜您,现在您已经拥有了使用 Fastlane 和 GitHub Actions 的 iOS 应用程序的完全自动化过程。

每次您发送新的提交,Google Play 控制台中将会构建一个 beta 版本。 我会根据您的反馈来改进这篇博客,如果您有任何问题或建议,请通过电子邮件联系我 martin@capgo.app

在您的设备上构建

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

如果您遇到问题

如果您遇到开发设备无法测试等问题,通常可以通过以下方式解决:

有一个神奇的命令可以帮助您:

fastlane match nuke development
fastlane match development

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

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

感谢

本博客基于以下文章:

继续阅读自动 Capacitor IOS 构建的教程,使用 GitHub 动作和 match

如果您正在使用 自动 Capacitor IOS 构建与 GitHub 动作使用匹配 规划 CI/CD 自动化,将其与 Capgo CI/CD 为产品工作流程在 Capgo CI/CD 中 Capgo 原生构建 为产品工作流程在 Capgo 原生构建中 Capgo 集成 为产品工作流程在 Capgo 集成中 CI/CD 集成 在 CI/CD 集成中,实现详细信息,并 GitHub 动作集成 为 GitHub Actions Integration 的实现细节。

Live updates for Capacitor apps

当一个 Web 层 bug 活跃时,通过 Capgo 将修复推送到用户,而不是等待 App Store 审批几天。用户在后台接收更新,而原生变化仍在正常审查路径中。

来自马丁的专业支持

立即开始

最新博客

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