Electron 企业构建与受控发布

验证:未记录
适用版本
未记录
验证范围
尚未登记验证记录。

CI(Continuous Integration,持续集成)负责构建与验证;正式发布必须等全部平台成功。以下使用 electron-builder,已有 pnpm-lock.yaml、test:ci、build:win:x64、build:mac:arm 脚本;构建脚本必须关闭自动发布(--publish never)。不能让打包工具绕过最后的发布任务。

1. 准备签名与权限#

  • 仓库保护 v* 标签;配置 desktop-signing 和 desktop-release Environment,限制受保护标签,后者要求发布审批。
  • signing 环境保存 Windows/macOS 签名材料和 Apple 公证凭据;示例使用 electron-builder 的 CSC_LINK、CSC_KEY_PASSWORD。证书由企业签名服务管理时替换为其受控签名步骤。
  • macOS 打包配置启用公证,使用 Apple API key,完成后 staple;下方会验证签名/公证结果,缺失即失败。Windows 需受信任代码签名和时间戳。
  • packageManager 固定已验证 pnpm 精确版本。示例 Action 使用主版本便于阅读,落库时通过 Dependabot/Renovate 固定完整 commit SHA,审批升级。

2. 工作流#

保存 .github/workflows/desktop-release.yml:

name: Desktop release
on:
  push:
    tags: ['v*']
permissions:
  contents: read
concurrency:
  group: desktop-${{ github.ref }}
  cancel-in-progress: false
jobs:
  build:
    environment: desktop-signing
    strategy:
      fail-fast: false
      matrix:
        include:
          - {os: windows-latest, platform: win, script: 'build:win:x64', files: 'dist/*.exe'}
          - {os: macos-latest, platform: mac, script: 'build:mac:arm', files: 'dist/*.dmg'}
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v6
        with: {persist-credentials: false}
      - uses: actions/setup-node@v6
        with: {node-version: '24'}
      - uses: pnpm/action-setup@v6
      - run: pnpm install --frozen-lockfile
      - run: pnpm run test:ci
      - name: Materialize notarization key
        if: matrix.platform == 'mac'
        shell: bash
        env:
          API_KEY: ${{ secrets.APPLE_API_KEY_CONTENT }}
        run: |
          umask 077
          printf '%s' "$API_KEY" > "$RUNNER_TEMP/AuthKey.p8"
      - name: Package and sign, never publish
        run: pnpm run ${{ matrix.script }} --publish never
        env:
          CSC_LINK: ${{ matrix.platform == 'win' && secrets.WIN_CSC_LINK || secrets.MAC_CSC_LINK }}
          CSC_KEY_PASSWORD: ${{ matrix.platform == 'win' && secrets.WIN_CSC_PASSWORD || secrets.MAC_CSC_PASSWORD }}
          APPLE_API_KEY: ${{ runner.temp }}/AuthKey.p8
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
      - name: Validate Windows signatures
        if: matrix.platform == 'win'
        shell: pwsh
        run: |
          $files = @(Get-ChildItem dist/*.exe)
          if ($files.Count -eq 0) { throw 'No executable' }
          foreach ($file in $files) {
            if ((Get-AuthenticodeSignature $file.FullName).Status -ne 'Valid') { throw 'Invalid signature' }
          }
      - name: Validate macOS notarization
        if: matrix.platform == 'mac'
        shell: bash
        run: |
          set -euo pipefail
          found=0
          for file in dist/*.dmg; do
            test -f "$file"
            xcrun stapler validate "$file"
            spctl --assess --type open --context context:primary-signature --verbose "$file"
            found=1
          done
          test "$found" = 1
      - uses: actions/upload-artifact@v4
        with:
          name: desktop-${{ matrix.platform }}
          path: ${{ matrix.files }}
          if-no-files-found: error
          retention-days: 14
  release:
    needs: build
    environment: desktop-release
    runs-on: ubuntu-latest
    permissions: {contents: write}
    steps:
      - uses: actions/download-artifact@v4
        with: {pattern: 'desktop-*', path: artifacts}
      - name: Validate all platforms and publish complete draft
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
          TAG: ${{ github.ref_name }}
        run: |
          set -euo pipefail
          compgen -G 'artifacts/desktop-win/*.exe' >/dev/null
          compgen -G 'artifacts/desktop-mac/*.dmg' >/dev/null
          # Do not mutate an existing release, including a failed run's draft.
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo 'Release exists; reconcile the previous run before retrying' >&2
            exit 1
          fi
          find artifacts -type f -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
          gh release create "$TAG" --verify-tag --draft --generate-notes
          gh release upload "$TAG" artifacts/desktop-win/*.exe artifacts/desktop-mac/*.dmg SHA256SUMS
          gh release view "$TAG" --json assets > assets.json
          EXPECTED=$(find artifacts -type f | wc -l)
          jq -e --argjson n "$EXPECTED" '.assets | length == ($n + 1)' assets.json
          if [[ "$TAG" == *-* ]]; then
            gh release edit "$TAG" --prerelease
          fi
          gh release edit "$TAG" --draft=false

Apple API 私钥由上述步骤写入临时 runner,禁止上传至构建产物;使用一次性 runner,任务结束后销毁。签名环境不得用于不可信 PR。

3. 验收与恢复#

  1. 在受保护测试标签故意使 Windows 测试失败:macOS 可构建,但 release 任务跳过,不产生正式版本。
  2. 移除签名凭据:签名验收失败;禁止通过关闭验签完成发布。
  3. 双平台通过后核对文件、SHA256SUMS、签名、公证和安装启动;上传失败只留下草稿。修复并人工核对/移除本次草稿后重跑,不覆盖已发布资产。
  4. 已发布版本有问题时标记问题版本、发布修复版本;自动更新渠道应指向审核版本。二进制可回退不代表用户数据格式可降级,迁移另做兼容测试。

GitHub 任务依赖、electron-builder 签名。

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