From 630ccfcacca21947f0b29eca03626aaf0f4dae67 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Apr 2019 16:30:50 -0500 Subject: [PATCH] Add flag to install an extension from the command line (#504) --- packages/server/src/cli.ts | 35 +++++++++++++++---- packages/server/src/vscode/bootstrapFork.ts | 16 ++++++--- .../vscode/src/fill/environmentService.ts | 4 ++- packages/vscode/webpack.bootstrap.config.js | 2 +- scripts/vscode.patch | 8 +++++ 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts index d726c61c..6544a1e6 100644 --- a/packages/server/src/cli.ts +++ b/packages/server/src/cli.ts @@ -1,6 +1,6 @@ import { field, logger } from "@coder/logger"; import { ServerMessage, SharedProcessActive } from "@coder/protocol/src/proto"; -import { ChildProcess, fork, ForkOptions, spawn } from "child_process"; +import { ChildProcess, fork, ForkOptions } from "child_process"; import { randomFillSync } from "crypto"; import * as fs from "fs"; import * as fse from "fs-extra"; @@ -29,6 +29,7 @@ commander.version(process.env.VERSION || "development") .option("-N, --no-auth", "Start without requiring authentication.", undefined) .option("-H, --allow-http", "Allow http connections.", false) .option("-P, --password ", "Specify a password for authentication.") + .option("--install-extension ", "Install an extension by its ID.") .option("--bootstrap-fork ", "Used for development. Never set.") .option("--extra-args ", "Used for development. Never set.") .arguments("Specify working directory.") @@ -61,6 +62,8 @@ const bold = (text: string | number): string | number => { readonly cert?: string; readonly certKey?: string; + readonly installExtension?: string; + readonly bootstrapFork?: string; readonly extraArgs?: string; }; @@ -112,11 +115,11 @@ const bold = (text: string | number): string | number => { process.exit(1); } - ((options.extraArgs ? JSON.parse(options.extraArgs) : []) as string[]).forEach((arg, i) => { - // [0] contains the binary running the script (`node` for example) and - // [1] contains the script name, so the arguments come after that. - process.argv[i + 2] = arg; - }); + process.argv = [ + process.argv[0], + process.argv[1], + ...(options.extraArgs ? JSON.parse(options.extraArgs) : []), + ]; return requireModule(modulePath, builtInExtensionsDir); } @@ -162,6 +165,26 @@ const bold = (text: string | number): string | number => { logger.warn('"--data-dir" is deprecated. Use "--user-data-dir" instead.'); } + if (options.installExtension) { + const fork = forkModule("vs/code/node/cli", [ + "--user-data-dir", dataDir, + "--builtin-extensions-dir", builtInExtensionsDir, + "--extensions-dir", extensionsDir, + "--install-extension", options.installExtension, + ], { + env: { + VSCODE_ALLOW_IO: "true", + VSCODE_LOGS: process.env.VSCODE_LOGS, + }, + }, dataDir); + + fork.stdout.on("data", (d: Buffer) => d.toString().split("\n").forEach((l) => logger.info(l))); + fork.stderr.on("data", (d: Buffer) => d.toString().split("\n").forEach((l) => logger.error(l))); + fork.on("exit", () => process.exit()); + + return; + } + // TODO: fill in appropriate doc url logger.info("Additional documentation: http://github.com/codercom/code-server"); logger.info("Initializing", field("data-dir", dataDir), field("extensions-dir", extensionsDir), field("working-dir", workingDir), field("log-dir", logDir)); diff --git a/packages/server/src/vscode/bootstrapFork.ts b/packages/server/src/vscode/bootstrapFork.ts index a399e690..1e66b64f 100644 --- a/packages/server/src/vscode/bootstrapFork.ts +++ b/packages/server/src/vscode/bootstrapFork.ts @@ -82,7 +82,6 @@ export const requireModule = (modulePath: string, builtInExtensionsDir: string): * @param modulePath Path of the VS Code module to load. */ export const forkModule = (modulePath: string, args?: string[], options?: cp.ForkOptions, dataDir?: string): cp.ChildProcess => { - let proc: cp.ChildProcess; const forkOptions: cp.ForkOptions = { stdio: [null, null, null, "ipc"], }; @@ -91,18 +90,27 @@ export const forkModule = (modulePath: string, args?: string[], options?: cp.For delete options.env.ELECTRON_RUN_AS_NODE; forkOptions.env = options.env; } + const forkArgs = ["--bootstrap-fork", modulePath]; if (args) { forkArgs.push("--extra-args", JSON.stringify(args)); } if (dataDir) { - forkArgs.push("--data-dir", dataDir); + forkArgs.push("--user-data-dir", dataDir); } + + const nodeArgs = []; if (isCli) { - proc = cp.spawn(process.execPath, [path.join(buildDir, "out", "cli.js"), ...forkArgs], forkOptions); + nodeArgs.push(path.join(buildDir, "out", "cli.js")); } else { - proc = cp.spawn(process.execPath, ["--require", "ts-node/register", "--require", "tsconfig-paths/register", process.argv[1], ...forkArgs], forkOptions); + nodeArgs.push( + "--require", "ts-node/register", + "--require", "tsconfig-paths/register", + process.argv[1], + ); } + + const proc = cp.spawn(process.execPath, [...nodeArgs, ...forkArgs], forkOptions); if (args && args[0] === "--type=watcherService" && os.platform() === "linux") { cp.exec(`renice -n 19 -p ${proc.pid}`, (error) => { if (error) { diff --git a/packages/vscode/src/fill/environmentService.ts b/packages/vscode/src/fill/environmentService.ts index 9f86d8a7..67cf95d2 100644 --- a/packages/vscode/src/fill/environmentService.ts +++ b/packages/vscode/src/fill/environmentService.ts @@ -1,7 +1,9 @@ -import * as path from "path"; import * as paths from "./paths"; import * as environment from "vs/platform/environment/node/environmentService"; +/** + * Customize paths using data received from the initialization message. + */ export class EnvironmentService extends environment.EnvironmentService { public get sharedIPCHandle(): string { return paths.getSocketPath() || super.sharedIPCHandle; diff --git a/packages/vscode/webpack.bootstrap.config.js b/packages/vscode/webpack.bootstrap.config.js index 01acbbdf..91a5ade6 100644 --- a/packages/vscode/webpack.bootstrap.config.js +++ b/packages/vscode/webpack.bootstrap.config.js @@ -60,7 +60,7 @@ module.exports = merge( "native-keymap": path.join(vsFills, "native-keymap.ts"), "native-watchdog": path.join(vsFills, "native-watchdog.ts"), "vs/base/common/amd": path.resolve(vsFills, "amd.ts"), - "vs/base/node/paths": path.resolve(vsFills, "paths.ts"), + "vs/base/node/paths": path.join(vsFills, "paths.ts"), "vs/platform/product/node/package": path.resolve(vsFills, "package.ts"), "vs/platform/product/node/product": path.resolve(vsFills, "product.ts"), "vs/base/node/zip": path.resolve(vsFills, "zip.ts"), diff --git a/scripts/vscode.patch b/scripts/vscode.patch index 9f8983f0..f6d78c1a 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -158,6 +158,14 @@ index 6fd8249..04c0933 100644 + backupMainService.initialize().catch(console.error); @@ -223,0 +236 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise { ++ const cli = await new Promise((c, e) => require(['vs/code/node/cliProcessMain'], c, e)); ++ await cli.main(args); ++ return; // Always just do this for now. diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index f97a692..0206957 100644 --- a/src/vs/editor/browser/config/configuration.ts