mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 13:22:35 -07:00
fix(cli): pass node arguments via NODE_OPTIONS during relaunch to support SEA (#26130)
This commit is contained in:
@@ -9,6 +9,11 @@ import {
|
||||
RELAUNCH_EXIT_CODE,
|
||||
relaunchApp,
|
||||
_resetRelaunchStateForTesting,
|
||||
isStandardSea,
|
||||
getScriptArgs,
|
||||
isSeaEnvironment,
|
||||
getSpawnConfig,
|
||||
type ProcessWithSea,
|
||||
} from './processUtils.js';
|
||||
import * as cleanup from './cleanup.js';
|
||||
import * as handleAutoUpdate from './handleAutoUpdate.js';
|
||||
@@ -36,3 +41,156 @@ describe('processUtils', () => {
|
||||
expect(processExit).toHaveBeenCalledWith(RELAUNCH_EXIT_CODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SEA handling utilities', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalExecArgv = process.execArgv;
|
||||
const originalExecPath = process.execPath;
|
||||
const originalIsSea = (process as ProcessWithSea).isSea;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.stubEnv('NODE_OPTIONS', '');
|
||||
process.argv = [...originalArgv];
|
||||
process.execArgv = [...originalExecArgv];
|
||||
process.execPath = '/fake/exec/path';
|
||||
delete (process as ProcessWithSea).isSea;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
process.argv = originalArgv;
|
||||
process.execArgv = originalExecArgv;
|
||||
process.execPath = originalExecPath;
|
||||
if (originalIsSea) {
|
||||
(process as ProcessWithSea).isSea = originalIsSea;
|
||||
} else {
|
||||
delete (process as ProcessWithSea).isSea;
|
||||
}
|
||||
});
|
||||
|
||||
describe('isStandardSea', () => {
|
||||
it('returns false if argv[0] === argv[1]', () => {
|
||||
process.argv = ['/bin/gemini', '/bin/gemini', 'my-command'];
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
expect(isStandardSea()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if IS_BINARY is true and argv[0] !== argv[1]', () => {
|
||||
process.argv = ['/bin/gemini', 'my-command'];
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
expect(isStandardSea()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if process.isSea() is true and argv[0] !== argv[1]', () => {
|
||||
process.argv = ['/bin/gemini', 'my-command'];
|
||||
(process as ProcessWithSea).isSea = () => true;
|
||||
expect(isStandardSea()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false in standard node environment', () => {
|
||||
process.argv = ['/bin/node', '/path/to/script.js', 'my-command'];
|
||||
expect(isStandardSea()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getScriptArgs', () => {
|
||||
it('slices from index 1 if isStandardSea is true', () => {
|
||||
process.argv = ['/bin/gemini', 'my-command', '--flag'];
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
expect(getScriptArgs()).toEqual(['my-command', '--flag']);
|
||||
});
|
||||
|
||||
it('slices from index 2 if isStandardSea is false (relaunch SEA or standard node)', () => {
|
||||
// Relaunch SEA
|
||||
process.argv = ['/bin/gemini', '/bin/gemini', 'my-command', '--flag'];
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
expect(getScriptArgs()).toEqual(['my-command', '--flag']);
|
||||
|
||||
// Standard node
|
||||
process.argv = ['/bin/node', '/path/to/script.js', 'my-command'];
|
||||
vi.stubEnv('IS_BINARY', '');
|
||||
expect(getScriptArgs()).toEqual(['my-command']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSeaEnvironment', () => {
|
||||
it('returns true if IS_BINARY is true', () => {
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
expect(isSeaEnvironment()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if process.isSea() is true', () => {
|
||||
(process as ProcessWithSea).isSea = () => true;
|
||||
expect(isSeaEnvironment()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if argv[0] === argv[1]', () => {
|
||||
process.argv = ['/bin/gemini', '/bin/gemini'];
|
||||
expect(isSeaEnvironment()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false otherwise', () => {
|
||||
process.argv = ['/bin/node', '/path/to/script.js'];
|
||||
expect(isSeaEnvironment()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSpawnConfig', () => {
|
||||
it('handles standard node mode', () => {
|
||||
process.argv = ['/bin/node', '/path/to/script.js', 'my-command'];
|
||||
process.execArgv = ['--inspect'];
|
||||
process.execPath = '/bin/node';
|
||||
|
||||
const config = getSpawnConfig(
|
||||
['--max-old-space-size=8192'],
|
||||
['my-command'],
|
||||
);
|
||||
|
||||
expect(config.spawnArgs).toEqual([
|
||||
'--inspect',
|
||||
'--max-old-space-size=8192',
|
||||
'/path/to/script.js',
|
||||
'my-command',
|
||||
]);
|
||||
expect(config.env['GEMINI_CLI_NO_RELAUNCH']).toBe('true');
|
||||
expect(config.env['NODE_OPTIONS']).toBeFalsy();
|
||||
});
|
||||
|
||||
it('handles SEA binary mode with new nodeArgs', () => {
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
vi.stubEnv('NODE_OPTIONS', '--existing-flag');
|
||||
process.argv = ['/bin/gemini', 'my-command'];
|
||||
process.execArgv = ['--inspect']; // Should not be duplicated in NODE_OPTIONS
|
||||
process.execPath = '/bin/gemini';
|
||||
|
||||
const config = getSpawnConfig(
|
||||
['--max-old-space-size=8192'],
|
||||
['my-command'],
|
||||
);
|
||||
|
||||
expect(config.spawnArgs).toEqual([
|
||||
'/bin/gemini', // explicitly uses execPath as placeholder
|
||||
'my-command',
|
||||
]);
|
||||
expect(config.env['NODE_OPTIONS']).toBe(
|
||||
'--existing-flag --max-old-space-size=8192',
|
||||
);
|
||||
expect(config.env['GEMINI_CLI_NO_RELAUNCH']).toBe('true');
|
||||
});
|
||||
|
||||
it('throws error for complex nodeArgs in SEA mode', () => {
|
||||
vi.stubEnv('IS_BINARY', 'true');
|
||||
|
||||
expect(() => {
|
||||
getSpawnConfig(['--title "My App"'], []);
|
||||
}).toThrow(
|
||||
'Unsupported node argument for SEA relaunch: --title "My App". Complex escaping is not supported.',
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
getSpawnConfig(['--title=A\\B'], []);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user