From fe5a4f8942fecaee37caf7e6f75feeebe833c104 Mon Sep 17 00:00:00 2001 From: AstroHan Date: Sat, 24 Oct 2026 17:32:46 +0800 Subject: [PATCH 1/3] fix(release): retain failed verifier command output Generated-by: OpenAI Codex --- scripts/verify-packaged-app.mjs | 13 +++++++++---- scripts/verify-packaged-app.test.mjs | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/verify-packaged-app.mjs b/scripts/verify-packaged-app.mjs index 59cbc4e1dba..9f2cae1a7d0 100644 --- a/scripts/verify-packaged-app.mjs +++ b/scripts/verify-packaged-app.mjs @@ -79,10 +79,15 @@ export function runCommand(command, args, options = {}) { return; } reject( - new Error( - `${command} ${args.join(' ')} failed with ${ - signal ? `signal ${signal}` : `exit code ${code}` - }\n${stderr.trim()}`, + Object.assign( + new Error( + `${command} ${args.join(' ')} failed with ${ + signal ? `signal ${signal}` : `exit code ${code}` + }` + + `${stdout.trim() ? `\nstdout: ${stdout.trim()}` : ''}` + + `${stderr.trim() ? `\nstderr: ${stderr.trim()}` : ''}`, + ), + { code, signal, stdout, stderr }, ), ); }); diff --git a/scripts/verify-packaged-app.test.mjs b/scripts/verify-packaged-app.test.mjs index 5aa2001aa89..3d4cc3f2a50 100644 --- a/scripts/verify-packaged-app.test.mjs +++ b/scripts/verify-packaged-app.test.mjs @@ -39,6 +39,7 @@ import { asarLookupPath, assertPackagedDependencyClosure, assertPackagedResources, + runCommand, stopChild, } from './verify-packaged-app.mjs'; @@ -46,6 +47,23 @@ const { version: productVersion } = JSON.parse( await readFile(new URL('../package.json', import.meta.url), 'utf8'), ); +test('runCommand preserves the exit code and both output streams on failure', async () => { + await assert.rejects( + runCommand(process.execPath, [ + '-e', + "require('node:fs').writeSync(1, 'CloudKit connection lost'); require('node:fs').writeSync(2, 'stderr detail'); process.exit(68)", + ]), + (error) => { + assert.equal(error.code, 68); + assert.equal(error.stdout, 'CloudKit connection lost'); + assert.equal(error.stderr, 'stderr detail'); + assert.match(error.message, /CloudKit connection lost/u); + assert.match(error.message, /stderr detail/u); + return true; + }, + ); +}); + test('stopChild waits for a process that ignores SIGTERM to exit before returning', { skip: process.platform === 'win32', }, async (t) => { From 48943016658874297337dc8a60524884f2cd731d Mon Sep 17 00:00:00 2001 From: AstroHan Date: Sat, 24 Oct 2026 17:45:09 +0800 Subject: [PATCH 2/3] fix(release): reuse ticket recovery for macOS app validation Generated-by: OpenAI Codex --- scripts/staple-macos-artifact.mjs | 9 +++++++-- scripts/verify-macos-dmg.mjs | 6 +++++- scripts/verify-packaged-app.test.mjs | 23 ++++++++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/scripts/staple-macos-artifact.mjs b/scripts/staple-macos-artifact.mjs index 42c0177dbf2..9f6caa6247c 100644 --- a/scripts/staple-macos-artifact.mjs +++ b/scripts/staple-macos-artifact.mjs @@ -26,10 +26,15 @@ const execute = promisify(execFile); export async function stapleMacosArtifact( path, - { run = execute, wait = sleep, report = (text) => process.stderr.write(text) } = {}, + { + validateOnly = false, + run = execute, + wait = sleep, + report = (text) => process.stderr.write(text), + } = {}, ) { const options = { encoding: 'utf8', timeout: 120_000 }; - for (const action of ['staple', 'validate']) { + for (const action of validateOnly ? ['validate'] : ['staple', 'validate']) { for (let attempt = 1; attempt <= 3; attempt += 1) { try { const result = await run('xcrun', ['stapler', action, path], options); diff --git a/scripts/verify-macos-dmg.mjs b/scripts/verify-macos-dmg.mjs index 1d041951e4a..bd801d9235b 100644 --- a/scripts/verify-macos-dmg.mjs +++ b/scripts/verify-macos-dmg.mjs @@ -34,6 +34,7 @@ import { crc32, inflateSync } from 'node:zlib'; import { FILESYSTEM_WORKER_PROTOCOL_VERSION } from '../packages/runtime/dist/filesystem-worker/protocol.js'; import { readProductManifestIdentity } from './product-release-identity.mjs'; import { assertPackagedUpdateConfiguration } from './desktop-update-contract.mjs'; +import { stapleMacosArtifact } from './staple-macos-artifact.mjs'; import { resolveDesktopBuildVersion, resolveDesktopReleaseTarget, @@ -355,7 +356,10 @@ export async function verifyPackagedMacApp( assertSingleArchitecture(executableArchitectures.stdout, 'Maka executable', expectedArch); await run('codesign', ['--verify', '--deep', '--strict', '--verbose=2', appPath]); await run('spctl', ['--assess', '--type', 'execute', '--verbose=4', appPath]); - await run('xcrun', ['stapler', 'validate', appPath]); + await stapleMacosArtifact(appPath, { + validateOnly: true, + run: (command, args, options) => run(command, args, { timeoutMs: options.timeout }), + }); const ptyProbe = makePtyProbe( '/bin/echo', diff --git a/scripts/verify-packaged-app.test.mjs b/scripts/verify-packaged-app.test.mjs index 3d4cc3f2a50..7747e89e794 100644 --- a/scripts/verify-packaged-app.test.mjs +++ b/scripts/verify-packaged-app.test.mjs @@ -496,7 +496,7 @@ const options = { collectPackagedAllowlist: () => allowlistOf(PTY_PACKAGES), }; -test('accepts the Intel Mach-O architecture for an x64 package', async () => { +test('verifies an Intel macOS package after a transient ticket validation failure', async () => { const { verifyPackagedMacApp } = await import('./verify-macos-dmg.mjs'); const asarPackages = await Promise.all( PTY_PACKAGES.map(async (name) => { @@ -526,6 +526,7 @@ test('accepts the Intel Mach-O architecture for an x64 package', async () => { const app = join(dirname(resources), 'Maka.app'); await mkdir(join(app, 'Contents'), { recursive: true }); await rename(resources, join(app, 'Contents', 'Resources')); + const verificationSteps = []; // The archive and update configuration are real; macOS command output and // app launches are the system boundaries this portable test substitutes. await verifyPackagedMacApp(app, { @@ -534,7 +535,9 @@ test('accepts the Intel Mach-O architecture for an x64 package', async () => { environment: { MAKA_DESKTOP_NIGHTLY_VERSION: version }, requirePath: async () => {}, smokeFilesystemWorker: async () => {}, - smokeRenderer: async () => {}, + smokeRenderer: async () => { + verificationSteps.push('renderer'); + }, run: async (command, args) => { if (command === 'plutil') { const values = { @@ -546,14 +549,24 @@ test('accepts the Intel Mach-O architecture for an x64 package', async () => { 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) - ) { + if (command === 'xcrun') { + verificationSteps.push(args[1]); + assert.deepEqual(args, ['stapler', 'validate', app]); + if (verificationSteps.length === 1) { + return runCommand(process.execPath, [ + '-e', + "require('node:fs').writeSync(1, 'Error Domain=NSURLErrorDomain Code=-1005 connection lost'); process.exit(68)", + ]); + } + return { stdout: '' }; + } + if (['codesign', 'spctl', join(app, 'Contents', 'MacOS', 'Maka')].includes(command)) { return { stdout: '' }; } throw new Error(`Unexpected command: ${command}`); }, }); + assert.deepEqual(verificationSteps, ['validate', 'validate', 'renderer']); }); describe('assertRenderableAppIcon', () => { From 527103d5b2e90998f64a3ab06560cfaaf049d82b Mon Sep 17 00:00:00 2001 From: AstroHan Date: Sun, 25 Oct 2026 08:12:19 +0800 Subject: [PATCH 3/3] fix(release): use TCP for hosted macOS ticket delivery Generated-by: OpenAI Codex --- .github/workflows/desktop-nightly.yml | 7 +++++++ .github/workflows/release.yml | 13 +++++++++++++ .../desktop-nightly-workflow-policy.test.mjs | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/.github/workflows/desktop-nightly.yml b/.github/workflows/desktop-nightly.yml index 64a949ff2fb..b381b18355a 100644 --- a/.github/workflows/desktop-nightly.yml +++ b/.github/workflows/desktop-nightly.yml @@ -143,6 +143,13 @@ jobs: umask 077 printf '%s' "$APPLE_API_KEY_CONTENT" > "$APPLE_API_KEY_PATH" + - name: Use TCP for Apple ticket delivery + if: matrix.platform == 'macos' + # Remove when actions/runner-images#14924 fixes HTTP/3 ticket connection loss. + run: | + echo 'block return out quick proto udp from any to any port 443' | sudo pfctl -f - + sudo pfctl -E + - name: Package the signed and notarized macOS Nightly if: matrix.platform == 'macos' env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac65ddfd2e3..8593cef4568 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,6 +156,13 @@ jobs: umask 077 printf '%s' "$APPLE_API_KEY_CONTENT" > "$APPLE_API_KEY_PATH" + - name: Use TCP for Apple ticket delivery + if: matrix.platform == 'macos' + # Remove when actions/runner-images#14924 fixes HTTP/3 ticket connection loss. + run: | + echo 'block return out quick proto udp from any to any port 443' | sudo pfctl -f - + sudo pfctl -E + - name: Package notarized app and signed DMG if: matrix.platform == 'macos' env: @@ -370,6 +377,12 @@ jobs: umask 077 printf '%s' "$APPLE_API_KEY_CONTENT" > "$APPLE_API_KEY_PATH" + - name: Use TCP for Apple ticket delivery + # Remove when actions/runner-images#14924 fixes HTTP/3 ticket connection loss. + run: | + echo 'block return out quick proto udp from any to any port 443' | sudo pfctl -f - + sudo pfctl -E + - name: Package signed and notarized CLI and TUI env: CSC_LINK: ${{ secrets.CSC_LINK }} diff --git a/scripts/desktop-nightly-workflow-policy.test.mjs b/scripts/desktop-nightly-workflow-policy.test.mjs index b5ed8e0f9b4..6790cced2ef 100644 --- a/scripts/desktop-nightly-workflow-policy.test.mjs +++ b/scripts/desktop-nightly-workflow-policy.test.mjs @@ -221,6 +221,25 @@ test('both macOS publication workflows staple through the shared recovery gate a } }); +test('macOS publication jobs disable the broken HTTP/3 route before any notarization', async () => { + for (const name of ['desktop-nightly.yml', 'release.yml']) { + const workflow = await readWorkflow(name); + for (const job of [workflow.jobs.desktop, workflow.jobs['cli-macos-arm64']].filter(Boolean)) { + const setup = job.steps.findIndex( + (step) => step.name === 'Use TCP for Apple ticket delivery', + ); + const packaging = job.steps.findIndex((step) => + /npm run package:(macos|cli:macos)/u.test(step.run ?? ''), + ); + assert.ok(setup >= 0 && setup < packaging, name); + const step = job.steps[setup]; + assert.equal(step.if, job.strategy ? "matrix.platform == 'macos'" : undefined); + assert.match(step.run, /block return out quick proto udp from any to any port 443/u); + assert.match(step.run, /sudo pfctl -f -\nsudo pfctl -E/u); + } + } +}); + test('Desktop Nightly has no Apache Nightlies transport or compatibility state', async () => { const workflow = await readWorkflow('desktop-nightly.yml'); assert.equal(workflow.jobs.publish.environment, 'nightly');