From ca3083f436aa4d5076f7513ecde127296039e5eb Mon Sep 17 00:00:00 2001 From: elkaix Date: Tue, 25 Aug 2026 16:35:01 -0400 Subject: [PATCH] ci(desktop): finalize the DMG with the tested release script The hand-written notarize step stapled the DMG after electron-builder had already written latest-mac.yml, so the manifest size and checksum no longer matched and the release failed verification. Reuse finalize-mac-artifacts.ts, which staples and then rewrites the manifest entry and drops the stale blockmap. --- .github/workflows/desktop-release.yml | 28 ++++--------------- .../desktop/scripts/finalize-mac-artifacts.ts | 15 +++++++++- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 9441d82b7..46fd4dddd 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -237,29 +237,13 @@ jobs: run: pnpm exec electron-builder --mac dmg zip --publish never # electron-builder notarizes and staples the .app but never the disk - # image around it. A quarantined, unnotarized DMG is what Gatekeeper - # reports as "damaged" on some macOS builds, so give the image its own - # ticket and staple it. The API key is preferred; the Apple ID + - # app-specific password pair is the fallback, mirroring electron-builder. + # image around it; a quarantined, unnotarized DMG is what Gatekeeper + # reports as "damaged". Stapling changes the DMG bytes, so the same + # script also rewrites its latest-mac.yml entry and drops the stale + # blockmap, keeping the update manifest verification below honest. - name: Notarize and staple macOS DMG - shell: bash - run: | - set -euo pipefail - dmg_path="$(find apps/desktop/dist -maxdepth 1 -type f -name '*.dmg' -print -quit)" - if [ -z "$dmg_path" ]; then - echo 'macOS DMG not found' >&2 - exit 1 - fi - if [ -n "${APPLE_API_KEY:-}" ]; then - auth=(--key "$APPLE_API_KEY" --key-id "$APPLE_API_KEY_ID" --issuer "$APPLE_API_ISSUER") - elif [ -n "${APPLE_ID:-}" ]; then - auth=(--apple-id "$APPLE_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --team-id "$APPLE_TEAM_ID") - else - echo 'No notarization credentials configured; the DMG cannot be notarized.' >&2 - exit 1 - fi - xcrun notarytool submit "$dmg_path" "${auth[@]}" --wait --timeout 30m - xcrun stapler staple "$dmg_path" + working-directory: apps/desktop + run: node --import tsx scripts/finalize-mac-artifacts.ts dist - name: Verify macOS update artifacts shell: bash diff --git a/apps/desktop/scripts/finalize-mac-artifacts.ts b/apps/desktop/scripts/finalize-mac-artifacts.ts index 7e0dbe3a1..634dd2ea2 100644 --- a/apps/desktop/scripts/finalize-mac-artifacts.ts +++ b/apps/desktop/scripts/finalize-mac-artifacts.ts @@ -10,7 +10,8 @@ import { unlinkSync, writeFileSync, } from 'node:fs' -import { basename, join } from 'node:path' +import { basename, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' import { resolveNotarizationCredentials } from './release-preflight' export interface CommandResult { @@ -191,3 +192,15 @@ export function finalizeMacArtifacts(options: FinalizeMacArtifactsOptions): void writeFileSync(metadataPath, metadata) } + +const invokedPath = process.argv[1] +if (invokedPath !== undefined && resolve(invokedPath) === fileURLToPath(import.meta.url)) { + const distDir = process.argv[2] + try { + if (distDir === undefined) throw new Error('Usage: finalize-mac-artifacts.ts ') + finalizeMacArtifacts({ distDir: resolve(distDir), env: process.env }) + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)) + process.exitCode = 1 + } +}