From d6b0d76957d83eb55241bc18f203329be769e609 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 10 Feb 2025 10:42:55 -0800 Subject: [PATCH 1/4] fix no-config debugger env var insertion to be less noisy --- src/extension/noConfigDebugInit.ts | 46 +++++++++++++------ .../unittest/noConfigDebugInit.unit.test.ts | 35 ++++++++++++-- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index e623e951..a995ffed 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -5,7 +5,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; import * as os from 'os'; -import { DebugSessionOptions, Disposable, GlobalEnvironmentVariableCollection, l10n, RelativePattern } from 'vscode'; +import { + DebugSessionOptions, + Disposable, + GlobalEnvironmentVariableCollection, + l10n, + RelativePattern, + workspace, +} from 'vscode'; import { createFileSystemWatcher, debugStartDebugging } from './utils'; import { traceError, traceLog, traceVerbose } from './common/log/logging'; @@ -30,21 +37,30 @@ export async function registerNoConfigDebug( const collection = envVarCollection; // create a temp directory for the noConfigDebugAdapterEndpoints - // file path format: tempDir/noConfigDebugAdapterEndpoints-/debuggerAdapterEndpoint.txt - const randomSuffix = crypto.randomBytes(10).toString('hex'); - const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`; - let tempDirPath = path.join(os.tmpdir(), tempDirName); - try { - traceLog('Attempting to use temp directory for noConfigDebugAdapterEndpoints, dir name:', tempDirName); - await fs.promises.mkdir(tempDirPath, { recursive: true }); - } catch (error) { - // Handle the error when accessing the temp directory - traceError('Error accessing temp directory:', error, ' Attempt to use extension root dir instead'); - // Make new temp directory in extension root dird - tempDirPath = path.join(extPath, '.temp'); - await fs.promises.mkdir(tempDirPath, { recursive: true }); + // file path format: extPath/.noConfigDebugAdapterEndpoints/endpoint-stableWorkspaceHash.txt + const workspaceUri = workspace.workspaceFolders?.[0]?.uri; + if (!workspaceUri) { + 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()); + const stableWorkspaceHash = hash.digest('hex'); + + const tempDirPath = path.join(extPath, '.noConfigDebugAdapterEndpoints'); + const tempFilePath = path.join(tempDirPath, `endpoint-${stableWorkspaceHash}.txt`); + + // create the temp directory if it doesn't exist + if (!fs.existsSync(tempDirPath)) { + fs.mkdirSync(tempDirPath, { recursive: true }); + } else { + // remove endpoint file in the temp directory if it exists + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } } - const tempFilePath = path.join(tempDirPath, 'debuggerAdapterEndpoint.txt'); // Add env var for PYDEVD_DISABLE_FILE_VALIDATION to disable extra output in terminal when starting the debug session. collection.replace('PYDEVD_DISABLE_FILE_VALIDATION', '1'); diff --git a/src/test/unittest/noConfigDebugInit.unit.test.ts b/src/test/unittest/noConfigDebugInit.unit.test.ts index 5be9edca..b6e8652c 100644 --- a/src/test/unittest/noConfigDebugInit.unit.test.ts +++ b/src/test/unittest/noConfigDebugInit.unit.test.ts @@ -6,7 +6,7 @@ import { IExtensionContext } from '../../extension/common/types'; import { registerNoConfigDebug as registerNoConfigDebug } from '../../extension/noConfigDebugInit'; import * as TypeMoq from 'typemoq'; import * as sinon from 'sinon'; -import { DebugConfiguration, DebugSessionOptions, RelativePattern, Uri } from 'vscode'; +import { DebugConfiguration, DebugSessionOptions, RelativePattern, Uri, workspace } from 'vscode'; import * as utils from '../../extension/utils'; import { assert } from 'console'; import * as fs from 'fs'; @@ -22,6 +22,7 @@ suite('setup for no-config debug scenario', function () { let DEBUGPY_ADAPTER_ENDPOINTS = 'DEBUGPY_ADAPTER_ENDPOINTS'; let BUNDLED_DEBUGPY_PATH = 'BUNDLED_DEBUGPY_PATH'; let tempDirPath: string; + let workspaceUriStub: sinon.SinonStub; const testDataDir = path.join(__dirname, 'testData'); const testFilePath = path.join(testDataDir, 'debuggerAdapterEndpoint.txt'); @@ -32,7 +33,7 @@ suite('setup for no-config debug scenario', function () { const randomSuffix = '1234567899'; const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`; tempDirPath = path.join(os.tmpdir(), tempDirName); - context.setup((c) => (c as any).extensionPath).returns(() => 'fake/extension/path'); + context.setup((c) => (c as any).extensionPath).returns(() => os.tmpdir()); context.setup((c) => c.subscriptions).returns(() => []); noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts'); bundledDebugPath = path.join(context.object.extensionPath, 'bundled/libs/debugpy'); @@ -41,12 +42,15 @@ suite('setup for no-config debug scenario', function () { let randomBytesStub = sinon.stub(crypto, 'randomBytes'); // Provide a valid Buffer object randomBytesStub.callsFake((_size: number) => Buffer.from('1234567899', 'hex')); + + workspaceUriStub = sinon.stub(workspace, 'workspaceFolders').value([{ uri: Uri.parse(os.tmpdir()) }]); } catch (error) { console.error('Error in setup:', error); } }); teardown(() => { sinon.restore(); + workspaceUriStub.restore(); }); test('should add environment variables for DEBUGPY_ADAPTER_ENDPOINTS, BUNDLED_DEBUGPY_PATH, and PATH', async () => { @@ -59,7 +63,7 @@ suite('setup for no-config debug scenario', function () { .setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((key, value) => { if (key === DEBUGPY_ADAPTER_ENDPOINTS) { - assert(value.includes('noConfigDebugAdapterEndpoints-1234567899')); + assert(value.includes('endpoint-')); } else if (key === BUNDLED_DEBUGPY_PATH) { assert(value === bundledDebugPath); } else if (key === 'PYDEVD_DISABLE_FILE_VALIDATION') { @@ -99,7 +103,7 @@ suite('setup for no-config debug scenario', function () { // Assert sinon.assert.calledOnce(createFileSystemWatcherFunct); - const expectedPattern = new RelativePattern(tempDirPath, '**/*'); + const expectedPattern = new RelativePattern(path.join(os.tmpdir(), '.noConfigDebugAdapterEndpoints'), '**/*'); sinon.assert.calledWith(createFileSystemWatcherFunct, expectedPattern); }); @@ -163,6 +167,29 @@ suite('setup for no-config debug scenario', function () { sinon.assert.calledWith(debugStub, undefined, expectedConfig, optionsExpected); }); + + test('should check if tempFilePath exists when debuggerAdapterEndpointFolder exists', async () => { + // Arrange + const environmentVariableCollectionMock = TypeMoq.Mock.ofType(); + context.setup((c) => c.environmentVariableCollection).returns(() => environmentVariableCollectionMock.object); + + const fsExistsSyncStub = sinon.stub(fs, 'existsSync').returns(true); + const fsUnlinkSyncStub = sinon.stub(fs, 'unlinkSync'); + + // Act + await registerNoConfigDebug(context.object.environmentVariableCollection, context.object.extensionPath); + + // Assert + sinon.assert.calledWith( + fsExistsSyncStub, + sinon.match((value: any) => value.includes('endpoint-')), + ); + sinon.assert.calledOnce(fsUnlinkSyncStub); + + // Cleanup + fsExistsSyncStub.restore(); + fsUnlinkSyncStub.restore(); + }); }); function setupFileSystemWatchers(): sinon.SinonStub { From db134dd5fdcca328ccd26f5322cb82aa27ca9c37 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 10 Feb 2025 10:44:08 -0800 Subject: [PATCH 2/4] remove unused imports --- src/extension/noConfigDebugInit.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index a995ffed..0ef7b0a3 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -4,7 +4,6 @@ import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; -import * as os from 'os'; import { DebugSessionOptions, Disposable, @@ -14,7 +13,7 @@ import { workspace, } from 'vscode'; import { createFileSystemWatcher, debugStartDebugging } from './utils'; -import { traceError, traceLog, traceVerbose } from './common/log/logging'; +import { traceError, traceVerbose } from './common/log/logging'; /** * Registers the configuration-less debugging setup for the extension. From 5d082d1a61d98ede94af8b6fa050d3c21ecb12f9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 10 Feb 2025 10:47:44 -0800 Subject: [PATCH 3/4] small fix --- src/test/unittest/noConfigDebugInit.unit.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/unittest/noConfigDebugInit.unit.test.ts b/src/test/unittest/noConfigDebugInit.unit.test.ts index b6e8652c..47f3a9fb 100644 --- a/src/test/unittest/noConfigDebugInit.unit.test.ts +++ b/src/test/unittest/noConfigDebugInit.unit.test.ts @@ -21,7 +21,6 @@ suite('setup for no-config debug scenario', function () { let bundledDebugPath: string; let DEBUGPY_ADAPTER_ENDPOINTS = 'DEBUGPY_ADAPTER_ENDPOINTS'; let BUNDLED_DEBUGPY_PATH = 'BUNDLED_DEBUGPY_PATH'; - let tempDirPath: string; let workspaceUriStub: sinon.SinonStub; const testDataDir = path.join(__dirname, 'testData'); @@ -30,9 +29,6 @@ suite('setup for no-config debug scenario', function () { try { context = TypeMoq.Mock.ofType(); - const randomSuffix = '1234567899'; - const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`; - tempDirPath = path.join(os.tmpdir(), tempDirName); context.setup((c) => (c as any).extensionPath).returns(() => os.tmpdir()); context.setup((c) => c.subscriptions).returns(() => []); noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts'); From 14cacd5a6e1e7217587cf308b2092e9f3baedde2 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 10 Feb 2025 13:06:29 -0800 Subject: [PATCH 4/4] limit to char length of 16 --- src/extension/noConfigDebugInit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index 0ef7b0a3..27e22c53 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -46,7 +46,7 @@ export async function registerNoConfigDebug( // create a stable hash for the workspace folder, reduce terminal variable churn const hash = crypto.createHash('sha256'); hash.update(workspaceUri.toString()); - const stableWorkspaceHash = hash.digest('hex'); + const stableWorkspaceHash = hash.digest('hex').slice(0, 16); const tempDirPath = path.join(extPath, '.noConfigDebugAdapterEndpoints'); const tempFilePath = path.join(tempDirPath, `endpoint-${stableWorkspaceHash}.txt`);