diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 52e1e219..8150432a 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -10,6 +10,11 @@ on: required: false default: true type: boolean + notarize_macos: + description: 'Notarize macOS artifacts' + required: false + default: true + type: boolean permissions: contents: write @@ -173,6 +178,7 @@ jobs: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + MACOS_NOTARIZE: ${{ github.event_name != 'workflow_dispatch' || inputs.notarize_macos }} run: | set -euo pipefail echo "::group::macOS signing diagnostics" @@ -182,9 +188,18 @@ jobs: xcrun --find notarytool xcrun notarytool --version || true security find-identity -v -p codesigning || true + echo "macOS notarization requested: ${MACOS_NOTARIZE}" echo "::endgroup::" - max_attempts=2 - build_timeout_seconds=2100 + if [ "$MACOS_NOTARIZE" = "true" ]; then + max_attempts=2 + build_timeout_seconds=2100 + builder_args=( ${{ matrix.builder_args }} --publish never ) + else + max_attempts=1 + build_timeout_seconds=900 + builder_args=( ${{ matrix.builder_args }} --publish never -c.mac.notarize=false ) + echo "::warning::macOS notarization is disabled for this draft run; artifacts will be Developer ID signed but not notarized." + fi run_signed_electron_builder() { local timeout_seconds="$1" @@ -223,7 +238,7 @@ jobs: echo "Starting signed electron-builder attempt ${attempt}/${max_attempts} at $(date -u '+%Y-%m-%dT%H:%M:%SZ') with ${build_timeout_seconds}s watchdog" rm -rf build-artifacts/electron set +e - run_signed_electron_builder "$build_timeout_seconds" node ./node_modules/electron-builder/out/cli/cli.js ${{ matrix.builder_args }} --publish never + run_signed_electron_builder "$build_timeout_seconds" node ./node_modules/electron-builder/out/cli/cli.js "${builder_args[@]}" status=$? set -e if [ "$status" -eq 0 ]; then @@ -254,9 +269,13 @@ jobs: run: bun run test:package-smoke --platform ${{ matrix.smoke_platform }} --package-kind release --artifacts-dir desktop/build-artifacts/electron - name: Verify macOS launch policy - if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true' + if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true' && (github.event_name != 'workflow_dispatch' || inputs.notarize_macos == true) run: bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/electron --require-macos-gatekeeper + - name: Warn macOS notarization skipped + if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true' && github.event_name == 'workflow_dispatch' && inputs.notarize_macos == false + run: echo "::warning::Skipping macOS Gatekeeper package-smoke because notarization is disabled for this draft. Artifacts are Developer ID signed but not notarized." + - name: Warn unsigned macOS launch policy skipped if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed != 'true' run: echo "::warning::Skipping macOS Gatekeeper package-smoke because this release is unsigned. Ship install-macos-unsigned.sh with the DMG." diff --git a/scripts/pr/release-workflow.test.ts b/scripts/pr/release-workflow.test.ts index d4068da2..c89d23ec 100644 --- a/scripts/pr/release-workflow.test.ts +++ b/scripts/pr/release-workflow.test.ts @@ -106,12 +106,19 @@ describe('release desktop workflow', () => { const gatekeeperStep = workflow.match( /- name: Verify macOS launch policy[\s\S]*?(?:\n\s{6}- name:|$)/, )?.[0] + const notarizationWarningStep = workflow.match( + /- name: Warn macOS notarization skipped[\s\S]*?(?:\n\s{6}- name:|$)/, + )?.[0] const unsignedWarningStep = workflow.match( /- name: Warn unsigned macOS launch policy skipped[\s\S]*?(?:\n\s{6}- name:|$)/, )?.[0] - expect(gatekeeperStep).toContain("if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true'") + expect(workflow).toContain('notarize_macos:') + expect(workflow).toContain("description: 'Notarize macOS artifacts'") + expect(gatekeeperStep).toContain("if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true' && (github.event_name != 'workflow_dispatch' || inputs.notarize_macos == true)") expect(gatekeeperStep).toContain('bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/electron --require-macos-gatekeeper') + expect(notarizationWarningStep).toContain("if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed == 'true' && github.event_name == 'workflow_dispatch' && inputs.notarize_macos == false") + expect(notarizationWarningStep).toContain('Developer ID signed but not notarized') expect(unsignedWarningStep).toContain("if: matrix.smoke_platform == 'macos' && needs.signing-preflight.outputs.macos_signed != 'true'") expect(unsignedWarningStep).toContain('install-macos-unsigned.sh') expect(workflow.indexOf('Verify macOS launch policy')).toBeLessThan(workflow.indexOf('Upload release artifacts for final publish')) @@ -128,14 +135,20 @@ describe('release desktop workflow', () => { expect(signedBuildStep).toContain('APPLE_ID: ${{ secrets.APPLE_ID }}') expect(signedBuildStep).toContain('APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}') expect(signedBuildStep).toContain('APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}') + expect(signedBuildStep).toContain("MACOS_NOTARIZE: ${{ github.event_name != 'workflow_dispatch' || inputs.notarize_macos }}") expect(signedBuildStep).not.toContain('CSC_IDENTITY_AUTO_DISCOVERY') expect(signedBuildStep).toContain('timeout-minutes: 80') expect(signedBuildStep).toContain("DEBUG: 'electron-builder,electron-osx-sign,electron-notarize*'") expect(signedBuildStep).toContain('macOS signing diagnostics') expect(signedBuildStep).toContain('xcrun --find notarytool') expect(signedBuildStep).toContain('security find-identity -v -p codesigning') + expect(signedBuildStep).toContain('macOS notarization requested: ${MACOS_NOTARIZE}') expect(signedBuildStep).toContain('max_attempts=2') expect(signedBuildStep).toContain('build_timeout_seconds=2100') + expect(signedBuildStep).toContain('max_attempts=1') + expect(signedBuildStep).toContain('build_timeout_seconds=900') + expect(signedBuildStep).toContain('-c.mac.notarize=false') + expect(signedBuildStep).toContain('macOS notarization is disabled for this draft run') expect(signedBuildStep).toContain('run_signed_electron_builder') expect(signedBuildStep).toContain('Signed electron-builder timed out') expect(signedBuildStep).toContain('pkill -TERM -P "$build_pid"') @@ -147,7 +160,7 @@ describe('release desktop workflow', () => { expect(signedBuildStep).toContain('Starting signed electron-builder attempt') expect(signedBuildStep).toContain('Finished signed electron-builder attempt') expect(signedBuildStep).toContain('retrying after 120 seconds') - expect(signedBuildStep).toContain(electronBuilderCli) + expect(signedBuildStep).toContain('node ./node_modules/electron-builder/out/cli/cli.js "${builder_args[@]}"') expect(unsignedBuildStep).toContain("if: matrix.smoke_platform != 'macos' || needs.signing-preflight.outputs.macos_signed != 'true'") expect(unsignedBuildStep).toContain("CSC_IDENTITY_AUTO_DISCOVERY: 'false'")