mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 22:33:05 -07:00
fix(core): prevent ENOENT crash due to proper-lockfile race condition
This commit is contained in:
@@ -294,6 +294,29 @@ describe('ProjectRegistry', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('safely handles ECOMPROMISED errors from proper-lockfile', async () => {
|
||||
const registry = new ProjectRegistry(registryPath);
|
||||
await registry.initialize();
|
||||
|
||||
let compromisedHandler: ((err: Error) => void) | undefined;
|
||||
vi.mocked(lock).mockImplementation(async (file, options) => {
|
||||
// @ts-expect-error onCompromised is not typed in the mock but is passed
|
||||
compromisedHandler = options.onCompromised;
|
||||
return vi.fn().mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
await registry.getShortId('/foo');
|
||||
|
||||
expect(compromisedHandler).toBeDefined();
|
||||
|
||||
// Calling the handler should not throw
|
||||
expect(() =>
|
||||
compromisedHandler!(
|
||||
Object.assign(new Error('Compromised'), { code: 'ECOMPROMISED' }),
|
||||
),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('throws if not initialized', async () => {
|
||||
const registry = new ProjectRegistry(registryPath);
|
||||
await expect(registry.getShortId('/foo')).rejects.toThrow(
|
||||
|
||||
@@ -179,6 +179,12 @@ export class ProjectRegistry {
|
||||
retries: Math.floor(LOCK_TIMEOUT_MS / LOCK_RETRY_DELAY_MS),
|
||||
minTimeout: LOCK_RETRY_DELAY_MS,
|
||||
},
|
||||
onCompromised: (err) => {
|
||||
debugLogger.debug(
|
||||
'Project registry lock was compromised (likely released concurrently):',
|
||||
err,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { Config } from '../config/config.js';
|
||||
import { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import type { PolicyEngine } from '../policy/policy-engine.js';
|
||||
@@ -37,7 +37,9 @@ describe('Tracker Tools Integration', () => {
|
||||
model: 'gemini-3-flash',
|
||||
debugMode: false,
|
||||
});
|
||||
await config.initialize();
|
||||
vi.spyOn(config.storage, 'getProjectTempTrackerDir').mockReturnValue(
|
||||
tempDir,
|
||||
);
|
||||
messageBus = new MessageBus(null as unknown as PolicyEngine, false);
|
||||
});
|
||||
|
||||
|
||||
@@ -119,6 +119,28 @@ describe('Trust Utility (Core)', () => {
|
||||
expect(savedContent[finalKey]).toBe(TrustLevel.TRUST_FOLDER);
|
||||
});
|
||||
|
||||
it('safely handles ECOMPROMISED errors from proper-lockfile', async () => {
|
||||
let compromisedHandler: ((err: Error) => void) | undefined;
|
||||
vi.mocked(lock).mockImplementation(async (file, options) => {
|
||||
// @ts-expect-error onCompromised is not typed in the mock but is passed
|
||||
compromisedHandler = options.onCompromised;
|
||||
return vi.fn().mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
const folders = loadTrustedFolders();
|
||||
const testPath = path.resolve('/new/trusted/path');
|
||||
await folders.setValue(testPath, TrustLevel.TRUST_FOLDER);
|
||||
|
||||
expect(compromisedHandler).toBeDefined();
|
||||
|
||||
// Calling the handler should not throw
|
||||
expect(() =>
|
||||
compromisedHandler!(
|
||||
Object.assign(new Error('Compromised'), { code: 'ECOMPROMISED' }),
|
||||
),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should handle comments in JSON', () => {
|
||||
const content = `
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ import { normalizePath, isSubpath } from './paths.js';
|
||||
import { FatalConfigError, getErrorMessage } from './errors.js';
|
||||
import { coreEvents } from './events.js';
|
||||
import { ideContextStore } from '../ide/ideContext.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
export enum TrustLevel {
|
||||
TRUST_FOLDER = 'TRUST_FOLDER',
|
||||
@@ -217,6 +218,13 @@ export class LoadedTrustedFolders {
|
||||
retries: 10,
|
||||
minTimeout: 100,
|
||||
},
|
||||
onCompromised: (err) => {
|
||||
// Ignore compromised lock since we expect another process might release it concurrently
|
||||
debugLogger.debug(
|
||||
'Trusted folders lock was compromised (likely released concurrently):',
|
||||
err,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const normalizedPath = normalizePath(folderPath);
|
||||
|
||||
Reference in New Issue
Block a user