Skip to content

Commit 13bf88f

Browse files
authored
fix no-config debugger env var insertion to be less noisy (#597)
* fix no-config debugger env var insertion to be less noisy * remove unused imports * small fix * limit to char length of 16
1 parent 7fedf5b commit 13bf88f

File tree

2 files changed

+63
-25
lines changed

2 files changed

+63
-25
lines changed

src/extension/noConfigDebugInit.ts

+32-17
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
import * as fs from 'fs';
55
import * as path from 'path';
66
import * as crypto from 'crypto';
7-
import * as os from 'os';
8-
import { DebugSessionOptions, Disposable, GlobalEnvironmentVariableCollection, l10n, RelativePattern } from 'vscode';
7+
import {
8+
DebugSessionOptions,
9+
Disposable,
10+
GlobalEnvironmentVariableCollection,
11+
l10n,
12+
RelativePattern,
13+
workspace,
14+
} from 'vscode';
915
import { createFileSystemWatcher, debugStartDebugging } from './utils';
10-
import { traceError, traceLog, traceVerbose } from './common/log/logging';
16+
import { traceError, traceVerbose } from './common/log/logging';
1117

1218
/**
1319
* Registers the configuration-less debugging setup for the extension.
@@ -30,21 +36,30 @@ export async function registerNoConfigDebug(
3036
const collection = envVarCollection;
3137

3238
// 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 });
39+
// file path format: extPath/.noConfigDebugAdapterEndpoints/endpoint-stableWorkspaceHash.txt
40+
const workspaceUri = workspace.workspaceFolders?.[0]?.uri;
41+
if (!workspaceUri) {
42+
traceError('No workspace folder found');
43+
return Promise.resolve(new Disposable(() => {}));
44+
}
45+
46+
// create a stable hash for the workspace folder, reduce terminal variable churn
47+
const hash = crypto.createHash('sha256');
48+
hash.update(workspaceUri.toString());
49+
const stableWorkspaceHash = hash.digest('hex').slice(0, 16);
50+
51+
const tempDirPath = path.join(extPath, '.noConfigDebugAdapterEndpoints');
52+
const tempFilePath = path.join(tempDirPath, `endpoint-${stableWorkspaceHash}.txt`);
53+
54+
// create the temp directory if it doesn't exist
55+
if (!fs.existsSync(tempDirPath)) {
56+
fs.mkdirSync(tempDirPath, { recursive: true });
57+
} else {
58+
// remove endpoint file in the temp directory if it exists
59+
if (fs.existsSync(tempFilePath)) {
60+
fs.unlinkSync(tempFilePath);
61+
}
4662
}
47-
const tempFilePath = path.join(tempDirPath, 'debuggerAdapterEndpoint.txt');
4863

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

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

+31-8
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';
@@ -21,18 +21,15 @@ suite('setup for no-config debug scenario', function () {
2121
let bundledDebugPath: string;
2222
let DEBUGPY_ADAPTER_ENDPOINTS = 'DEBUGPY_ADAPTER_ENDPOINTS';
2323
let BUNDLED_DEBUGPY_PATH = 'BUNDLED_DEBUGPY_PATH';
24-
let tempDirPath: string;
24+
let workspaceUriStub: sinon.SinonStub;
2525

2626
const testDataDir = path.join(__dirname, 'testData');
2727
const testFilePath = path.join(testDataDir, 'debuggerAdapterEndpoint.txt');
2828
setup(() => {
2929
try {
3030
context = TypeMoq.Mock.ofType<IExtensionContext>();
3131

32-
const randomSuffix = '1234567899';
33-
const tempDirName = `noConfigDebugAdapterEndpoints-${randomSuffix}`;
34-
tempDirPath = path.join(os.tmpdir(), tempDirName);
35-
context.setup((c) => (c as any).extensionPath).returns(() => 'fake/extension/path');
32+
context.setup((c) => (c as any).extensionPath).returns(() => os.tmpdir());
3633
context.setup((c) => c.subscriptions).returns(() => []);
3734
noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts');
3835
bundledDebugPath = path.join(context.object.extensionPath, 'bundled/libs/debugpy');
@@ -41,12 +38,15 @@ suite('setup for no-config debug scenario', function () {
4138
let randomBytesStub = sinon.stub(crypto, 'randomBytes');
4239
// Provide a valid Buffer object
4340
randomBytesStub.callsFake((_size: number) => Buffer.from('1234567899', 'hex'));
41+
42+
workspaceUriStub = sinon.stub(workspace, 'workspaceFolders').value([{ uri: Uri.parse(os.tmpdir()) }]);
4443
} catch (error) {
4544
console.error('Error in setup:', error);
4645
}
4746
});
4847
teardown(() => {
4948
sinon.restore();
49+
workspaceUriStub.restore();
5050
});
5151

5252
test('should add environment variables for DEBUGPY_ADAPTER_ENDPOINTS, BUNDLED_DEBUGPY_PATH, and PATH', async () => {
@@ -59,7 +59,7 @@ suite('setup for no-config debug scenario', function () {
5959
.setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
6060
.callback((key, value) => {
6161
if (key === DEBUGPY_ADAPTER_ENDPOINTS) {
62-
assert(value.includes('noConfigDebugAdapterEndpoints-1234567899'));
62+
assert(value.includes('endpoint-'));
6363
} else if (key === BUNDLED_DEBUGPY_PATH) {
6464
assert(value === bundledDebugPath);
6565
} else if (key === 'PYDEVD_DISABLE_FILE_VALIDATION') {
@@ -99,7 +99,7 @@ suite('setup for no-config debug scenario', function () {
9999

100100
// Assert
101101
sinon.assert.calledOnce(createFileSystemWatcherFunct);
102-
const expectedPattern = new RelativePattern(tempDirPath, '**/*');
102+
const expectedPattern = new RelativePattern(path.join(os.tmpdir(), '.noConfigDebugAdapterEndpoints'), '**/*');
103103
sinon.assert.calledWith(createFileSystemWatcherFunct, expectedPattern);
104104
});
105105

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

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

168191
function setupFileSystemWatchers(): sinon.SinonStub {

0 commit comments

Comments
 (0)