# 在用于 Xcode 开发的 macOS 运行器上安装 Apple 证书

了解如何通过在 GitHub Actions 运行器上安装 Apple 代码签名证书，在持续集成（CI）工作流中为 Xcode 应用签名。

## 先决条件

你应该熟悉 YAML 和语法 GitHub Actions。 有关详细信息，请参阅：

* [撰写工作流程](/zh/actions/how-tos/write-workflows)
* [GitHub Actions 的工作流语法](/zh/actions/reference/workflows-and-actions/workflow-syntax)

您应该了解如何使用 Xcode 进行应用构建和签名。 有关详细信息，请参阅 [Apple 开发人员文档](https://developer.apple.com/documentation/)。

## 为您的证书和预配配置文件创建密码

签名过程包括存储证书和预配配置文件、将它们传输给运行器、将它们导入运行器的密钥链，以及在构建中使用它们。

若要在运行器上使用您的证书和预配置描述文件，我们强烈建议您使用 GitHub 密钥。 有关创建机密并在工作流中使用它们的详细信息，请参阅“[在 GitHub Actions 中使用机密](/zh/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets)”。

在您的仓库或组织中为下列项目创建密钥：

* 您的 Apple 签名证书。

  * 这是你的 `p12` 证书文件。 有关从 Xcode 导出签名证书的详细信息，请参阅 [Xcode 文档](https://help.apple.com/xcode/mac/current/#/dev154b28f09)。

  * 当您将证书保存为密钥时，您应该将其转换为 Base64 。 在此示例中，机密命名为 `BUILD_CERTIFICATE_BASE64`。

  * 使用以下命令将证书转换为 Base64 并将其复制到剪贴板：

    ```shell
    base64 -i BUILD_CERTIFICATE.p12 | pbcopy
    ```

* 您的 Apple 签名证书的密码。
  * 在此示例中，机密命名为 `P12_PASSWORD`。

* 您的 Apple 配置描述文件。

  * 有关从 Xcode 导出预置描述文件的详细信息，请参阅 [Xcode 文档](https://help.apple.com/xcode/mac/current/#/deva899b4fe5)。

  * 当将配置文件保存为秘密时，您应该将其转换为Base64。 在此示例中，机密命名为 `BUILD_PROVISION_PROFILE_BASE64`。

  * 使用以下命令将预配配置文件转换为 Base64 并将其复制到剪贴板：

    ```shell
    base64 -i PROVISIONING_PROFILE.mobileprovision | pbcopy
    ```

* 密钥链密码。

  * 将在运行器上创建一个新的密钥链，因此新密钥链的密码可以是任何新的随机字符串。 在此示例中，机密命名为 `KEYCHAIN_PASSWORD`。

## 在工作流程中添加一个步骤

此示例工作流包含一个步骤，用于从 GitHub 机密中导入 Apple 证书和描述文件，并将其安装到运行器上。

```yaml copy
name: App build
on: push

jobs:
  build_with_signing:
    runs-on: macos-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
      - name: Install the Apple certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          # create variables
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

          # import certificate and provisioning profile from secrets
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

          # create temporary keychain
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # import certificate to keychain
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # apply provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
      - name: Build app
          # ...
```

> \[!NOTE]
> 对于 iOS 生成目标，配置描述文件的扩展名应为 `.mobileprovision`。 对于 macOS 生成目标，扩展名应为 `.provisionprofile`。 应更新上述示例工作流以反映目标平台。

## 自托管运行器上的必要清理

GitHub托管运行器是隔离的虚拟机，会在作业执行结束时自动销毁。 这意味着在任务进行期间，运行器中使用的证书和预配配置文件将在任务完成后与运行器一起被销毁。

在自托管运行器上，`$RUNNER_TEMP` 目录在任务执行结束时被清除，但在运行器上可能仍然存在密钥链和预置描述文件。

如果您使用自托管的运行器， 您应该在工作流程中添加最后一步，以帮助确保这些敏感文件在作业结束时被删除。 下面显示的工作流程步骤是如何执行此操作的一个示例。

```yaml
- name: Clean up keychain and provisioning profile
  if: ${{ always() }}
  run: |
    security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
    rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
```