From 90f1fe9d324575945f37438b398e21cd9c69cdaa Mon Sep 17 00:00:00 2001 From: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:08:00 -0800 Subject: [PATCH 1/5] correct workspace path for mutiroot scenarios no-config debugging (#602) * correct workspace path for mutiroot scenarios no-config debugging * rename --- src/extension/noConfigDebugInit.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index 27e22c53..55b185d5 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -37,15 +37,18 @@ export async function registerNoConfigDebug( // create a temp directory for the noConfigDebugAdapterEndpoints // file path format: extPath/.noConfigDebugAdapterEndpoints/endpoint-stableWorkspaceHash.txt - const workspaceUri = workspace.workspaceFolders?.[0]?.uri; - if (!workspaceUri) { + let workspaceString = workspace.workspaceFile?.fsPath; + if (!workspaceString) { + workspaceString = workspace.workspaceFolders?.map((e) => e.uri.fsPath).join(';'); + } + if (!workspaceString) { traceError('No workspace folder found'); return Promise.resolve(new Disposable(() => {})); } // create a stable hash for the workspace folder, reduce terminal variable churn const hash = crypto.createHash('sha256'); - hash.update(workspaceUri.toString()); + hash.update(workspaceString.toString()); const stableWorkspaceHash = hash.digest('hex').slice(0, 16); const tempDirPath = path.join(extPath, '.noConfigDebugAdapterEndpoints'); From bdd8163571dc3ed94bc02ed279b5e20da8aba19b Mon Sep 17 00:00:00 2001 From: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:19:23 -0800 Subject: [PATCH 2/5] fix powershell script for powershell no-config debug (#610) --- bundled/scripts/noConfigScripts/debugpy.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bundled/scripts/noConfigScripts/debugpy.ps1 b/bundled/scripts/noConfigScripts/debugpy.ps1 index 945c5091..bf949d64 100755 --- a/bundled/scripts/noConfigScripts/debugpy.ps1 +++ b/bundled/scripts/noConfigScripts/debugpy.ps1 @@ -1,6 +1,7 @@ # PowerShell script -if ($PSVersionTable.OS -match "Windows") { +$os = [System.Environment]::OSVersion.Platform +if ($os -eq [System.PlatformID]::Win32NT) { python $env:BUNDLED_DEBUGPY_PATH --listen 0 --wait-for-client $args } else { python3 $env:BUNDLED_DEBUGPY_PATH --listen 0 --wait-for-client $args -} +} \ No newline at end of file From f43cf4687b2fc1e0c8da3c6eabb8801a4de1ef9d Mon Sep 17 00:00:00 2001 From: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 13 Feb 2025 08:52:30 -0800 Subject: [PATCH 3/5] get launch config from settings.json as fallback (#585) * get launch config from settings.json as fallback * update error return * remove dup getConfig --- .../launch.json/launchJsonReader.ts | 34 +++-- .../launch.json/launchJsonReader.unit.test.ts | 116 ++++++++++++++++++ 2 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/test/unittest/configuration/launch.json/launchJsonReader.unit.test.ts diff --git a/src/extension/debugger/configuration/launch.json/launchJsonReader.ts b/src/extension/debugger/configuration/launch.json/launchJsonReader.ts index e2cad8b2..ac906613 100644 --- a/src/extension/debugger/configuration/launch.json/launchJsonReader.ts +++ b/src/extension/debugger/configuration/launch.json/launchJsonReader.ts @@ -12,19 +12,18 @@ export async function getConfigurationsForWorkspace(workspace: WorkspaceFolder): traceLog('Getting configurations for workspace'); const filename = path.join(workspace.uri.fsPath, '.vscode', 'launch.json'); if (!(await fs.pathExists(filename))) { - // Check launch config in the workspace file - const codeWorkspaceConfig = getConfiguration('launch', workspace); - if (!codeWorkspaceConfig.configurations || !Array.isArray(codeWorkspaceConfig.configurations)) { - return []; - } - traceLog('Using configuration in workspace'); - return codeWorkspaceConfig.configurations; + return getConfigurationsFromSettings(workspace); } - const text = await fs.readFile(filename, 'utf-8'); const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false }); - if (!parsed.configurations || !Array.isArray(parsed.configurations)) { - throw Error('Missing field in launch.json: configurations'); + // no launch.json or no configurations found in launch.json, look in settings.json + if (!parsed || !parsed.configurations) { + traceLog('No configurations found in launch.json, looking in settings.json.'); + return getConfigurationsFromSettings(workspace); + } + // configurations found in launch.json, verify them then return + if (!Array.isArray(parsed.configurations) || parsed.configurations.length === 0) { + throw Error('Invalid configurations in launch.json'); } if (!parsed.version) { throw Error('Missing field in launch.json: version'); @@ -42,3 +41,18 @@ export async function getConfigurationsByUri(uri?: Uri): Promise { + let sandbox: sinon.SinonSandbox; + + setup(() => { + sandbox = sinon.createSandbox(); + }); + + teardown(() => { + sandbox.restore(); + }); + + suite('getConfigurationsForWorkspace', () => { + test('Should return configurations from launch.json if it exists', async () => { + const workspace = typemoq.Mock.ofType(); + workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace')); + + const launchJsonContent = `{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Program", + "type": "python", + "request": "launch", + "program": "${workspace.object.uri}/app.py" + } + ] + }`; + + sandbox.stub(fs, 'pathExists').resolves(true); + sandbox.stub(fs, 'readFile').resolves(launchJsonContent); + + const configurations = await getConfigurationsForWorkspace(workspace.object); + assert.strictEqual(configurations.length, 1); + assert.strictEqual(configurations[0].name, 'Launch Program'); + }); + + test('Should return configurations from settings.json if launch.json does not exist', async () => { + const workspace = typemoq.Mock.ofType(); + workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace')); + + const mockConfig = typemoq.Mock.ofType(); + mockConfig + .setup((c) => c.configurations) + .returns(() => [ + { + name: 'Launch Program 2', + type: 'python', + request: 'launch', + program: '${workspaceFolder}/app.py', + }, + ]); + + sandbox.stub(fs, 'pathExists').resolves(false); + sandbox.stub(vscodeapi, 'getConfiguration').returns(mockConfig.object); + + const configurations = await getConfigurationsForWorkspace(workspace.object); + assert.strictEqual(configurations.length, 1); + assert.strictEqual(configurations[0].name, 'Launch Program 2'); + }); + }); + + suite('getConfigurationsFromSettings', () => { + test('Should return configurations from settings.json', () => { + const workspace = typemoq.Mock.ofType(); + workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace')); + + const mockConfig = typemoq.Mock.ofType(); + mockConfig + .setup((c) => c.configurations) + .returns(() => [ + { + name: 'Launch Program 3', + type: 'python', + request: 'launch', + program: '${workspaceFolder}/app.py', + }, + ]); + + sandbox.stub(vscodeapi, 'getConfiguration').returns(mockConfig.object); + + const configurations = getConfigurationsFromSettings(workspace.object); + assert.strictEqual(configurations.length, 1); + assert.strictEqual(configurations[0].name, 'Launch Program 3'); + }); + + test('Should error if no configurations in settings.json', () => { + const workspace = typemoq.Mock.ofType(); + workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace')); + + const mockConfig = typemoq.Mock.ofType(); + mockConfig.setup((c) => c.get('configurations')).returns(() => []); + mockConfig.setup((c) => c.configurations).returns(() => []); + + sandbox.stub(vscodeapi, 'getConfiguration').returns(mockConfig.object); + + assert.throws( + () => getConfigurationsFromSettings(workspace.object), + Error, + 'No configurations found in launch.json or settings.json', + ); + }); + }); +}); From 500cedcbf3fb71a074b352538e2a0a337c569081 Mon Sep 17 00:00:00 2001 From: tcpsoft <29502181+TCPsoftware@users.noreply.github.com> Date: Sat, 15 Feb 2025 03:14:58 +0800 Subject: [PATCH 4/5] Make "args": "${command:pickArgs}" as default debug configuration (#548) Co-authored-by: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> --- .../debugger/configuration/providers/fileLaunchWithArgs.ts | 2 +- .../configuration/providers/fileLaunchWithArgs.unit.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extension/debugger/configuration/providers/fileLaunchWithArgs.ts b/src/extension/debugger/configuration/providers/fileLaunchWithArgs.ts index 591a34e3..52b83d3c 100644 --- a/src/extension/debugger/configuration/providers/fileLaunchWithArgs.ts +++ b/src/extension/debugger/configuration/providers/fileLaunchWithArgs.ts @@ -21,7 +21,7 @@ export async function buildFileWithArgsLaunchDebugConfiguration( request: 'launch', program: '${file}', console: 'integratedTerminal', - args: ['${command:pickArgs}'], + args: '${command:pickArgs}', }; sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, { configurationType: DebugConfigurationType.launchFileWithArgs, diff --git a/src/test/unittest/configuration/providers/fileLaunchWithArgs.unit.test.ts b/src/test/unittest/configuration/providers/fileLaunchWithArgs.unit.test.ts index c829a490..30e0b590 100644 --- a/src/test/unittest/configuration/providers/fileLaunchWithArgs.unit.test.ts +++ b/src/test/unittest/configuration/providers/fileLaunchWithArgs.unit.test.ts @@ -28,7 +28,7 @@ suite('Debugging - Configuration Provider File with Arguments', () => { request: 'launch', program: '${file}', console: 'integratedTerminal', - args: ['${command:pickArgs}'], + args: '${command:pickArgs}', }; expect(state.config).to.be.deep.equal(config); From 4fe41f9149ddcfab1b45a0a918a235c55890d533 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:19:33 -0800 Subject: [PATCH 5/5] fix bug with env var conflict --- bundled/scripts/noConfigScripts/debugpy | 1 + src/extension/noConfigDebugInit.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bundled/scripts/noConfigScripts/debugpy b/bundled/scripts/noConfigScripts/debugpy index 1893a2cd..def62ec5 100755 --- a/bundled/scripts/noConfigScripts/debugpy +++ b/bundled/scripts/noConfigScripts/debugpy @@ -1,3 +1,4 @@ #! /bin/bash # Bash script +export DEBUGPY_ADAPTER_ENDPOINTS=$VSCODE_DEBUGPY_ADAPTER_ENDPOINTS python3 $BUNDLED_DEBUGPY_PATH --listen 0 --wait-for-client $@ diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index 55b185d5..0854958f 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -68,7 +68,7 @@ export async function registerNoConfigDebug( collection.replace('PYDEVD_DISABLE_FILE_VALIDATION', '1'); // Add env vars for DEBUGPY_ADAPTER_ENDPOINTS, BUNDLED_DEBUGPY_PATH, and PATH - collection.replace('DEBUGPY_ADAPTER_ENDPOINTS', tempFilePath); + collection.replace('VSCODE_DEBUGPY_ADAPTER_ENDPOINTS', tempFilePath); const noConfigScriptsDir = path.join(extPath, 'bundled', 'scripts', 'noConfigScripts'); const pathSeparator = process.platform === 'win32' ? ';' : ':';