mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 12:40:44 +00:00
refactor(cli): improve findEnvFile logic and remove flaky test
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
49b1a39c0b
commit
842ff42928
2 changed files with 108 additions and 15 deletions
|
|
@ -2685,11 +2685,45 @@ describe('Settings Loading and Merging', () => {
|
|||
expect(process.env['TESTTEST']).toEqual('1234');
|
||||
});
|
||||
|
||||
it('does not load env files from untrusted spaces', () => {
|
||||
setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
|
||||
it('does not load project .env files from untrusted workspaces', () => {
|
||||
delete process.env['PROJECT_ENV_VAR'];
|
||||
const cwdSpy = vi
|
||||
.spyOn(process, 'cwd')
|
||||
.mockReturnValue(MOCK_WORKSPACE_DIR);
|
||||
|
||||
const projectEnvPath = path.join(MOCK_WORKSPACE_DIR, '.env');
|
||||
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: false,
|
||||
source: 'file',
|
||||
});
|
||||
(mockFsExistsSync as Mock).mockImplementation((p: fs.PathLike) =>
|
||||
[USER_SETTINGS_PATH, projectEnvPath].includes(p.toString()),
|
||||
);
|
||||
const userSettingsContent: Settings = {
|
||||
ui: {
|
||||
theme: 'dark',
|
||||
},
|
||||
security: {
|
||||
folderTrust: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === projectEnvPath) return 'PROJECT_ENV_VAR=from_project';
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
|
||||
loadEnvironment(loadSettings(MOCK_WORKSPACE_DIR).merged);
|
||||
|
||||
expect(process.env['TESTTEST']).not.toEqual('1234');
|
||||
// Project .env should NOT be loaded when workspace is untrusted
|
||||
expect(process.env['PROJECT_ENV_VAR']).toBeUndefined();
|
||||
cwdSpy.mockRestore();
|
||||
});
|
||||
|
||||
describe('settings.env field', () => {
|
||||
|
|
@ -2878,6 +2912,47 @@ describe('Settings Loading and Merging', () => {
|
|||
expect(process.env['USER_ENV_VAR']).toEqual('user_value');
|
||||
expect(process.env['WORKSPACE_ENV_VAR']).toEqual('workspace_value');
|
||||
});
|
||||
|
||||
it('should load user-level settings.env even when workspace is untrusted', () => {
|
||||
const userSettingsContent: Settings = {
|
||||
env: {
|
||||
USER_ENV_VAR: 'user_value',
|
||||
},
|
||||
};
|
||||
const workspaceSettingsContent = {
|
||||
env: {
|
||||
WORKSPACE_ENV_VAR: 'workspace_value',
|
||||
},
|
||||
};
|
||||
|
||||
(mockFsExistsSync as Mock).mockImplementation((p: fs.PathLike) =>
|
||||
[USER_SETTINGS_PATH, MOCK_WORKSPACE_SETTINGS_PATH].includes(
|
||||
p.toString(),
|
||||
),
|
||||
);
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
|
||||
// Workspace is untrusted
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: false,
|
||||
source: 'file',
|
||||
});
|
||||
|
||||
loadSettings(MOCK_WORKSPACE_DIR);
|
||||
|
||||
// User-level settings.env should still be loaded even when untrusted
|
||||
expect(process.env['USER_ENV_VAR']).toEqual('user_value');
|
||||
// Workspace-level settings.env should NOT be loaded (filtered by mergeSettings)
|
||||
expect(process.env['WORKSPACE_ENV_VAR']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -798,26 +798,48 @@ export function createMinimalSettings(): LoadedSettings {
|
|||
);
|
||||
}
|
||||
|
||||
function findEnvFile(startDir: string): string | null {
|
||||
/**
|
||||
* Finds the .env file to load, respecting workspace trust settings.
|
||||
*
|
||||
* When workspace is untrusted, only allow user-level .env files at:
|
||||
* - ~/.qwen/.env
|
||||
* - ~/.env
|
||||
*/
|
||||
function findEnvFile(settings: Settings, startDir: string): string | null {
|
||||
const homeDir = homedir();
|
||||
const isTrusted = isWorkspaceTrusted(settings).isTrusted;
|
||||
|
||||
// Pre-compute user-level .env paths for fast comparison
|
||||
const userLevelPaths = new Set([
|
||||
path.normalize(path.join(homeDir, '.env')),
|
||||
path.normalize(path.join(homeDir, QWEN_DIR, '.env')),
|
||||
]);
|
||||
|
||||
// Determine if we can use this .env file based on trust settings
|
||||
const canUseEnvFile = (filePath: string): boolean =>
|
||||
isTrusted !== false || userLevelPaths.has(path.normalize(filePath));
|
||||
|
||||
let currentDir = path.resolve(startDir);
|
||||
while (true) {
|
||||
// prefer gemini-specific .env under QWEN_DIR
|
||||
// Prefer gemini-specific .env under QWEN_DIR
|
||||
const geminiEnvPath = path.join(currentDir, QWEN_DIR, '.env');
|
||||
if (fs.existsSync(geminiEnvPath)) {
|
||||
if (fs.existsSync(geminiEnvPath) && canUseEnvFile(geminiEnvPath)) {
|
||||
return geminiEnvPath;
|
||||
}
|
||||
|
||||
const envPath = path.join(currentDir, '.env');
|
||||
if (fs.existsSync(envPath)) {
|
||||
if (fs.existsSync(envPath) && canUseEnvFile(envPath)) {
|
||||
return envPath;
|
||||
}
|
||||
|
||||
const parentDir = path.dirname(currentDir);
|
||||
if (parentDir === currentDir || !parentDir) {
|
||||
// check .env under home as fallback, again preferring gemini-specific .env
|
||||
const homeGeminiEnvPath = path.join(homedir(), QWEN_DIR, '.env');
|
||||
// At home directory - check fallback .env files
|
||||
const homeGeminiEnvPath = path.join(homeDir, QWEN_DIR, '.env');
|
||||
if (fs.existsSync(homeGeminiEnvPath)) {
|
||||
return homeGeminiEnvPath;
|
||||
}
|
||||
const homeEnvPath = path.join(homedir(), '.env');
|
||||
const homeEnvPath = path.join(homeDir, '.env');
|
||||
if (fs.existsSync(homeEnvPath)) {
|
||||
return homeEnvPath;
|
||||
}
|
||||
|
|
@ -859,11 +881,7 @@ export function setUpCloudShellEnvironment(envFilePath: string | null): void {
|
|||
* 5. defaults
|
||||
*/
|
||||
export function loadEnvironment(settings: Settings): void {
|
||||
const envFilePath = findEnvFile(process.cwd());
|
||||
|
||||
if (!isWorkspaceTrusted(settings).isTrusted) {
|
||||
return;
|
||||
}
|
||||
const envFilePath = findEnvFile(settings, process.cwd());
|
||||
|
||||
// Cloud Shell environment variable handling
|
||||
if (process.env['CLOUD_SHELL'] === 'true') {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue