diff --git a/CHANGELOG.md b/CHANGELOG.md index 75d2af3d..883c0306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### 2.4.0 | 2024-05-24 - Allow installing unreleased builds using an `-unreleased` suffix, such as `insiders-unreleased`. +- Allow passing different data directories in `runVSCodeCommand`, using it for extension development. ### 2.3.10 | 2024-05-13 diff --git a/lib/runTest.ts b/lib/runTest.ts index bf1841eb..3e622852 100644 --- a/lib/runTest.ts +++ b/lib/runTest.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as cp from 'child_process'; -import * as path from 'path'; -import { DownloadOptions, defaultCachePath, downloadAndUnzipVSCode } from './download'; -import { killTree } from './util'; +import { DownloadOptions, downloadAndUnzipVSCode } from './download'; +import { getProfileArguments, killTree } from './util'; export interface TestOptions extends Partial { /** @@ -108,25 +107,6 @@ export async function runTests(options: TestOptions): Promise { return innerRunTests(options.vscodeExecutablePath, args, options.extensionTestsEnv); } - -/** Adds the extensions and user data dir to the arguments for the VS Code CLI */ -export function getProfileArguments(args: readonly string[]) { - const out: string[] = []; - if (!hasArg('extensions-dir', args)) { - out.push(`--extensions-dir=${path.join(defaultCachePath, 'extensions')}`); - } - - if (!hasArg('user-data-dir', args)) { - out.push(`--user-data-dir=${path.join(defaultCachePath, 'user-data')}`); - } - - return out; -} - -function hasArg(argName: string, argList: readonly string[]) { - return argList.some((a) => a === `--${argName}` || a.startsWith(`--${argName}=`)); -} - const SIGINT = 'SIGINT'; async function innerRunTests( diff --git a/lib/util.ts b/lib/util.ts index 0e3c2336..c0902be5 100644 --- a/lib/util.ts +++ b/lib/util.ts @@ -11,9 +11,9 @@ import * as https from 'https'; import * as createHttpsProxyAgent from 'https-proxy-agent'; import * as path from 'path'; import { URL } from 'url'; -import { DownloadOptions, DownloadPlatform, downloadAndUnzipVSCode } from './download'; +import { DownloadOptions, DownloadPlatform, defaultCachePath, downloadAndUnzipVSCode } from './download'; import * as request from './request'; -import { TestOptions, getProfileArguments } from './runTest'; +import { TestOptions } from './runTest'; export let systemDefaultPlatform: DownloadPlatform; @@ -219,7 +219,39 @@ export function resolveCliArgsFromVSCodeExecutablePath( return args; } -export type RunVSCodeCommandOptions = Partial & { spawn?: SpawnOptions }; +export interface RunVSCodeCommandOptions extends Partial { + /** + * Additional options to pass to `child_process.spawn` + */ + spawn?: SpawnOptions; + + /** + * Whether VS Code should be launched using default settings and extensions + * installed on this machine. If `false`, then separate directories will be + * used inside the `.vscode-test` folder within the project. + * + * Defaults to `false`. + */ + reuseMachineInstall?: boolean; +} + +/** Adds the extensions and user data dir to the arguments for the VS Code CLI */ +export function getProfileArguments(args: readonly string[]) { + const out: string[] = []; + if (!hasArg('extensions-dir', args)) { + out.push(`--extensions-dir=${path.join(defaultCachePath, 'extensions')}`); + } + + if (!hasArg('user-data-dir', args)) { + out.push(`--user-data-dir=${path.join(defaultCachePath, 'user-data')}`); + } + + return out; +} + +export function hasArg(argName: string, argList: readonly string[]) { + return argList.some((a) => a === `--${argName}` || a.startsWith(`--${argName}=`)); +} export class VSCodeCommandError extends Error { constructor( @@ -233,20 +265,30 @@ export class VSCodeCommandError extends Error { } /** - * Runs a VS Code command, and returns its output + * Runs a VS Code command, and returns its output. + * * @throws a {@link VSCodeCommandError} if the command fails */ -export async function runVSCodeCommand(args: string[], options: RunVSCodeCommandOptions = {}) { - const vscodeExecutablePath = await downloadAndUnzipVSCode(options); - const [cli, ...baseArgs] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); +export async function runVSCodeCommand(_args: readonly string[], options: RunVSCodeCommandOptions = {}) { + const args = _args.slice(); - const shell = process.platform === 'win32'; + let executable = await downloadAndUnzipVSCode(options); + let shell = false; + if (!options.reuseMachineInstall) { + args.push(...getProfileArguments(args)); + } + + // Unless the user is manually running tests or extension development, then resolve to the CLI script + if (!hasArg('extensionTestsPath', args) && !hasArg('extensionDevelopmentPath', args)) { + executable = resolveCliPathFromVSCodeExecutablePath(executable, options?.platform ?? systemDefaultPlatform); + shell = process.platform === 'win32'; // CVE-2024-27980 + } return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { let stdout = ''; let stderr = ''; - const child = spawn(shell ? `"${cli}"` : cli, [...baseArgs, ...args], { + const child = spawn(shell ? `"${executable}"` : executable, args, { stdio: 'pipe', shell, windowsHide: true,