import * as cp from 'node:child_process'; import * as fs from 'fs-extra'; import * as os from 'node:os'; import * as path from 'node:path'; import { expect } from 'chai'; const features = process._linkedBinding('electron_common_features'); const fixturesPath = path.resolve(__dirname, '..', 'fixtures'); export const shouldRunCodesignTests = process.platform === 'darwin' && !(process.env.CI && process.arch === 'arm64') && !process.mas && !features.isComponentBuild(); let identity: string | null; export function getCodesignIdentity () { if (identity === undefined) { const result = cp.spawnSync(path.resolve(__dirname, '../../script/codesign/get-trusted-identity.sh')); if (result.status !== 0 || result.stdout.toString().trim().length === 0) { // Per https://circleci.com/docs/2.0/env-vars: // CIRCLE_PR_NUMBER is only present on forked PRs if (process.env.CI && !process.env.CIRCLE_PR_NUMBER) { throw new Error('No valid signing identity available to run autoUpdater specs'); } identity = null; } else { identity = result.stdout.toString().trim(); } } return identity; } export async function copyApp (newDir: string, fixture: string | null = 'initial') { const appBundlePath = path.resolve(process.execPath, '../../..'); const newPath = path.resolve(newDir, 'Electron.app'); cp.spawnSync('cp', ['-R', appBundlePath, path.dirname(newPath)]); if (fixture) { const appDir = path.resolve(newPath, 'Contents/Resources/app'); await fs.mkdirp(appDir); await fs.copy(path.resolve(fixturesPath, 'auto-update', fixture), appDir); } const plistPath = path.resolve(newPath, 'Contents', 'Info.plist'); await fs.writeFile( plistPath, (await fs.readFile(plistPath, 'utf8')).replace('BuildMachineOSBuild', `NSAppTransportSecurity NSAllowsArbitraryLoads NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads NSIncludesSubdomains BuildMachineOSBuild`) ); return newPath; }; export function spawn (cmd: string, args: string[], opts: any = {}) { let out = ''; const child = cp.spawn(cmd, args, opts); child.stdout.on('data', (chunk: Buffer) => { out += chunk.toString(); }); child.stderr.on('data', (chunk: Buffer) => { out += chunk.toString(); }); return new Promise<{ code: number, out: string }>((resolve) => { child.on('exit', (code, signal) => { expect(signal).to.equal(null); resolve({ code: code!, out }); }); }); }; export function signApp (appPath: string, identity: string) { return spawn('codesign', ['-s', identity, '--deep', '--force', appPath]); }; export async function withTempDirectory (fn: (dir: string) => Promise, autoCleanUp = true) { const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-update-spec-')); try { await fn(dir); } finally { if (autoCleanUp) { cp.spawnSync('rm', ['-r', dir]); } } };