Skip to content

Commit

Permalink
fix: improve flexibility of runVSCodeCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed May 24, 2024
1 parent 021f593 commit 50afe68
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
24 changes: 2 additions & 22 deletions lib/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DownloadOptions> {
/**
Expand Down Expand Up @@ -108,25 +107,6 @@ export async function runTests(options: TestOptions): Promise<number> {

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(
Expand Down
60 changes: 51 additions & 9 deletions lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -219,7 +219,39 @@ export function resolveCliArgsFromVSCodeExecutablePath(
return args;
}

export type RunVSCodeCommandOptions = Partial<DownloadOptions> & { spawn?: SpawnOptions };
export interface RunVSCodeCommandOptions extends Partial<DownloadOptions> {
/**
* 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(
Expand All @@ -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,
Expand Down

0 comments on commit 50afe68

Please sign in to comment.