diff --git a/src/shared/shipit-liveness.test.ts b/src/shared/shipit-liveness.test.ts index 57aeb72086f..c5703c73161 100644 --- a/src/shared/shipit-liveness.test.ts +++ b/src/shared/shipit-liveness.test.ts @@ -7,6 +7,7 @@ import { getShipItLivenessForBundle } from './shipit-liveness' const BUNDLE = '/Applications/Orca.app' const SHIPIT = `${BUNDLE}/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/ShipIt` +const SHIPIT_FRAMEWORK_ROOT = `${BUNDLE}/Contents/Frameworks/Squirrel.framework/Resources/ShipIt` const psOutput = (...lines: string[]): void => { runProcessSyncMock.mockReturnValue({ @@ -37,6 +38,11 @@ describe('isShipItRunningForBundle', () => { expect(getShipItLivenessForBundle(BUNDLE)).toBe('live') }) + it('matches the framework-root resource path used by production ShipIt launches', () => { + psOutput(`${SHIPIT_FRAMEWORK_ROOT} com.stablyai.orca.ShipIt /tmp/state.plist`) + expect(getShipItLivenessForBundle(BUNDLE)).toBe('live') + }) + it('ignores a process that merely mentions the path', () => { // A substring match would count a grep, an editor, or this probe's own shell. psOutput(`/usr/bin/grep -r ${SHIPIT} /Users/someone/notes`) diff --git a/src/shared/shipit-liveness.ts b/src/shared/shipit-liveness.ts index c0c4cefe528..7a9134be3ff 100644 --- a/src/shared/shipit-liveness.ts +++ b/src/shared/shipit-liveness.ts @@ -27,16 +27,22 @@ export function getShipItLivenessForBundle(bundlePath: string): ShipItLiveness { // Why the full path and not a bare name: a `ps` match on any command line *mentioning* the // bundle would count unrelated processes (a grep, an editor, this very check's own shell) and // hold the gate closed forever. - const shipItPath = join( - resolvedBundlePath, - 'Contents', - 'Frameworks', - 'Squirrel.framework', - 'Versions', - 'A', - 'Resources', - 'ShipIt' - ) + // Squirrel.Mac has shipped both the framework's versioned resource path and its + // framework-root symlink as argv[0] across releases. Keep both explicit so we + // recognize the real installer without broadening the process-table match. + const shipItPaths = [ + join( + resolvedBundlePath, + 'Contents', + 'Frameworks', + 'Squirrel.framework', + 'Versions', + 'A', + 'Resources', + 'ShipIt' + ), + join(resolvedBundlePath, 'Contents', 'Frameworks', 'Squirrel.framework', 'Resources', 'ShipIt') + ] try { const result = runProcessSync({ program: '/bin/ps', @@ -51,11 +57,13 @@ export function getShipItLivenessForBundle(bundlePath: string): ShipItLiveness { // be a separator stops `.../ShipIt-other` from counting as `.../ShipIt`. const running = result.stdout.split('\n').some((line) => { const argv0Line = line.trimStart() - if (!argv0Line.startsWith(shipItPath)) { - return false - } - const next = argv0Line.charAt(shipItPath.length) - return next === '' || next === ' ' + return shipItPaths.some((shipItPath) => { + if (!argv0Line.startsWith(shipItPath)) { + return false + } + const next = argv0Line.charAt(shipItPath.length) + return next === '' || next === ' ' + }) }) return running ? 'live' : 'exited' } catch {