Skip to content

Commit d6b0d76

Browse files
committed
fix no-config debugger env var insertion to be less noisy
1 parent 7fedf5b commit d6b0d76

File tree

2 files changed

+62
-19
lines changed

2 files changed

+62
-19
lines changed

src/extension/noConfigDebugInit.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import * as fs from 'fs';
55
import * as path from 'path';
66
import * as crypto from 'crypto';
77
import * as os from 'os';
8-
import { DebugSessionOptions, Disposable, GlobalEnvironmentVariableCollection, l10n, RelativePattern } from 'vscode';
8+
import {
9+
DebugSessionOptions,
10+
Disposable,
11+
GlobalEnvironmentVariableCollection,
12+
l10n,
13+
RelativePattern,
14+
workspace,
15+
} from 'vscode';
916
import { createFileSystemWatcher, debugStartDebugging } from './utils';
1017
import { traceError, traceLog, traceVerbose } from './common/log/logging';
1118

@@ -30,21 +37,30 @@ export async function registerNoConfigDebug(
3037
const collection = envVarCollection;
3138

3239
// create a temp directory for the noConfigDebugAdapterEndpoints
33-
// file path format: tempDir/noConfigDebugAdapterEndpoints-<randomString>/debuggerAdapterEndpoint.txt
34-
const randomSuffix = crypto.randomBytes(10).toString('hex');
35-
const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`;
36-
let tempDirPath = path.join(os.tmpdir(), tempDirName);
37-
try {
38-
traceLog('Attempting to use temp directory for noConfigDebugAdapterEndpoints, dir name:', tempDirName);
39-
await fs.promises.mkdir(tempDirPath, { recursive: true });
40-
} catch (error) {
41-
// Handle the error when accessing the temp directory
42-
traceError('Error accessing temp directory:', error, ' Attempt to use extension root dir instead');
43-
// Make new temp directory in extension root dird
44-
tempDirPath = path.join(extPath, '.temp');
45-
await fs.promises.mkdir(tempDirPath, { recursive: true });
40+
// file path format: extPath/.noConfigDebugAdapterEndpoints/endpoint-stableWorkspaceHash.txt
41+
const workspaceUri = workspace.workspaceFolders?.[0]?.uri;
42+
if (!workspaceUri) {
43+
traceError('No workspace folder found');
44+
return Promise.resolve(new Disposable(() => {}));
45+
}
46+
47+
// create a stable hash for the workspace folder, reduce terminal variable churn
48+
const hash = crypto.createHash('sha256');
49+
hash.update(workspaceUri.toString());
50+
const stableWorkspaceHash = hash.digest('hex');
51+
52+
const tempDirPath = path.join(extPath, '.noConfigDebugAdapterEndpoints');
53+
const tempFilePath = path.join(tempDirPath, `endpoint-${stableWorkspaceHash}.txt`);
54+
55+
// create the temp directory if it doesn't exist
56+
if (!fs.existsSync(tempDirPath)) {
57+
fs.mkdirSync(tempDirPath, { recursive: true });
58+
} else {
59+
// remove endpoint file in the temp directory if it exists
60+
if (fs.existsSync(tempFilePath)) {
61+
fs.unlinkSync(tempFilePath);
62+
}
4663
}
47-
const tempFilePath = path.join(tempDirPath, 'debuggerAdapterEndpoint.txt');
4864

4965
// Add env var for PYDEVD_DISABLE_FILE_VALIDATION to disable extra output in terminal when starting the debug session.
5066
collection.replace('PYDEVD_DISABLE_FILE_VALIDATION', '1');

src/test/unittest/noConfigDebugInit.unit.test.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IExtensionContext } from '../../extension/common/types';
66
import { registerNoConfigDebug as registerNoConfigDebug } from '../../extension/noConfigDebugInit';
77
import * as TypeMoq from 'typemoq';
88
import * as sinon from 'sinon';
9-
import { DebugConfiguration, DebugSessionOptions, RelativePattern, Uri } from 'vscode';
9+
import { DebugConfiguration, DebugSessionOptions, RelativePattern, Uri, workspace } from 'vscode';
1010
import * as utils from '../../extension/utils';
1111
import { assert } from 'console';
1212
import * as fs from 'fs';
@@ -22,6 +22,7 @@ suite('setup for no-config debug scenario', function () {
2222
let DEBUGPY_ADAPTER_ENDPOINTS = 'DEBUGPY_ADAPTER_ENDPOINTS';
2323
let BUNDLED_DEBUGPY_PATH = 'BUNDLED_DEBUGPY_PATH';
2424
let tempDirPath: string;
25+
let workspaceUriStub: sinon.SinonStub;
2526

2627
const testDataDir = path.join(__dirname, 'testData');
2728
const testFilePath = path.join(testDataDir, 'debuggerAdapterEndpoint.txt');
@@ -32,7 +33,7 @@ suite('setup for no-config debug scenario', function () {
3233
const randomSuffix = '1234567899';
3334
const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`;
3435
tempDirPath = path.join(os.tmpdir(), tempDirName);
35-
context.setup((c) => (c as any).extensionPath).returns(() => 'fake/extension/path');
36+
context.setup((c) => (c as any).extensionPath).returns(() => os.tmpdir());
3637
context.setup((c) => c.subscriptions).returns(() => []);
3738
noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts');
3839
bundledDebugPath = path.join(context.object.extensionPath, 'bundled/libs/debugpy');
@@ -41,12 +42,15 @@ suite('setup for no-config debug scenario', function () {
4142
let randomBytesStub = sinon.stub(crypto, 'randomBytes');
4243
// Provide a valid Buffer object
4344
randomBytesStub.callsFake((_size: number) => Buffer.from('1234567899', 'hex'));
45+
46+
workspaceUriStub = sinon.stub(workspace, 'workspaceFolders').value([{ uri: Uri.parse(os.tmpdir()) }]);
4447
} catch (error) {
4548
console.error('Error in setup:', error);
4649
}
4750
});
4851
teardown(() => {
4952
sinon.restore();
53+
workspaceUriStub.restore();
5054
});
5155

