qwen-code/packages/desktop/scripts/build/darwin.ts
顾盼 963fc543d1
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
ci(desktop): mac code-signing + App Store Connect API-key notarization (#5013)
* chore(desktop): drop dead NOTARIZE env flag from mac signing paths

electron-builder (>=24) auto-notarizes via notarytool whenever APPLE_ID,
APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID are present in the env. The
NOTARIZE=true flag set in the release workflow, build-dmg.sh, and
scripts/build/darwin.ts was never read by electron-builder, and the
build-dmg.sh comment claiming it enabled notarization was misleading.
Remove the no-op and document the actual auto-detection behavior.

* ci(desktop): notarize via App Store Connect API key instead of Apple ID

Switch the macOS desktop release notarization path from the Apple ID +
app-specific password method to the App Store Connect API key method,
which is more robust (no 2FA, no password expiry) and reuses the notary
key already provisioned for the org.

The signing step now reads APPLE_NOTARY_API_KEY_P8_BASE64,
APPLE_NOTARY_KEY_ID, and APPLE_NOTARY_ISSUER_ID, decodes the .p8 to a
temp file, and exports APPLE_API_KEY/APPLE_API_KEY_ID/APPLE_API_ISSUER,
which electron-builder (>=24) consumes to notarize via notarytool.
Published mac releases now require those notary secrets plus
APPLE_TEAM_ID.
2026-06-12 13:14:43 +08:00

81 lines
2.5 KiB
TypeScript

/**
* macOS-specific build logic
*/
import { $ } from 'bun';
import { existsSync } from 'fs';
import { join } from 'path';
import type { BuildConfig } from './common';
/**
* Package the macOS app with electron-builder
*/
export async function packageDarwin(config: BuildConfig): Promise<string> {
const { arch, electronDir } = config;
console.log('Packaging app with electron-builder...');
// Set up environment for electron-builder
process.env.CSC_IDENTITY_AUTO_DISCOVERY = 'true';
// Build electron-builder arguments
const builderArgs = ['--mac', `--${arch}`];
// Add code signing if identity is available
if (process.env.APPLE_SIGNING_IDENTITY) {
// Strip "Developer ID Application: " prefix if present (electron-builder adds it automatically)
const cscName = process.env.APPLE_SIGNING_IDENTITY.replace(
'Developer ID Application: ',
'',
);
console.log(` Using signing identity: ${cscName}`);
process.env.CSC_NAME = cscName;
}
// Add notarization if all credentials are available.
// electron-builder auto-notarizes via notarytool when APPLE_ID,
// APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID are present in the env;
// no `notarize:` config block or NOTARIZE flag is required (or read).
if (
process.env.APPLE_ID &&
process.env.APPLE_TEAM_ID &&
process.env.APPLE_APP_SPECIFIC_PASSWORD
) {
console.log(' Notarization enabled');
}
// Run electron-builder
await $`cd ${electronDir} && npx electron-builder ${builderArgs}`;
// Verify the DMG and ZIP were built (ZIP is used by electron-updater for auto-updates)
const dmgName = `Qwen-Code-Desktop-${arch}.dmg`;
const zipName = `Qwen-Code-Desktop-${arch}.zip`;
const dmgPath = join(electronDir, 'release', dmgName);
const zipPath = join(electronDir, 'release', zipName);
if (!existsSync(dmgPath)) {
console.error('Contents of release directory:');
await $`ls -la ${join(electronDir, 'release')}`;
throw new Error(`Expected DMG not found at ${dmgPath}`);
}
if (!existsSync(zipPath)) {
console.warn(
` Warning: ZIP not found at ${zipPath} (needed for auto-updates)`,
);
}
// Get file sizes
const dmgFile = Bun.file(dmgPath);
const dmgSizeMB = ((await dmgFile.size) / 1024 / 1024).toFixed(2);
console.log(`\n=== Build Complete ===`);
console.log(`DMG: ${dmgPath} (${dmgSizeMB} MB)`);
if (existsSync(zipPath)) {
const zipFile = Bun.file(zipPath);
const zipSizeMB = ((await zipFile.size) / 1024 / 1024).toFixed(2);
console.log(`ZIP: ${zipPath} (${zipSizeMB} MB)`);
}
return dmgPath;
}