code-server/src/cli.ts

182 lines
6.3 KiB
TypeScript
Raw Normal View History

2019-07-02 21:55:54 +00:00
import * as os from "os";
2019-07-11 22:12:52 +00:00
2019-07-02 21:55:54 +00:00
import { validatePaths } from "vs/code/node/paths";
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import { ParsedArgs } from "vs/platform/environment/common/environment";
2019-07-02 23:29:48 +00:00
import { buildHelpMessage, buildVersionMessage, options } from "vs/platform/environment/node/argv";
2019-07-02 21:55:54 +00:00
import pkg from "vs/platform/product/node/package";
2019-07-16 19:57:02 +00:00
import product from "vs/platform/product/node/product";
2019-07-11 22:12:52 +00:00
2019-07-24 00:06:40 +00:00
import { AuthType, MainServer } from "vs/server/src/server";
2019-07-11 22:12:52 +00:00
import "vs/server/src/tar";
import { buildAllowedMessage, generateCertificate, generatePassword, open, unpackExecutables } from "vs/server/src/util";
2019-07-02 21:55:54 +00:00
2019-07-02 23:29:48 +00:00
interface Args extends ParsedArgs {
auth?: AuthType;
"base-path"?: string;
cert?: string;
"cert-key"?: string;
"extra-builtin-extensions-dir"?: string;
"extra-extensions-dir"?: string;
host?: string;
open?: string;
2019-07-02 23:29:48 +00:00
port?: string;
socket?: string;
2019-07-02 23:29:48 +00:00
}
// The last item is _ which is like -- so our options need to come before it.
const last = options.pop()!;
// Remove options that won't work or don't make sense.
let i = options.length;
while (i--) {
switch (options[i].id) {
case "add":
case "diff":
case "file-uri":
case "folder-uri":
case "goto":
case "new-window":
case "reuse-window":
case "wait":
case "disable-gpu":
// TODO: pretty sure these don't work but not 100%.
case "max-memory":
case "prof-startup":
case "inspect-extensions":
case "inspect-brk-extensions":
options.splice(i, 1);
break;
}
}
options.push({ id: "base-path", type: "string", cat: "o", description: "Base path of the URL at which code-server is hosted (used for login redirects)." });
options.push({ id: "cert", type: "string", cat: "o", description: "Path to certificate. If the path is omitted, both this and --cert-key will be generated." });
options.push({ id: "cert-key", type: "string", cat: "o", description: "Path to the certificate's key if one was provided." });
options.push({ id: "extra-builtin-extensions-dir", type: "string", cat: "o", description: "Path to an extra builtin extension directory." });
options.push({ id: "extra-extensions-dir", type: "string", cat: "o", description: "Path to an extra user extension directory." });
2019-07-24 00:06:40 +00:00
options.push({ id: "host", type: "string", cat: "o", description: "Host for the server." });
options.push({ id: "auth", type: "string", cat: "o", description: `The type of authentication to use. ${buildAllowedMessage(AuthType)}.` });
options.push({ id: "open", type: "boolean", cat: "o", description: "Open in the browser on startup." });
2019-07-02 23:29:48 +00:00
options.push({ id: "port", type: "string", cat: "o", description: "Port for the main server." });
options.push({ id: "socket", type: "string", cat: "o", description: "Listen on a socket instead of host:port." });
options.push(last);
2019-07-02 23:29:48 +00:00
2019-07-02 21:55:54 +00:00
interface IMainCli {
main: (argv: ParsedArgs) => Promise<void>;
}
const main = async (): Promise<void> => {
2019-07-02 23:29:48 +00:00
const args = validatePaths(parseMainProcessArgv(process.argv)) as Args;
["extra-extensions-dir", "extra-builtin-extensions-dir"].forEach((key) => {
if (typeof args[key] === "string") {
args[key] = [args[key]];
}
});
2019-07-02 21:55:54 +00:00
if (!product.extensionsGallery) {
product.extensionsGallery = {
serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com",
itemUrl: process.env.ITEM_URL || "",
controlUrl: "",
recommendationsUrl: "",
};
}
const version = `${(pkg as any).codeServerVersion || "development"}-vsc${pkg.version}`;
2019-07-02 21:55:54 +00:00
if (args.help) {
const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`;
2019-07-19 22:43:54 +00:00
return console.log(buildHelpMessage(product.nameLong, executable, version, undefined, false));
2019-07-02 21:55:54 +00:00
}
if (args.version) {
return console.log(buildVersionMessage(version, product.commit));
2019-07-02 21:55:54 +00:00
}
const shouldSpawnCliProcess = (): boolean => {
return !!args["install-source"]
|| !!args["list-extensions"]
|| !!args["install-extension"]
|| !!args["uninstall-extension"]
|| !!args["locate-extension"]
|| !!args["telemetry"];
};
if (shouldSpawnCliProcess()) {
const cli = await new Promise<IMainCli>((c, e) => require(["vs/code/node/cliProcessMain"], c, e));
await cli.main(args);
2019-07-19 22:43:54 +00:00
return process.exit(0); // There is a WriteStream instance keeping it open.
2019-07-02 21:55:54 +00:00
}
2019-07-19 22:43:54 +00:00
const extra = args["_"] || [];
2019-07-11 22:12:52 +00:00
const options = {
auth: args.auth,
basePath: args["base-path"],
2019-07-12 20:21:00 +00:00
cert: args.cert,
certKey: args["cert-key"],
2019-07-19 22:43:54 +00:00
folderUri: extra.length > 1 ? extra[extra.length - 1] : undefined,
host: args.host,
2019-07-12 20:21:00 +00:00
password: process.env.PASSWORD,
2019-07-11 22:12:52 +00:00
};
if (options.auth && Object.keys(AuthType).filter((k) => AuthType[k] === options.auth).length === 0) {
throw new Error(`'${options.auth}' is not a valid authentication type.`);
} else if (options.auth && !options.password) {
options.password = await generatePassword();
2019-07-12 20:21:00 +00:00
}
if (!options.certKey && typeof options.certKey !== "undefined") {
throw new Error(`--cert-key cannot be blank`);
} else if (options.certKey && !options.cert) {
throw new Error(`--cert-key was provided but --cert was not`);
} if (!options.cert && typeof options.cert !== "undefined") {
2019-07-11 22:12:52 +00:00
const { cert, certKey } = await generateCertificate();
options.cert = cert;
options.certKey = certKey;
}
const server = new MainServer({
...options,
2019-07-19 22:43:54 +00:00
port: typeof args.port !== "undefined" && parseInt(args.port, 10) || 8443,
2019-07-11 22:12:52 +00:00
socket: args.socket,
2019-07-24 00:06:40 +00:00
}, args);
2019-07-11 22:12:52 +00:00
2019-07-24 00:06:40 +00:00
const [serverAddress, /* ignore */] = await Promise.all([
2019-07-17 00:26:05 +00:00
server.listen(),
unpackExecutables(),
2019-07-05 15:54:44 +00:00
]);
2019-07-24 00:06:40 +00:00
console.log(`Server listening on ${serverAddress}`);
2019-07-12 20:21:00 +00:00
if (options.auth && !process.env.PASSWORD) {
2019-07-12 20:21:00 +00:00
console.log(" - Password is", options.password);
console.log(" - To use your own password, set the PASSWORD environment variable");
} else if (options.auth) {
console.log(" - Using custom password for authentication");
} else {
console.log(" - No authentication");
}
if (server.protocol === "https") {
2019-07-12 20:21:00 +00:00
console.log(
args.cert
? ` - Using provided certificate${args["cert-key"] ? " and key" : ""} for HTTPS`
: ` - Using generated certificate and key for HTTPS`,
2019-07-12 20:21:00 +00:00
);
} else {
console.log(" - Not serving HTTPS");
}
2019-07-15 18:31:05 +00:00
if (!server.options.socket && args.open) {
2019-07-17 00:23:58 +00:00
// The web socket doesn't seem to work if using 0.0.0.0.
2019-07-19 22:43:54 +00:00
const openAddress = `http://localhost:${server.options.port}`;
2019-07-17 00:23:58 +00:00
await open(openAddress).catch(console.error);
console.log(` - Opened ${openAddress}`);
2019-07-15 18:31:05 +00:00
}
2019-07-02 21:55:54 +00:00
};
main().catch((error) => {
console.error(error);
process.exit(1);
});