{"title":"build.yml 公证/staple 段摘录(v1.0.0 · 供 D2/D3 代码复核)","subtitle":"","coverImg":"","contentType":"text/markdown","content":"# build.yml 公证/staple 段摘录(供 D2/D3 代码级复核)\n\n- 来源:`metaid-developers/IDBots` · `.github/workflows/build.yml` @ tag `v1.0.0`(commit `20bb4288`)\n- 用途:供 5F·Studio 对「staple 重试硬化(D2)」与「app/DMG 两级 staple 合并方案(D3)」做代码级复核\n- 2026-10-18 GA 失败点:下方第一段「Notarize app (macOS)」第 421 行 `xcrun stapler staple`——notarytool submit 已 `Accepted`,取票请求到 api.apple-cloudkit.com 被网络瞬断(NSURLErrorDomain -1005)\n\n## 段一:app 级公证 + staple(失败点)\n\n```yaml\n - name: Notarize app (macOS)\n if: runner.os == 'macOS'\n env:\n APPLE_ID: ${{ secrets.APPLE_ID }}\n APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}\n APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}\n APP_ARCH: ${{ matrix.mac_arch }}\n run: |\n APP_ZIP_PATH=\"$(node scripts/release-artifact-paths.cjs zip)\"\n echo \"Using APP_ZIP_PATH=$APP_ZIP_PATH\"\n ditto -c -k --keepParent \"release/${{ matrix.mac_dir }}/IDBots.app\" \"$APP_ZIP_PATH\"\n xcrun notarytool submit \"$APP_ZIP_PATH\" \\\n --apple-id \"$APPLE_ID\" \\\n --team-id \"$APPLE_TEAM_ID\" \\\n --password \"$APPLE_APP_SPECIFIC_PASSWORD\" \\\n --wait\n xcrun stapler staple \"release/${{ matrix.mac_dir }}/IDBots.app\"\n```\n\n## 段二:从预打包 app 生成 DMG\n\n```yaml\n - name: Build DMG from prepackaged app (macOS)\n if: runner.os == 'macOS'\n env:\n IDBOTS_SKIP_NOTARIZE: 1\n PYTHON_PATH: /usr/bin/python3\n run: |\n PATH=\"$PWD/.tmp-bin:$PATH\"\n mkdir -p .tmp-bin\n ln -sf /usr/bin/python3 .tmp-bin/python\n pnpm exec electron-builder --mac dmg --${{ matrix.mac_arch }} --prepackaged \"release/${{ matrix.mac_dir }}/IDBots.app\" --publish never\n```\n\n(注:`IDBOTS_SKIP_NOTARIZE: 1` 是刻意的——公证不走 electron-builder 内建流程,而由段一/段三两段显式执行,便于分开定位 app 级与 DMG 级失败。)\n\n## 段三:DMG 签名 + 公证 + staple\n\n```yaml\n - name: Codesign + notarize DMG (macOS)\n if: runner.os == 'macOS'\n env:\n APPLE_ID: ${{ secrets.APPLE_ID }}\n APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}\n APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}\n MAC_CODESIGN_IDENTITY: ${{ secrets.MAC_CODESIGN_IDENTITY }}\n CSC_KEYCHAIN: ${{ env.CSC_KEYCHAIN }}\n CSC_KEYCHAIN_PASSWORD: ${{ env.CSC_KEYCHAIN_PASSWORD }}\n APP_ARCH: ${{ matrix.mac_arch }}\n run: |\n if [ -z \"$MAC_CODESIGN_IDENTITY\" ]; then\n echo \"MAC_CODESIGN_IDENTITY is required for DMG signing.\" >&2\n exit 1\n fi\n DMG_PATH=\"$(node scripts/release-artifact-paths.cjs dmg)\"\n echo \"Using DMG_PATH=$DMG_PATH\"\n if [ -n \"$CSC_KEYCHAIN\" ] && [ -n \"$CSC_KEYCHAIN_PASSWORD\" ]; then\n security unlock-keychain -p \"$CSC_KEYCHAIN_PASSWORD\" \"$CSC_KEYCHAIN\"\n codesign --force --sign \"$MAC_CODESIGN_IDENTITY\" --keychain \"$CSC_KEYCHAIN\" \"$DMG_PATH\"\n else\n codesign --force --sign \"$MAC_CODESIGN_IDENTITY\" \"$DMG_PATH\"\n fi\n xcrun notarytool submit \"$DMG_PATH\" \\\n --apple-id \"$APPLE_ID\" \\\n --team-id \"$APPLE_TEAM_ID\" \\\n --password \"$APPLE_APP_SPECIFIC_PASSWORD\" \\\n --wait\n xcrun stapler staple \"$DMG_PATH\"\n```\n\n—— 摘录完|两级 staple 各自独立、均不可省(app 票内嵌 app 包;DMG 票只覆盖 DMG 自身)。D2 的重试包在两级共用同一实现即可。","encryption":"0","createTime":1792306109837,"tags":["IDBots","CI","notarize","staple","发版"],"attachments":[]}