From 99fe7e72f27f9f9d64a350ef2383802aee7d0995 Mon Sep 17 00:00:00 2001 From: AstroHan Date: Thu, 3 Sep 2026 03:38:03 +0800 Subject: [PATCH 1/2] fix(desktop): accept Intel Mach-O architecture in package verification macOS Intel Nightly packages pass notarization but fail verification because lipo reports x86_64 while the release target uses Node architecture x64. Translate that name at the existing Mach-O assertion while retaining the single-architecture requirement for both Nightly and formal releases. Exercise the packaged-app verifier with real ASAR and update configuration fixtures. Cover accepted Intel and ARM packages, wrong architectures, and universal binaries. The Intel regression fails without the mapping and passes with it. Generated-by: Codex --- scripts/verify-macos-dmg.mjs | 4 +- scripts/verify-packaged-app.test.mjs | 90 +++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/scripts/verify-macos-dmg.mjs b/scripts/verify-macos-dmg.mjs index a8ea3ee971..21a5e6a05b 100644 --- a/scripts/verify-macos-dmg.mjs +++ b/scripts/verify-macos-dmg.mjs @@ -112,7 +112,9 @@ export async function smokePackagedFilesystemWorker( function assertSingleArchitecture(output, subject, expectedArch) { const architectures = output.trim().split(/\s+/).filter(Boolean); - if (architectures.length !== 1 || architectures[0] !== expectedArch) { + // lipo names Intel Mach-O slices x86_64; Node and the release target use x64. + const machoArch = expectedArch === 'x64' ? 'x86_64' : expectedArch; + if (architectures.length !== 1 || architectures[0] !== machoArch) { throw new Error( `${subject} must contain only ${expectedArch}, found: ${architectures.join(', ')}`, ); diff --git a/scripts/verify-packaged-app.test.mjs b/scripts/verify-packaged-app.test.mjs index 6918e09e3c..b7642f1859 100644 --- a/scripts/verify-packaged-app.test.mjs +++ b/scripts/verify-packaged-app.test.mjs @@ -18,7 +18,7 @@ */ import assert from 'node:assert/strict'; -import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { after, describe, test } from 'node:test'; @@ -192,6 +192,94 @@ const options = { collectPackagedAllowlist: () => allowlistOf(PTY_PACKAGES), }; +describe('macOS packaged architecture', () => { + async function verifyArchitecture(expectedArch, lipoOutput) { + const { verifyPackagedMacApp } = await import('./verify-macos-dmg.mjs'); + const asarPackages = await Promise.all( + PTY_PACKAGES.map(async (name) => { + const manifest = JSON.parse( + await readFile(new URL(`../node_modules/${name}/package.json`, import.meta.url), 'utf8'), + ); + return `${name}@${manifest.version}`; + }), + ); + const resources = await makeResources({ + asarPackages, + notices: await readFile( + new URL('../apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt', import.meta.url), + 'utf8', + ), + rendererLicenses: [ + 'licenses/renderer/GEIST_LICENSE.txt', + 'licenses/renderer/GEIST_MONO_LICENSE.txt', + ], + }); + await writeFile( + join(resources, 'app-update.yml'), + 'provider: github\nowner: apache\nrepo: maka\nchannel: dev\nupdaterCacheDirName: "@makadesktop-updater"\n', + ); + const version = '0.2.0-dev.14.20260902'; + const app = join(dirname(resources), 'Maka.app'); + await mkdir(join(app, 'Contents'), { recursive: true }); + await rename(resources, join(app, 'Contents', 'Resources')); + // The archive and update configuration are real; macOS command output and + // app launches are the system boundaries this portable test substitutes. + await verifyPackagedMacApp(app, { + expectedArch, + channel: 'nightly', + environment: { MAKA_DESKTOP_NIGHTLY_VERSION: version }, + requirePath: async () => {}, + smokeFilesystemWorker: async () => {}, + smokeRenderer: async () => {}, + run: async (command, args) => { + if (command === 'plutil') { + const values = { + CFBundleIdentifier: 'com.maka.desktop', + CFBundleShortVersionString: version, + CFBundleExecutable: 'Maka', + }; + assert.ok(Object.hasOwn(values, args[1])); + return { stdout: `${values[args[1]]}\n` }; + } + if (command === 'lipo') return { stdout: lipoOutput }; + if ( + ['codesign', 'spctl', 'xcrun', join(app, 'Contents', 'MacOS', 'Maka')].includes(command) + ) { + return { stdout: '' }; + } + throw new Error(`Unexpected command: ${command}`); + }, + }); + } + + test('accepts the Intel Mach-O architecture for an x64 package', async () => { + await assert.doesNotReject(verifyArchitecture('x64', 'x86_64\n')); + }); + + test('accepts the ARM Mach-O architecture for an arm64 package', async () => { + await assert.doesNotReject(verifyArchitecture('arm64', 'arm64\n')); + }); + + for (const [expectedArch, wrongArch] of [ + ['x64', 'arm64'], + ['arm64', 'x86_64'], + ]) { + test(`rejects the other architecture in an ${expectedArch} package`, async () => { + await assert.rejects( + verifyArchitecture(expectedArch, `${wrongArch}\n`), + /Maka executable must contain only/, + ); + }); + + test(`rejects a universal binary in an ${expectedArch} package`, async () => { + await assert.rejects( + verifyArchitecture(expectedArch, 'x86_64 arm64\n'), + /Maka executable must contain only/, + ); + }); + } +}); + describe('assertPackagedDependencyClosure', () => { test('accepts an artifact whose asar, bundle record, and shipped notices match', async () => { const resources = await makeResources(); From bf70e51a5cc81639cefe5a8f98551ccf5c72fb03 Mon Sep 17 00:00:00 2001 From: AstroHan Date: Thu, 3 Sep 2026 03:41:02 +0800 Subject: [PATCH 2/2] test(desktop): narrow macOS coverage to the Intel regression Keep one packaged-app regression for the observed x64 versus x86_64 failure. Remove the five additional architecture cases and their parameterized harness without changing the production fix. Generated-by: Codex --- scripts/verify-packaged-app.test.mjs | 139 +++++++++++---------------- 1 file changed, 55 insertions(+), 84 deletions(-) diff --git a/scripts/verify-packaged-app.test.mjs b/scripts/verify-packaged-app.test.mjs index b7642f1859..e5ada284f9 100644 --- a/scripts/verify-packaged-app.test.mjs +++ b/scripts/verify-packaged-app.test.mjs @@ -192,92 +192,63 @@ const options = { collectPackagedAllowlist: () => allowlistOf(PTY_PACKAGES), }; -describe('macOS packaged architecture', () => { - async function verifyArchitecture(expectedArch, lipoOutput) { - const { verifyPackagedMacApp } = await import('./verify-macos-dmg.mjs'); - const asarPackages = await Promise.all( - PTY_PACKAGES.map(async (name) => { - const manifest = JSON.parse( - await readFile(new URL(`../node_modules/${name}/package.json`, import.meta.url), 'utf8'), - ); - return `${name}@${manifest.version}`; - }), - ); - const resources = await makeResources({ - asarPackages, - notices: await readFile( - new URL('../apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt', import.meta.url), - 'utf8', - ), - rendererLicenses: [ - 'licenses/renderer/GEIST_LICENSE.txt', - 'licenses/renderer/GEIST_MONO_LICENSE.txt', - ], - }); - await writeFile( - join(resources, 'app-update.yml'), - 'provider: github\nowner: apache\nrepo: maka\nchannel: dev\nupdaterCacheDirName: "@makadesktop-updater"\n', - ); - const version = '0.2.0-dev.14.20260902'; - const app = join(dirname(resources), 'Maka.app'); - await mkdir(join(app, 'Contents'), { recursive: true }); - await rename(resources, join(app, 'Contents', 'Resources')); - // The archive and update configuration are real; macOS command output and - // app launches are the system boundaries this portable test substitutes. - await verifyPackagedMacApp(app, { - expectedArch, - channel: 'nightly', - environment: { MAKA_DESKTOP_NIGHTLY_VERSION: version }, - requirePath: async () => {}, - smokeFilesystemWorker: async () => {}, - smokeRenderer: async () => {}, - run: async (command, args) => { - if (command === 'plutil') { - const values = { - CFBundleIdentifier: 'com.maka.desktop', - CFBundleShortVersionString: version, - CFBundleExecutable: 'Maka', - }; - assert.ok(Object.hasOwn(values, args[1])); - return { stdout: `${values[args[1]]}\n` }; - } - if (command === 'lipo') return { stdout: lipoOutput }; - if ( - ['codesign', 'spctl', 'xcrun', join(app, 'Contents', 'MacOS', 'Maka')].includes(command) - ) { - return { stdout: '' }; - } - throw new Error(`Unexpected command: ${command}`); - }, - }); - } - - test('accepts the Intel Mach-O architecture for an x64 package', async () => { - await assert.doesNotReject(verifyArchitecture('x64', 'x86_64\n')); +test('accepts the Intel Mach-O architecture for an x64 package', async () => { + const { verifyPackagedMacApp } = await import('./verify-macos-dmg.mjs'); + const asarPackages = await Promise.all( + PTY_PACKAGES.map(async (name) => { + const manifest = JSON.parse( + await readFile(new URL(`../node_modules/${name}/package.json`, import.meta.url), 'utf8'), + ); + return `${name}@${manifest.version}`; + }), + ); + const resources = await makeResources({ + asarPackages, + notices: await readFile( + new URL('../apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt', import.meta.url), + 'utf8', + ), + rendererLicenses: [ + 'licenses/renderer/GEIST_LICENSE.txt', + 'licenses/renderer/GEIST_MONO_LICENSE.txt', + ], }); - - test('accepts the ARM Mach-O architecture for an arm64 package', async () => { - await assert.doesNotReject(verifyArchitecture('arm64', 'arm64\n')); + await writeFile( + join(resources, 'app-update.yml'), + 'provider: github\nowner: apache\nrepo: maka\nchannel: dev\nupdaterCacheDirName: "@makadesktop-updater"\n', + ); + const version = '0.2.0-dev.14.20260902'; + const app = join(dirname(resources), 'Maka.app'); + await mkdir(join(app, 'Contents'), { recursive: true }); + await rename(resources, join(app, 'Contents', 'Resources')); + // The archive and update configuration are real; macOS command output and + // app launches are the system boundaries this portable test substitutes. + await verifyPackagedMacApp(app, { + expectedArch: 'x64', + channel: 'nightly', + environment: { MAKA_DESKTOP_NIGHTLY_VERSION: version }, + requirePath: async () => {}, + smokeFilesystemWorker: async () => {}, + smokeRenderer: async () => {}, + run: async (command, args) => { + if (command === 'plutil') { + const values = { + CFBundleIdentifier: 'com.maka.desktop', + CFBundleShortVersionString: version, + CFBundleExecutable: 'Maka', + }; + assert.ok(Object.hasOwn(values, args[1])); + return { stdout: `${values[args[1]]}\n` }; + } + if (command === 'lipo') return { stdout: 'x86_64\n' }; + if ( + ['codesign', 'spctl', 'xcrun', join(app, 'Contents', 'MacOS', 'Maka')].includes(command) + ) { + return { stdout: '' }; + } + throw new Error(`Unexpected command: ${command}`); + }, }); - - for (const [expectedArch, wrongArch] of [ - ['x64', 'arm64'], - ['arm64', 'x86_64'], - ]) { - test(`rejects the other architecture in an ${expectedArch} package`, async () => { - await assert.rejects( - verifyArchitecture(expectedArch, `${wrongArch}\n`), - /Maka executable must contain only/, - ); - }); - - test(`rejects a universal binary in an ${expectedArch} package`, async () => { - await assert.rejects( - verifyArchitecture(expectedArch, 'x86_64 arm64\n'), - /Maka executable must contain only/, - ); - }); - } }); describe('assertPackagedDependencyClosure', () => {