如何在 Github Actions macos runner 中签署和公证 PKG

How to sign and notarize a PKG within a Github Actions macos runner

上下文

我正在构建一个 Github 操作作业来 构建、签名和公证 PKG 文件。

我正在使用 Apple Id 帐户(工作流程需要用户名和密码)以及 Developer Id Installer 证书私钥(加密)。两者都保存为机密 (base64),并将在工作流程中转换为 .p12 文件,然后添加到钥匙串。

这项工作是私有存储库中更大工作流程的一部分,它首先从软件生成文件(使用 Pyinstaller),然后将 PKG 导出到 AWS S3 存储桶。

实施

jobs:
  [...]

  pkg:
    needs: [...]
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Download macos bin file
        uses: actions/download-artifact@v2
        with:
          name: macos-bin-files
          path: dist/
      - name:
        run: | 
          ----- Create certificate files from secrets base64 -----
          echo ${{ secrets.DEVELOPER_ID_INSTALLER_CER }} | base64 --decode > certificate_installer.cer
          echo ${{ secrets.DEVELOPER_ID_INSTALLER_KEY }} | base64 --decode > certificate_installer.key

          ----- Create p12 file -----
          openssl pkcs12 -export -name zup -in certificate_installer.cer -inkey certificate_installer.key -passin pass:${{ secrets.KEY_PASSWORD }} -out certificate_installer.p12 -passout pass:${{ secrets.P12_PASSWORD }}

          ----- Configure Keychain -----
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH

          ----- Import certificates on Keychain -----
          security import certificate_installer.p12 -P "${{ secrets.P12_PASSWORD }}" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          ----- Generate PKG from files -----
          use a macos installer script (ref: https://github.com/KosalaHerath/macos-installer-builder/tree/master/macOS-x64)

          ----- Sign PKG file -----
          productsign --sign "${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}" $INPUT_FILE_PATH $OUTPUT_FILE_PATH

          - name: "Notarize Release Build PKG"
            uses: devbotsxyz/xcode-notarize@v1 
            with:
              product-path: $PATH_TO_PKG
              appstore-connect-username: ${{ secrets.APPLE_ACCOUNT_USERNAME }}
              appstore-connect-password: ${{ secrets.APPLE_ACCOUNT_PASSWORD }}
              primary-bundle-id: 'BUNDLE_ID'

          - name: "Staple Release Build"
            uses: devbotsxyz/xcode-staple@v1
            with:
              product-path: $PATH_TO_PKG

  [...]

问题

我遵循了 installing an Apple certificate on macOS runners 的 Github Action 官方文档,这部分工作正常。我可以将证书添加到钥匙串,并使用它通过 productsign 命令对 PKG 文件进行签名。

但是,当我将 PKG 发送给 Apple 进行公证时,它 returns 我遇到了错误:

Error: Notarization status <invalid> - Package Invalid
Error: Notarization failed

我试过的

PKG 在分发时按预期工作(只需要以管理员身份打开,因为没有公证),所以问题似乎与包实现无关。

我尝试使用命令行执行公证,点击不同来源的链接:

但是,即使使用这些命令行(不使用公证操作),我也无法公证 PKG 文件。

问题

我的工作流程出了什么问题,我在尝试对包裹进行公证之前是否遗漏了什么?

PS:我找不到在 Github Actions macos runner 上执行此操作的任何参考...

解决方案

设置 devbotsxyz/xcode-notarize@v1 字段 verbose:true 后,我观察到 Apple returns a link 到 JSON 解释了为什么包对公证无效.

在这个 JSON 中,它被告知组成我的 PKG 的所有文件(有很多,因为我无法在我的上下文中使用 --onefile 和 Pyinstaller)都是无效的 因为它们没有签名和时间戳

经过一些研究,我发现 this post on the Apple Developer Forum 并了解到 PKG 文件需要进行 INSIDE OUT 签名 :首先,对 Pyinstaller 生成的每个文件进行签名,然后进行签名PKG 收集所有这些文件。

为此,您不能使用 productsign 命令(因为它仅适用于 .pkg、.zip 和 .dmg),但 codesign.

但是,codesign 不使用 Developer Id Installer certificate,而是 Developer Id Application certificate,因此我也必须将这个新证书添加到工作流程中。

请注意,由于我的 PKG 将由数百个文件组成,因此我还需要 a bash script to use codesign to sign each one of them

我的最终工作流程是这样的:

jobs:
  [...]

  pkg:
    needs: [...]
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Download macos bin file
        uses: actions/download-artifact@v2
        with:
          name: macos-bin-files
          path: dist/
      - name:
        run: | 
          ----- Create certificate files from secrets base64 -----
          echo ${{ secrets.DEVELOPER_ID_INSTALLER_CER }} | base64 --decode > certificate_installer.cer
          echo ${{ secrets.DEVELOPER_ID_INSTALLER_KEY }} | base64 --decode > certificate_installer.key
          echo ${{ secrets.DEVELOPER_ID_APPLICATION_CER }} | base64 --decode > certificate_application.cer
          echo ${{ secrets.DEVELOPER_ID_APPLICATION_KEY }} | base64 --decode > certificate_application.key

          ----- Create p12 file -----
          openssl pkcs12 -export -name zup -in certificate_installer.cer -inkey certificate_installer.key -passin pass:${{ secrets.KEY_PASSWORD }} -out certificate_installer.p12 -passout pass:${{ secrets.P12_PASSWORD }}
          openssl pkcs12 -export -name zup -in certificate_application.cer -inkey certificate_application.key -passin pass:${{ secrets.KEY_PASSWORD }} -out certificate_application.p12 -passout pass:${{ secrets.P12_PASSWORD }}

          ----- Configure Keychain -----
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH

          ----- Import certificates on Keychain -----
          security import certificate_installer.p12 -P "${{ secrets.P12_PASSWORD }}" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security import certificate_application.p12 -P "${{ secrets.P12_PASSWORD }}" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          ----- Codesign files with script -----
          use a script to sign each file from the artifact (ref: https://gist.github.com/GuillaumeFalourd/4efc73f1a6014b791c0ef223a023520a)

          ----- Generate PKG from codesigned files -----
          use a macos installer script (ref: https://github.com/KosalaHerath/macos-installer-builder/tree/master/macOS-x64)

          ----- Sign PKG file -----
          productsign --sign "${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}" $INPUT_FILE_PATH $OUTPUT_FILE_PATH

          - name: "Notarize Release Build PKG"
            uses: devbotsxyz/xcode-notarize@v1 
            with:
              product-path: $PATH_TO_PKG
              appstore-connect-username: ${{ secrets.APPLE_ACCOUNT_USERNAME }}
              appstore-connect-password: ${{ secrets.APPLE_ACCOUNT_PASSWORD }}
              primary-bundle-id: 'BUNDLE_ID'

          - name: "Staple Release Build"
            uses: devbotsxyz/xcode-staple@v1
            with:
              product-path: $PATH_TO_PKG

  [...]