From 39a57700bcc11784df5b5bb484877223ce13c249 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 18 Feb 2020 12:24:12 -0600 Subject: [PATCH] Enable access to vscode cli --- scripts/vscode.patch | 43 +++++++++++++++++++++++++++++++------------ src/node/app/app.ts | 2 +- src/node/cli.ts | 13 +++++++++++++ src/node/entry.ts | 30 ++++++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/scripts/vscode.patch b/scripts/vscode.patch index 77fe4367..8590fae5 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -12,7 +12,7 @@ index 160c42ed74..0d544c495c 100644 coverage/ diff --git a/coder.js b/coder.js new file mode 100644 -index 0000000000..fc18355f89 +index 0000000000..6aee0e46bc --- /dev/null +++ b/coder.js @@ -0,0 +1,70 @@ @@ -32,9 +32,9 @@ index 0000000000..fc18355f89 + buildfile.workbenchWeb, + buildfile.workerExtensionHost, + buildfile.keyboardMaps, -+ buildfile.entrypoint('vs/platform/files/node/watcher/unix/watcherApp', ["vs/css", "vs/nls"]), -+ buildfile.entrypoint('vs/platform/files/node/watcher/nsfw/watcherApp', ["vs/css", "vs/nls"]), -+ buildfile.entrypoint('vs/workbench/services/extensions/node/extensionHostProcess', ["vs/css", "vs/nls"]), ++ buildfile.entrypoint("vs/platform/files/node/watcher/unix/watcherApp", ["vs/css", "vs/nls"]), ++ buildfile.entrypoint("vs/platform/files/node/watcher/nsfw/watcherApp", ["vs/css", "vs/nls"]), ++ buildfile.entrypoint("vs/workbench/services/extensions/node/extensionHostProcess", ["vs/css", "vs/nls"]), +]); + +const vscodeResources = [ @@ -894,10 +894,10 @@ index 0000000000..eb62b87798 +} diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts new file mode 100644 -index 0000000000..9995e9f7fc +index 0000000000..0d7feaa24e --- /dev/null +++ b/src/vs/server/entry.ts -@@ -0,0 +1,67 @@ +@@ -0,0 +1,76 @@ +import { field } from '@coder/logger'; +import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc'; @@ -953,6 +953,15 @@ index 0000000000..9995e9f7fc + exit(1); + } + break; ++ case 'cli': ++ try { ++ await vscode.cli(message.args); ++ exit(0); ++ } catch (error) { ++ logger.error(error.message); ++ exit(1); ++ } ++ break; + case 'socket': + vscode.handleWebSocket(socket, message.query); + break; @@ -976,10 +985,10 @@ index 0000000000..56331ff1fc +require('../../bootstrap-amd').load('vs/server/entry'); diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts new file mode 100644 -index 0000000000..a1047fff86 +index 0000000000..82566066ff --- /dev/null +++ b/src/vs/server/ipc.d.ts -@@ -0,0 +1,101 @@ +@@ -0,0 +1,106 @@ +/** + * External interfaces for integration into code-server over IPC. No vs imports + * should be made in this file. @@ -998,7 +1007,12 @@ index 0000000000..a1047fff86 + query: Query; +} + -+export type CodeServerMessage = InitMessage | SocketMessage; ++export interface CliMessage { ++ type: 'cli'; ++ args: Args; ++} ++ ++export type CodeServerMessage = InitMessage | SocketMessage | CliMessage; + +export interface ReadyMessage { + type: 'ready'; @@ -1032,8 +1046,8 @@ index 0000000000..a1047fff86 +} + +export interface VscodeOptions { -+ readonly remoteAuthority: string; + readonly args: Args; ++ readonly remoteAuthority: string; + readonly startPath?: StartPath; +} + @@ -2152,10 +2166,10 @@ index 0000000000..3c74512192 +} diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts new file mode 100644 -index 0000000000..13d71949ce +index 0000000000..20dbca69b2 --- /dev/null +++ b/src/vs/server/node/server.ts -@@ -0,0 +1,252 @@ +@@ -0,0 +1,257 @@ +import * as net from 'net'; +import * as path from 'path'; +import { Emitter } from 'vs/base/common/event'; @@ -2165,6 +2179,7 @@ index 0000000000..13d71949ce +import { ClientConnectionEvent, IPCServer, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; +import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; ++import { main } from "vs/code/node/cliProcessMain"; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; @@ -2222,6 +2237,10 @@ index 0000000000..13d71949ce + private readonly services = new ServiceCollection(); + private servicesPromise?: Promise; + ++ public async cli(args: ParsedArgs): Promise { ++ return main(args); ++ } ++ + public async initialize(options: VscodeOptions): Promise { + const transformer = getUriTransformer(options.remoteAuthority); + if (!this.servicesPromise) { diff --git a/src/node/app/app.ts b/src/node/app/app.ts index 136c4cc6..c80fb0b7 100644 --- a/src/node/app/app.ts +++ b/src/node/app/app.ts @@ -136,7 +136,7 @@ export class MainHttpProvider extends HttpProvider { private async getUpdate(): Promise { if (!this.update.enabled) { - return "Updates are disabled" + return `
Updates are disabled
` } const humanize = (time: number): string => { diff --git a/src/node/cli.ts b/src/node/cli.ts index 34409ac4..a87b0666 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -15,6 +15,7 @@ export interface Args extends VsArgs { readonly cert?: OptionalString readonly "cert-key"?: string readonly "disable-updates"?: boolean + readonly "disable-telemetry"?: boolean readonly help?: boolean readonly host?: string readonly json?: boolean @@ -22,6 +23,9 @@ export interface Args extends VsArgs { readonly port?: number readonly socket?: string readonly version?: boolean + readonly "list-extensions"?: boolean + readonly "install-extension"?: string[] + readonly "uninstall-extension"?: string[] readonly _: string[] } @@ -68,6 +72,7 @@ const options: Options> = { }, "cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." }, "disable-updates": { type: "boolean", description: "Disable automatic updates." }, + "disable-telemetry": { type: "boolean", description: "Disable telemetry." }, host: { type: "string", description: "Host for the HTTP server." }, help: { type: "boolean", short: "h", description: "Show this output." }, json: { type: "boolean" }, @@ -82,6 +87,9 @@ const options: Options> = { "builtin-extensions-dir": { type: "string", path: true }, "extra-extensions-dir": { type: "string[]", path: true }, "extra-builtin-extensions-dir": { type: "string[]", path: true }, + "list-extensions": { type: "boolean" }, + "install-extension": { type: "string[]" }, + "uninstall-extension": { type: "string[]" }, log: { type: "string" }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, @@ -193,8 +201,13 @@ export const parse = (argv: string[]): Args => { if (process.env.LOG_LEVEL === "trace" || args.verbose) { args.verbose = true args.log = "trace" + } else if (!args.log) { + args.log = process.env.LOG_LEVEL } + // Ensure this passes down to forked processes. + process.env.LOG_LEVEL = args.log + switch (args.log) { case "trace": logger.level = Level.Trace diff --git a/src/node/entry.ts b/src/node/entry.ts index b9a03a93..bfa8bc4c 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -1,10 +1,13 @@ -import { logger } from "@coder/logger" -import { Args, optionDescriptions, parse } from "./cli" +import { field, logger } from "@coder/logger" +import * as cp from "child_process" +import * as path from "path" +import { CliMessage } from "../../lib/vscode/src/vs/server/ipc" import { ApiHttpProvider } from "./app/api" import { MainHttpProvider } from "./app/app" import { LoginHttpProvider } from "./app/login" import { UpdateHttpProvider } from "./app/update" import { VscodeHttpProvider } from "./app/vscode" +import { Args, optionDescriptions, parse } from "./cli" import { AuthType, HttpServer } from "./http" import { generateCertificate, generatePassword, hash, open } from "./util" import { ipcMain, wrap } from "./wrapper" @@ -105,6 +108,29 @@ if (args.help) { console.log(version) } process.exit(0) +} else if (args["list-extensions"] || args["install-extension"] || args["uninstall-extension"]) { + process.env.NBIN_BYPASS = "true" + logger.debug("Forking VS Code CLI...") + const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], { + env: { + ...process.env, + CODE_SERVER_PARENT_PID: process.pid.toString(), + }, + }) + vscode.once("message", (message) => { + logger.debug("Got message from VS Code", field("message", message)) + if (message.type !== "ready") { + logger.error("Unexpected response waiting for ready response") + process.exit(1) + } + const send: CliMessage = { type: "cli", args } + vscode.send(send) + }) + vscode.once("error", (error) => { + logger.error(error.message) + process.exit(1) + }) + vscode.on("exit", (code) => process.exit(code || 0)) } else { wrap(() => main(args)) }