5256
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 () {
5963
.setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
6064
.callback((key, value) => {
6165
if (key === DEBUGPY_ADAPTER_ENDPOINTS) {
62-
assert(value.includes('noConfigDebugAdapterEndpoints-1234567899'));
66+
assert(value.includes('endpoint-'));
6367
} else if (key === BUNDLED_DEBUGPY_PATH) {
6468
assert(value === bundledDebugPath);
6569
} else if (key === 'PYDEVD_DISABLE_FILE_VALIDATION') {
@@ -99,7 +103,7 @@ suite('setup for no-config debug scenario', function () {
99103

100104
// Assert
101105
sinon.assert.calledOnce(createFileSystemWatcherFunct);
102-
const expectedPattern = new RelativePattern(tempDirPath, '**/*');
106+
const expectedPattern = new RelativePattern(path.join(os.tmpdir(), '.noConfigDebugAdapterEndpoints'), '**/*');
103107
sinon.assert.calledWith(createFileSystemWatcherFunct, expectedPattern);
104108
});
105109

@@ -163,6 +167,29 @@ suite('setup for no-config debug scenario', function () {
163167

164168
sinon.assert.calledWith(debugStub, undefined, expectedConfig, optionsExpected);
165169
});
170+
171+
test('should check if tempFilePath exists when debuggerAdapterEndpointFolder exists', async () => {
172+
// Arrange
173+
const environmentVariableCollectionMock = TypeMoq.Mock.ofType<any>();
174+
context.setup((c) => c.environmentVariableCollection).returns(() => environmentVariableCollectionMock.object);
175+
176+
const fsExistsSyncStub = sinon.stub(fs, 'existsSync').returns(true);
177+
const fsUnlinkSyncStub = sinon.stub(fs, 'unlinkSync');
178+
179+
// Act
180+
await registerNoConfigDebug(context.object.environmentVariableCollection, context.object.extensionPath);
181+
182+
// Assert
183+
sinon.assert.calledWith(
184+
fsExistsSyncStub,
185+
sinon.match((value: any) => value.includes('endpoint-')),
186+
);
187+
sinon.assert.calledOnce(fsUnlinkSyncStub);
188+
189+
// Cleanup
190+
fsExistsSyncStub.restore();
191+
fsUnlinkSyncStub.restore();
192+
});
166193
});
167194

168195
function setupFileSystemWatchers(): sinon.SinonStub {

0 commit comments

Comments
 (0)