From 579bb94a6c702b331aa333d2e0f3e8ef598939dc Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 8 Sep 2020 19:39:17 -0400 Subject: [PATCH 01/29] Add coder cloud expose command --- .gitignore | 1 + package.json | 2 ++ src/node/cli.ts | 5 +++++ src/node/coder-cloud.ts | 30 ++++++++++++++++++++++++++++++ src/node/entry.ts | 15 +++++++++++++++ yarn.lock | 16 +++++++++++++++- 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/node/coder-cloud.ts diff --git a/.gitignore b/.gitignore index 616f9b01b..0b810b296 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ release-images/ node_modules node-* /plugins +/lib/coder-cloud-agent diff --git a/package.json b/package.json index 4d75331e9..bf5977c8c 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/pem": "^1.9.5", "@types/safe-compare": "^1.1.0", "@types/semver": "^7.1.0", + "@types/split2": "^2.1.6", "@types/tar-fs": "^2.0.0", "@types/tar-stream": "^2.1.0", "@types/ws": "^7.2.6", @@ -76,6 +77,7 @@ "safe-buffer": "^5.1.1", "safe-compare": "^1.1.4", "semver": "^7.1.3", + "split2": "^3.2.2", "tar": "^6.0.1", "tar-fs": "^2.0.0", "ws": "^7.2.0", diff --git a/src/node/cli.ts b/src/node/cli.ts index d3afe203d..b8272aa51 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -47,6 +47,8 @@ export interface Args extends VsArgs { readonly _: string[] readonly "reuse-window"?: boolean readonly "new-window"?: boolean + + readonly "expose"?: OptionalString } interface Option { @@ -155,6 +157,9 @@ const options: Options> = { locale: { type: "string" }, log: { type: LogLevel }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, + + "expose": { type: OptionalString, description: "Expose via Coder Cloud with the passed name. You'll get a URL" + + "like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub." }, } export const optionDescriptions = (): string[] => { diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts new file mode 100644 index 000000000..082e2d823 --- /dev/null +++ b/src/node/coder-cloud.ts @@ -0,0 +1,30 @@ +import { spawn } from "child_process" +import path from "path" +import { logger } from "@coder/logger" +import split2 from "split2" + +export async function coderCloudExpose(serverName: string): Promise { + const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") + const agent = spawn(coderCloudAgent, ["link", serverName], { + stdio: ["inherit", "inherit", "pipe"], + }) + + agent.stderr.pipe(split2()).on("data", line => { + line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "") + logger.info(line) + }) + + return new Promise((res, rej) => { + agent.on("error", rej) + + agent.on("close", code => { + if (code !== 0) { + rej({ + message: `coder cloud agent exited with ${code}`, + }) + return + } + res() + }) + }) +} diff --git a/src/node/entry.ts b/src/node/entry.ts index a416ae993..860d8de7a 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -16,6 +16,7 @@ import { AuthType, HttpServer, HttpServerOptions } from "./http" import { loadPlugins } from "./plugin" import { generateCertificate, hash, humanPath, open } from "./util" import { ipcMain, wrap } from "./wrapper" +import { coderCloudExpose } from "./coder-cloud" process.on("uncaughtException", (error) => { logger.error(`Uncaught exception: ${error.message}`) @@ -188,6 +189,20 @@ async function entry(): Promise { process.exit(1) }) vscode.on("exit", (code) => process.exit(code || 0)) + } else if (args["expose"]) { + logger.debug("exposing code-server via the coder-cloud agent") + + if (!args["expose"].value) { + logger.error("You must pass a name to expose with coder cloud. See --help") + process.exit(1) + } + + try { + await coderCloudExpose(args["expose"].value) + } catch (err) { + logger.error(err.message) + process.exit(1) + } } else if (process.env.VSCODE_IPC_HOOK_CLI) { const pipeArgs: OpenCommandPipeArgs = { type: "open", diff --git a/yarn.lock b/yarn.lock index 68221a85d..6f388626b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1107,6 +1107,13 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.3.tgz#3ad6ed949e7487e7bda6f886b4a2434a2c3d7b1a" integrity sha512-jQxClWFzv9IXdLdhSaTf16XI3NYe6zrEbckSpb5xhKfPbWgIyAY0AFyWWWfaiDcBuj3UHmMkCIwSRqpKMTZL2Q== +"@types/split2@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@types/split2/-/split2-2.1.6.tgz#b095c9e064853824b22c67993d99b066777402b1" + integrity sha512-ddaFSOMuy2Rp97l6q/LEteQygvTQJuEZ+SRhxFKR0uXGsdbFDqX/QF2xoGcOqLQ8XV91v01SnAv2vpgihNgW/Q== + dependencies: + "@types/node" "*" + "@types/tar-fs@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-2.0.0.tgz#db94cb4ea1cccecafe3d1a53812807efb4bbdbc1" @@ -5996,7 +6003,7 @@ readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6621,6 +6628,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" From c7c62daa67ef926c57c2f38271d5b9f2018e07a7 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 8 Sep 2020 19:53:14 -0400 Subject: [PATCH 02/29] Remove unused code in optionDescriptions --- src/node/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index b8272aa51..8830531fc 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -175,7 +175,7 @@ export const optionDescriptions = (): string[] => { ([k, v]) => `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k}${" ".repeat( widths.long - k.length, - )} ${v.description}${typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : ""}`, + )} ${v.description}`, ) } From 916e24e1098d21893e4df6b0ec5a1d3723d71987 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 8 Sep 2020 20:30:31 -0400 Subject: [PATCH 03/29] Add support for multiline descriptions --- src/node/cli.ts | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 8830531fc..318b85935 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -131,8 +131,8 @@ const options: Options> = { force: { type: "boolean", description: "Avoid prompts when installing VS Code extensions." }, "install-extension": { type: "string[]", - description: - "Install or update a VS Code extension by id or vsix. The identifier of an extension is `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.", + description: "Install or update a VS Code extension by id or vsix. The identifier of an extension is `${publisher}.${name}`.\n" + + "To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.", }, "enable-proposed-api": { type: "string[]", @@ -158,8 +158,14 @@ const options: Options> = { log: { type: LogLevel }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, - "expose": { type: OptionalString, description: "Expose via Coder Cloud with the passed name. You'll get a URL" + - "like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub." }, + "expose": { + type: OptionalString, + description: ` + Securely expose code-server via Coder Cloud with the passed name. You'll get a URL like + https://myname.coder-cloud.com at which you can easily access your code-server instance. + Authorization is done via GitHub. Only the first code-server spawned with the current + configuration will be accessible.` + }, } export const optionDescriptions = (): string[] => { @@ -172,10 +178,16 @@ export const optionDescriptions = (): string[] => { { short: 0, long: 0 }, ) return entries.map( - ([k, v]) => - `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k}${" ".repeat( - widths.long - k.length, - )} ${v.description}`, + ([k, v]) => { + let help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k} ` + return help + v.description?.trim().split(/\n/).map((line, i) => { + line = line.trim() + if (i == 0) { + return " ".repeat(widths.long - k.length) + line + } + return " ".repeat(widths.long + widths.short + 6) + line + }).join("\n") + }, ) } From 55a7e8b56fa9d5d89d5a7df9509d38fb421d292a Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 9 Sep 2020 00:03:01 -0400 Subject: [PATCH 04/29] Implement automatic cloud proxying --- package.json | 3 +- src/node/coder-cloud.ts | 129 +++++++++++++++++++++++++++++++++++++++- src/node/entry.ts | 4 +- yarn.lock | 5 ++ 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index bf5977c8c..5bf72f641 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,8 @@ "tar-fs": "^2.0.0", "ws": "^7.2.0", "xdg-basedir": "^4.0.0", - "yarn": "^1.22.4" + "yarn": "^1.22.4", + "delay": "^4.4.0" }, "bin": { "code-server": "out/node/entry.js" diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index 082e2d823..167b80c3f 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -2,9 +2,14 @@ import { spawn } from "child_process" import path from "path" import { logger } from "@coder/logger" import split2 from "split2" +import delay from "delay" +import fs from "fs" +import { promisify } from "util" +import xdgBasedir from "xdg-basedir" + +const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") export async function coderCloudExpose(serverName: string): Promise { - const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") const agent = spawn(coderCloudAgent, ["link", serverName], { stdio: ["inherit", "inherit", "pipe"], }) @@ -28,3 +33,125 @@ export async function coderCloudExpose(serverName: string): Promise { }) }) } + +export function coderCloudProxy(addr: string) { + // addr needs to be in host:port format. + // So we trim the protocol. + addr = addr.replace(/^https?:\/\//, "") + + if (!xdgBasedir.config) { + return + } + + const sessionTokenPath = path.join(xdgBasedir.config, "coder-cloud", "session") + + const _proxy = async () => { + await waitForPath(sessionTokenPath) + + logger.info("exposing coder-server with coder-cloud") + + const agent = spawn(coderCloudAgent, ["proxy", "--code-server-addr", addr], { + stdio: ["inherit", "inherit", "pipe"], + }) + + agent.stderr.pipe(split2()).on("data", line => { + line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "") + logger.info(line) + }) + + return new Promise((res, rej) => { + agent.on("error", rej) + + agent.on("close", code => { + if (code !== 0) { + rej({ + message: `coder cloud agent exited with ${code}`, + }) + return + } + res() + }) + }) + } + + const proxy = async () => { + try { + await _proxy() + } catch(err) { + logger.error(err.message) + } + setTimeout(proxy, 3000) + } + proxy() +} + +/** + * waitForPath efficiently implements waiting for the existence of a path. + * + * We intentionally do not use fs.watchFile as it is very slow from testing. + * I believe it polls instead of watching. + * + * The way this works is for each level of the path it will check if it exists + * and if not, it will wait for it. e.g. if the path is /home/nhooyr/.config/coder-cloud/session + * then first it will check if /home exists, then /home/nhooyr and so on. + * + * The wait works by first creating a watch promise for the p segment. + * We call fs.watch on the dirname of the p segment. When the dirname has a change, + * we check if the p segment exists and if it does, we resolve the watch promise. + * On any error or the watcher being closed, we reject the watch promise. + * + * Once that promise is setup, we check if the p segment exists with fs.exists + * and if it does, we close the watcher and return. + * + * Now we race the watch promise and a 2000ms delay promise. Once the race + * is complete, we close the watcher. + * + * If the watch promise was the one to resolve, we return. + * Otherwise we setup the watch promise again and retry. + * + * This combination of polling and watching is very reliable and efficient. + */ +async function waitForPath(p: string): Promise { + const segs = p.split(path.sep) + for (let i = 0; i < segs.length; i++) { + const s = path.join("/", ...segs.slice(0, i + 1)) + // We need to wait for each segment to exist. + await _waitForPath(s) + } +} + +async function _waitForPath(p: string): Promise { + const watchDir = path.dirname(p) + + logger.debug(`waiting for ${p}`) + + for (;;) { + const w = fs.watch(watchDir) + const watchPromise = new Promise((res, rej) => { + w.on("change", async () => { + if (await promisify(fs.exists)(p)) { + res() + } + }) + w.on("close", () => rej(new Error("watcher closed"))) + w.on("error", rej) + }) + + // We want to ignore any errors from this promise being rejected if the file + // already exists below. + watchPromise.catch(() => {}) + + if (await promisify(fs.exists)(p)) { + // The path exists! + w.close() + return + } + + // Now we wait for either the watch promise to resolve/reject or 2000ms. + const s = await Promise.race([watchPromise.then(() => "exists"), delay(2000)]) + w.close() + if (s === "exists") { + return + } + } +} diff --git a/src/node/entry.ts b/src/node/entry.ts index 860d8de7a..539b3bccd 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -12,11 +12,11 @@ import { StaticHttpProvider } from "./app/static" import { UpdateHttpProvider } from "./app/update" import { VscodeHttpProvider } from "./app/vscode" import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli" +import { coderCloudExpose, coderCloudProxy } from "./coder-cloud" import { AuthType, HttpServer, HttpServerOptions } from "./http" import { loadPlugins } from "./plugin" import { generateCertificate, hash, humanPath, open } from "./util" import { ipcMain, wrap } from "./wrapper" -import { coderCloudExpose } from "./coder-cloud" process.on("uncaughtException", (error) => { logger.error(`Uncaught exception: ${error.message}`) @@ -123,6 +123,8 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise httpServer.proxyDomains.forEach((domain) => logger.info(` - *.${domain}`)) } + coderCloudProxy(serverAddress!) + if (serverAddress && !options.socket && args.open) { // The web socket doesn't seem to work if browsing with 0.0.0.0. const openAddress = serverAddress.replace(/:\/\/0.0.0.0/, "://localhost") diff --git a/yarn.lock b/yarn.lock index 6f388626b..8e13b6f35 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2525,6 +2525,11 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +delay@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-4.4.0.tgz#71abc745f3ce043fe7f450491236541edec4ad0c" + integrity sha512-txgOrJu3OdtOfTiEOT2e76dJVfG/1dz2NZ4F0Pyt4UGZJryssMRp5vdM5wQoLwSOBNdrJv3F9PAhp/heqd7vrA== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" From 0aa98279d679c97ded60e71a94267b4d2284f0cd Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 9 Sep 2020 00:06:28 -0400 Subject: [PATCH 05/29] Fixes for CI --- src/node/cli.ts | 40 +++++++++++++++++++++++----------------- src/node/coder-cloud.ts | 16 ++++++++-------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 318b85935..c4d0d9dd4 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -48,7 +48,7 @@ export interface Args extends VsArgs { readonly "reuse-window"?: boolean readonly "new-window"?: boolean - readonly "expose"?: OptionalString + readonly expose?: OptionalString } interface Option { @@ -131,8 +131,9 @@ const options: Options> = { force: { type: "boolean", description: "Avoid prompts when installing VS Code extensions." }, "install-extension": { type: "string[]", - description: "Install or update a VS Code extension by id or vsix. The identifier of an extension is `${publisher}.${name}`.\n" + - "To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.", + description: + "Install or update a VS Code extension by id or vsix. The identifier of an extension is `${publisher}.${name}`.\n" + + "To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.", }, "enable-proposed-api": { type: "string[]", @@ -158,13 +159,13 @@ const options: Options> = { log: { type: LogLevel }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, - "expose": { + expose: { type: OptionalString, description: ` Securely expose code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub. Only the first code-server spawned with the current - configuration will be accessible.` + configuration will be accessible.`, }, } @@ -177,18 +178,23 @@ export const optionDescriptions = (): string[] => { }), { short: 0, long: 0 }, ) - return entries.map( - ([k, v]) => { - let help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k} ` - return help + v.description?.trim().split(/\n/).map((line, i) => { - line = line.trim() - if (i == 0) { - return " ".repeat(widths.long - k.length) + line - } - return " ".repeat(widths.long + widths.short + 6) + line - }).join("\n") - }, - ) + return entries.map(([k, v]) => { + const help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k} ` + return ( + help + + v.description + ?.trim() + .split(/\n/) + .map((line, i) => { + line = line.trim() + if (i === 0) { + return " ".repeat(widths.long - k.length) + line + } + return " ".repeat(widths.long + widths.short + 6) + line + }) + .join("\n") + ) + }) } export const parse = ( diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index 167b80c3f..b621b08cd 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -1,9 +1,9 @@ -import { spawn } from "child_process" -import path from "path" import { logger } from "@coder/logger" -import split2 from "split2" +import { spawn } from "child_process" import delay from "delay" import fs from "fs" +import path from "path" +import split2 from "split2" import { promisify } from "util" import xdgBasedir from "xdg-basedir" @@ -14,7 +14,7 @@ export async function coderCloudExpose(serverName: string): Promise { stdio: ["inherit", "inherit", "pipe"], }) - agent.stderr.pipe(split2()).on("data", line => { + agent.stderr.pipe(split2()).on("data", (line) => { line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "") logger.info(line) }) @@ -22,7 +22,7 @@ export async function coderCloudExpose(serverName: string): Promise { return new Promise((res, rej) => { agent.on("error", rej) - agent.on("close", code => { + agent.on("close", (code) => { if (code !== 0) { rej({ message: `coder cloud agent exited with ${code}`, @@ -54,7 +54,7 @@ export function coderCloudProxy(addr: string) { stdio: ["inherit", "inherit", "pipe"], }) - agent.stderr.pipe(split2()).on("data", line => { + agent.stderr.pipe(split2()).on("data", (line) => { line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "") logger.info(line) }) @@ -62,7 +62,7 @@ export function coderCloudProxy(addr: string) { return new Promise((res, rej) => { agent.on("error", rej) - agent.on("close", code => { + agent.on("close", (code) => { if (code !== 0) { rej({ message: `coder cloud agent exited with ${code}`, @@ -77,7 +77,7 @@ export function coderCloudProxy(addr: string) { const proxy = async () => { try { await _proxy() - } catch(err) { + } catch (err) { logger.error(err.message) } setTimeout(proxy, 3000) From eacca7d6929cc1a40f8ad7053a09db118d9733a3 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 9 Sep 2020 00:07:04 -0400 Subject: [PATCH 06/29] Unrelated fixes for CI --- ci/release-image/entrypoint.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ci/release-image/entrypoint.sh b/ci/release-image/entrypoint.sh index ee58d07a6..7842a3564 100755 --- a/ci/release-image/entrypoint.sh +++ b/ci/release-image/entrypoint.sh @@ -2,7 +2,8 @@ set -eu # This isn't set by default. -export USER="$(whoami)" +USER="$(whoami)" +export USER if [ "${DOCKER_USER-}" != "$USER" ]; then echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/nopasswd > /dev/null @@ -11,7 +12,7 @@ if [ "${DOCKER_USER-}" != "$USER" ]; then sudo usermod --login "$DOCKER_USER" coder sudo groupmod -n "$DOCKER_USER" coder - export USER="$(whoami)" + USER="$DOCKER_USER" sudo sed -i "/coder/d" /etc/sudoers.d/nopasswd sudo sed -i "s/coder/$DOCKER_USER/g" /etc/fixuid/config.yml From b22f3cb72f4e95dd0b432dc1b0f98295969c3d12 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 15 Sep 2020 10:15:51 -0400 Subject: [PATCH 07/29] Add $HOME to ./ci/dev/image/run.sh --- .gitignore | 1 + ci/build/clean.sh | 3 ++- ci/dev/image/run.sh | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0b810b296..4929c46fb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ node_modules node-* /plugins /lib/coder-cloud-agent +.home diff --git a/ci/build/clean.sh b/ci/build/clean.sh index 0e0425a4b..7a83a2845 100755 --- a/ci/build/clean.sh +++ b/ci/build/clean.sh @@ -14,7 +14,8 @@ main() { release-images \ dist \ .cache \ - node-* + node-* \ + .home pushd lib/vscode git clean -xffd diff --git a/ci/dev/image/run.sh b/ci/dev/image/run.sh index 70ab67e1d..0f17d4c27 100755 --- a/ci/dev/image/run.sh +++ b/ci/dev/image/run.sh @@ -4,11 +4,13 @@ set -euo pipefail main() { cd "$(dirname "$0")/../../.." source ./ci/lib.sh + mkdir -p .home docker run \ -it \ --rm \ -v "$PWD:/src" \ + -e HOME="/src/.home" \ -w /src \ -p 127.0.0.1:8080:8080 \ -u "$(id -u):$(id -g)" \ From 607444c695aefe0a29503ee0434024a90109b8f7 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 15 Sep 2020 10:41:47 -0400 Subject: [PATCH 08/29] Switch off debian:8 to debian:10 for the typescript build image We only want to use an old version for glibc which the centos:7 image takes care of. The old version of git used in debian:8 was causing problems with the uid/gid passthrough with no user in passwd. --- .github/workflows/ci.yaml | 10 +++++----- .github/workflows/publish.yaml | 4 ++-- ci/dev/image/run.sh | 3 ++- ci/dev/lint.sh | 5 +---- ci/images/centos7/Dockerfile | 2 +- ci/images/{debian8 => debian10}/Dockerfile | 22 ++++++---------------- doc/CONTRIBUTING.md | 4 ++-- 7 files changed, 19 insertions(+), 31 deletions(-) rename ci/images/{debian8 => debian10}/Dockerfile (57%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dcf917841..a265c98ef 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/fmt.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/fmt.sh @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/lint.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/lint.sh @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/test.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/test.sh @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/release.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/release.sh - name: Upload npm package artifact @@ -116,7 +116,7 @@ jobs: name: release-packages path: ./release-packages - name: Run ./ci/steps/build-docker-image.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/build-docker-image.sh - name: Upload release image diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index c2fe429b9..74540651f 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/publish-npm.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/publish-npm.sh env: @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Run ./ci/steps/push-docker-manifest.sh - uses: ./ci/images/debian8 + uses: ./ci/images/debian10 with: args: ./ci/steps/push-docker-manifest.sh env: diff --git a/ci/dev/image/run.sh b/ci/dev/image/run.sh index 0f17d4c27..08391581b 100755 --- a/ci/dev/image/run.sh +++ b/ci/dev/image/run.sh @@ -11,11 +11,12 @@ main() { --rm \ -v "$PWD:/src" \ -e HOME="/src/.home" \ + -e USER="coder" \ -w /src \ -p 127.0.0.1:8080:8080 \ -u "$(id -u):$(id -g)" \ -e CI \ - "$(docker_build ./ci/images/debian8)" \ + "$(docker_build ./ci/images/"${IMAGE-debian10}")" \ "$@" } diff --git a/ci/dev/lint.sh b/ci/dev/lint.sh index 219c3793b..5f7c549bc 100755 --- a/ci/dev/lint.sh +++ b/ci/dev/lint.sh @@ -7,10 +7,7 @@ main() { eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js") stylelint $(git ls-files "*.css") tsc --noEmit - # See comment in ./ci/image/debian8 - if [[ ! ${CI-} ]]; then - shellcheck -e SC2046,SC2164,SC2154,SC1091,SC1090,SC2002 $(git ls-files "*.sh") - fi + shellcheck -e SC2046,SC2164,SC2154,SC1091,SC1090,SC2002 $(git ls-files "*.sh") } main "$@" diff --git a/ci/images/centos7/Dockerfile b/ci/images/centos7/Dockerfile index 92c212024..2c0c71ecd 100644 --- a/ci/images/centos7/Dockerfile +++ b/ci/images/centos7/Dockerfile @@ -15,7 +15,7 @@ RUN npm config set python python2 RUN yum install -y epel-release && yum install -y jq RUN yum install -y rsync -# Copied from ../debian8/Dockerfile +# Copied from ../debian10/Dockerfile # Install Go dependencies RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \ curl -fsSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz diff --git a/ci/images/debian8/Dockerfile b/ci/images/debian10/Dockerfile similarity index 57% rename from ci/images/debian8/Dockerfile rename to ci/images/debian10/Dockerfile index 4c62a398b..a13a25a03 100644 --- a/ci/images/debian8/Dockerfile +++ b/ci/images/debian10/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:8 +FROM debian:10 RUN apt-get update @@ -24,23 +24,13 @@ RUN apt-get install -y build-essential \ RUN apt-get install -y gettext-base # Misc build dependencies. -RUN apt-get install -y git rsync unzip - -# We need latest jq from debian buster for date support. -RUN ARCH="$(dpkg --print-architecture)" && \ - curl -fsSOL http://http.us.debian.org/debian/pool/main/libo/libonig/libonig5_6.9.1-1_$ARCH.deb && \ - dpkg -i libonig*.deb && \ - curl -fsSOL http://http.us.debian.org/debian/pool/main/j/jq/libjq1_1.5+dfsg-2+b1_$ARCH.deb && \ - dpkg -i libjq*.deb && \ - curl -fsSOL http://http.us.debian.org/debian/pool/main/j/jq/jq_1.5+dfsg-2+b1_$ARCH.deb && \ - dpkg -i jq*.deb && rm *.deb +RUN apt-get install -y git rsync unzip jq # Installs shellcheck. -# Unfortunately coredumps on debian:8 so disabled for now. -#RUN curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.$(uname -m).tar.xz | \ -# tar -xJ && \ -# mv shellcheck*/shellcheck /usr/local/bin && \ -# rm -R shellcheck* +RUN curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.$(uname -m).tar.xz | \ + tar -xJ && \ + mv shellcheck*/shellcheck /usr/local/bin && \ + rm -R shellcheck* # Install Go dependencies RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \ diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md index 80348848d..a15e25d20 100644 --- a/doc/CONTRIBUTING.md +++ b/doc/CONTRIBUTING.md @@ -32,7 +32,7 @@ Differences: - We require a minimum of node v12 but later versions should work. - We use [nfpm](https://github.com/goreleaser/nfpm) to build `.deb` and `.rpm` packages. - We use [jq](https://stedolan.github.io/jq/) to build code-server releases. -- The [CI container](../ci/images/debian8/Dockerfile) is a useful reference for all our dependencies. +- The [CI container](../ci/images/debian10/Dockerfile) is a useful reference for all our dependencies. ## Development Workflow @@ -76,7 +76,7 @@ node . Build release packages (make sure you run `./ci/steps/release.sh` first): ``` -./ci/dev/image/run.sh ./ci/steps/release-packages.sh +IMAGE=centos7 ./ci/dev/image/run.sh ./ci/steps/release-packages.sh # The standalone release is in ./release-standalone # .deb, .rpm and the standalone archive are in ./release-packages ``` From 22c4a7e10f8cfd4a7a6f752952221f80f37bc5f1 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Mon, 28 Sep 2020 15:43:26 -0400 Subject: [PATCH 09/29] Make linking and starting code-server to the cloud a single command --- src/node/cli.ts | 6 +++--- src/node/coder-cloud.ts | 2 +- src/node/entry.ts | 41 ++++++++++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index c4d0d9dd4..854686685 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -48,7 +48,7 @@ export interface Args extends VsArgs { readonly "reuse-window"?: boolean readonly "new-window"?: boolean - readonly expose?: OptionalString + readonly "coder-link"?: OptionalString } interface Option { @@ -159,10 +159,10 @@ const options: Options> = { log: { type: LogLevel }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, - expose: { + "coder-link": { type: OptionalString, description: ` - Securely expose code-server via Coder Cloud with the passed name. You'll get a URL like + Securely link code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub. Only the first code-server spawned with the current configuration will be accessible.`, diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index b621b08cd..c8782812b 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -9,7 +9,7 @@ import xdgBasedir from "xdg-basedir" const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") -export async function coderCloudExpose(serverName: string): Promise { +export async function coderCloudLink(serverName: string): Promise { const agent = spawn(coderCloudAgent, ["link", serverName], { stdio: ["inherit", "inherit", "pipe"], }) diff --git a/src/node/entry.ts b/src/node/entry.ts index 539b3bccd..42abbc2de 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -12,7 +12,7 @@ import { StaticHttpProvider } from "./app/static" import { UpdateHttpProvider } from "./app/update" import { VscodeHttpProvider } from "./app/vscode" import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli" -import { coderCloudExpose, coderCloudProxy } from "./coder-cloud" +import { coderCloudLink, coderCloudProxy } from "./coder-cloud" import { AuthType, HttpServer, HttpServerOptions } from "./http" import { loadPlugins } from "./plugin" import { generateCertificate, hash, humanPath, open } from "./util" @@ -36,6 +36,15 @@ const version = pkg.version || "development" const commit = pkg.commit || "development" const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise => { + if (args["coder-link"]) { + // If we're being exposed to the cloud, we listen on a random address. + args = { + ...args, + host: "localhost", + port: 0, + } + } + if (!args.auth) { args = { ...args, @@ -131,6 +140,22 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise await open(openAddress).catch(console.error) logger.info(`Opened ${openAddress}`) } + + if (args["coder-link"]) { + if (!args["coder-link"].value) { + logger.error("You must pass a name to link with coder cloud. See --help") + process.exit(1) + } + + logger.info(`linking code-server to the cloud with name ${args["coder-link"].value}`) + + try { + await coderCloudLink(args["coder-link"].value) + } catch (err) { + logger.error(err.message) + process.exit(1) + } + } } async function entry(): Promise { @@ -191,20 +216,6 @@ async function entry(): Promise { process.exit(1) }) vscode.on("exit", (code) => process.exit(code || 0)) - } else if (args["expose"]) { - logger.debug("exposing code-server via the coder-cloud agent") - - if (!args["expose"].value) { - logger.error("You must pass a name to expose with coder cloud. See --help") - process.exit(1) - } - - try { - await coderCloudExpose(args["expose"].value) - } catch (err) { - logger.error(err.message) - process.exit(1) - } } else if (process.env.VSCODE_IPC_HOOK_CLI) { const pipeArgs: OpenCommandPipeArgs = { type: "open", From 9035bfa871669a5ff8ab9e3ab4ae485d2d57c26f Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 14:19:04 -0400 Subject: [PATCH 10/29] Add coder cloud agent binary to build process --- ci/build/build-code-server.sh | 6 ++++++ ci/build/build-release.sh | 1 + ci/build/clean.sh | 3 ++- ci/dev/image/run.sh | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ci/build/build-code-server.sh b/ci/build/build-code-server.sh index df0852804..0aff035af 100755 --- a/ci/build/build-code-server.sh +++ b/ci/build/build-code-server.sh @@ -18,6 +18,12 @@ main() { chmod +x out/node/entry.js fi + if ! [ -f ./lib/coder-cloud-agent ]; then + OS="$(uname | tr '[:upper:]' '[:lower:]')" + curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent + chmod +x ./lib/coder-cloud-agent + fi + parcel build \ --public-url "." \ --out-dir dist \ diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index 8d8d1c903..88d3fe613 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -25,6 +25,7 @@ main() { rsync README.md "$RELEASE_PATH" rsync LICENSE.txt "$RELEASE_PATH" rsync ./lib/vscode/ThirdPartyNotices.txt "$RELEASE_PATH" + rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib" # code-server exports types which can be imported and used by plugins. Those # types import ipc.d.ts but it isn't included in the final vscode build so diff --git a/ci/build/clean.sh b/ci/build/clean.sh index 7a83a2845..52d123c9a 100755 --- a/ci/build/clean.sh +++ b/ci/build/clean.sh @@ -15,7 +15,8 @@ main() { dist \ .cache \ node-* \ - .home + .home \ + lib/coder-cloud-agent pushd lib/vscode git clean -xffd diff --git a/ci/dev/image/run.sh b/ci/dev/image/run.sh index 08391581b..0557f1b2a 100755 --- a/ci/dev/image/run.sh +++ b/ci/dev/image/run.sh @@ -12,6 +12,7 @@ main() { -v "$PWD:/src" \ -e HOME="/src/.home" \ -e USER="coder" \ + -e GITHUB_TOKEN \ -w /src \ -p 127.0.0.1:8080:8080 \ -u "$(id -u):$(id -g)" \ From c308ae0eddb71bbeea3da025c1fdb676e4485ac3 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 14:21:08 -0400 Subject: [PATCH 11/29] Ignore dirty lib/vscode --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 9854a1b1d..f2cdafc7a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "lib/vscode"] path = lib/vscode url = https://github.com/microsoft/vscode + ignore = dirty From fae07e14fbd2ed4101d25c8c5bf715efcd041253 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 14:36:55 -0400 Subject: [PATCH 12/29] Fix Go inside dev image --- ci/images/centos7/Dockerfile | 9 +++++++-- ci/images/debian10/Dockerfile | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ci/images/centos7/Dockerfile b/ci/images/centos7/Dockerfile index 2c0c71ecd..52a101778 100644 --- a/ci/images/centos7/Dockerfile +++ b/ci/images/centos7/Dockerfile @@ -16,10 +16,15 @@ RUN yum install -y epel-release && yum install -y jq RUN yum install -y rsync # Copied from ../debian10/Dockerfile -# Install Go dependencies +# Install Go. RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \ curl -fsSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz -ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH +ENV GOPATH=/gopath +# Ensures running this image as another user works. +RUN mkdir -p $GOPATH && chmod -R 777 $GOPATH +ENV PATH=/usr/local/go/bin:$GOPATH/bin:$PATH + +# Install Go dependencies ENV GO111MODULE=on RUN go get mvdan.cc/sh/v3/cmd/shfmt RUN go get github.com/goreleaser/nfpm/cmd/nfpm diff --git a/ci/images/debian10/Dockerfile b/ci/images/debian10/Dockerfile index a13a25a03..108348b65 100644 --- a/ci/images/debian10/Dockerfile +++ b/ci/images/debian10/Dockerfile @@ -32,10 +32,15 @@ RUN curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v0.7.1/s mv shellcheck*/shellcheck /usr/local/bin && \ rm -R shellcheck* -# Install Go dependencies +# Install Go. RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \ curl -fsSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz -ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH +ENV GOPATH=/gopath +# Ensures running this image as another user works. +RUN mkdir -p $GOPATH && chmod -R 777 $GOPATH +ENV PATH=/usr/local/go/bin:$GOPATH/bin:$PATH + +# Install Go dependencies ENV GO111MODULE=on RUN go get mvdan.cc/sh/v3/cmd/shfmt RUN go get github.com/goreleaser/nfpm/cmd/nfpm From dd996d8f6051741baff7ef1a37cfd8e21e2bd61b Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 14:45:27 -0400 Subject: [PATCH 13/29] v3.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5bf72f641..878eebf18 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-server", "license": "MIT", - "version": "3.5.0", + "version": "3.6.0", "description": "Run VS Code on a remote server.", "homepage": "https://github.com/cdr/code-server", "bugs": { From 6e8248cf0cf16383af7f6d732862a5d021649ea4 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 17:19:36 -0400 Subject: [PATCH 14/29] Fix zip release creation --- ci/build/build-packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build/build-packages.sh b/ci/build/build-packages.sh index 058a54781..c3ad6577a 100755 --- a/ci/build/build-packages.sh +++ b/ci/build/build-packages.sh @@ -33,7 +33,7 @@ release_archive() { elif [[ $OS == "darwin" && $ARCH == "x86_64" ]]; then # Just exists to make autoupdating from 3.2.0 work again. mv ./release-standalone "./$release_name" - zip -r "release-packages/$release_name.zip" "./$release_name" + zip -yr "release-packages/$release_name.zip" "./$release_name" mv "./$release_name" ./release-standalone return else From c3c24fe4d2935ae553ddd5a6be192f449b3f4512 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 21:05:32 -0400 Subject: [PATCH 15/29] Fixes for @ammarb --- src/node/cli.ts | 8 ++++---- src/node/coder-cloud.ts | 2 +- src/node/entry.ts | 22 +++++++++------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 854686685..61e5d9d78 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -48,7 +48,7 @@ export interface Args extends VsArgs { readonly "reuse-window"?: boolean readonly "new-window"?: boolean - readonly "coder-link"?: OptionalString + readonly "coder-bind"?: string } interface Option { @@ -159,10 +159,10 @@ const options: Options> = { log: { type: LogLevel }, verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, - "coder-link": { - type: OptionalString, + "coder-bind": { + type: "string", description: ` - Securely link code-server via Coder Cloud with the passed name. You'll get a URL like + Securely bind code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub. Only the first code-server spawned with the current configuration will be accessible.`, diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index c8782812b..5f7b7c0a1 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -9,7 +9,7 @@ import xdgBasedir from "xdg-basedir" const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") -export async function coderCloudLink(serverName: string): Promise { +export async function coderCloudBind(serverName: string): Promise { const agent = spawn(coderCloudAgent, ["link", serverName], { stdio: ["inherit", "inherit", "pipe"], }) diff --git a/src/node/entry.ts b/src/node/entry.ts index 42abbc2de..1be886e03 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -12,7 +12,7 @@ import { StaticHttpProvider } from "./app/static" import { UpdateHttpProvider } from "./app/update" import { VscodeHttpProvider } from "./app/vscode" import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli" -import { coderCloudLink, coderCloudProxy } from "./coder-cloud" +import { coderCloudBind, coderCloudProxy } from "./coder-cloud" import { AuthType, HttpServer, HttpServerOptions } from "./http" import { loadPlugins } from "./plugin" import { generateCertificate, hash, humanPath, open } from "./util" @@ -36,13 +36,15 @@ const version = pkg.version || "development" const commit = pkg.commit || "development" const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise => { - if (args["coder-link"]) { - // If we're being exposed to the cloud, we listen on a random address. + if (args["coder-bind"]) { + // If we're being exposed to the cloud, we listen on a random address and disable auth. args = { ...args, host: "localhost", port: 0, + auth: AuthType.None, } + logger.info("coder-bind: disabling auth and listening on random localhost port") } if (!args.auth) { @@ -132,8 +134,6 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise httpServer.proxyDomains.forEach((domain) => logger.info(` - *.${domain}`)) } - coderCloudProxy(serverAddress!) - if (serverAddress && !options.socket && args.open) { // The web socket doesn't seem to work if browsing with 0.0.0.0. const openAddress = serverAddress.replace(/:\/\/0.0.0.0/, "://localhost") @@ -141,16 +141,12 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise logger.info(`Opened ${openAddress}`) } - if (args["coder-link"]) { - if (!args["coder-link"].value) { - logger.error("You must pass a name to link with coder cloud. See --help") - process.exit(1) - } - - logger.info(`linking code-server to the cloud with name ${args["coder-link"].value}`) + if (args["coder-bind"]) { + logger.info(`linking code-server to the cloud with name ${args["coder-bind"]}`) try { - await coderCloudLink(args["coder-link"].value) + await coderCloudBind(args["coder-bind"]) + coderCloudProxy(serverAddress!) } catch (err) { logger.error(err.message) process.exit(1) From 1c16814a89e8c057591101763e4d39b54bc6d8f8 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Tue, 6 Oct 2020 21:10:46 -0400 Subject: [PATCH 16/29] Update coder-bind docs --- src/node/cli.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 61e5d9d78..d7ae7fc23 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -164,8 +164,8 @@ const options: Options> = { description: ` Securely bind code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. - Authorization is done via GitHub. Only the first code-server spawned with the current - configuration will be accessible.`, + Authorization is done via GitHub. + `, }, } From 4b3c089630aa0ad495e3177df824f0bb0c7d60fa Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 02:03:27 -0400 Subject: [PATCH 17/29] Remove dead code --- package.json | 3 +- src/node/coder-cloud.ts | 85 ----------------------------------------- src/node/entry.ts | 4 +- 3 files changed, 3 insertions(+), 89 deletions(-) diff --git a/package.json b/package.json index 878eebf18..cc3edd30a 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,7 @@ "tar-fs": "^2.0.0", "ws": "^7.2.0", "xdg-basedir": "^4.0.0", - "yarn": "^1.22.4", - "delay": "^4.4.0" + "yarn": "^1.22.4" }, "bin": { "code-server": "out/node/entry.js" diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index 5f7b7c0a1..a3c3c5906 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -1,11 +1,7 @@ import { logger } from "@coder/logger" import { spawn } from "child_process" -import delay from "delay" -import fs from "fs" import path from "path" import split2 from "split2" -import { promisify } from "util" -import xdgBasedir from "xdg-basedir" const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") @@ -39,17 +35,7 @@ export function coderCloudProxy(addr: string) { // So we trim the protocol. addr = addr.replace(/^https?:\/\//, "") - if (!xdgBasedir.config) { - return - } - - const sessionTokenPath = path.join(xdgBasedir.config, "coder-cloud", "session") - const _proxy = async () => { - await waitForPath(sessionTokenPath) - - logger.info("exposing coder-server with coder-cloud") - const agent = spawn(coderCloudAgent, ["proxy", "--code-server-addr", addr], { stdio: ["inherit", "inherit", "pipe"], }) @@ -84,74 +70,3 @@ export function coderCloudProxy(addr: string) { } proxy() } - -/** - * waitForPath efficiently implements waiting for the existence of a path. - * - * We intentionally do not use fs.watchFile as it is very slow from testing. - * I believe it polls instead of watching. - * - * The way this works is for each level of the path it will check if it exists - * and if not, it will wait for it. e.g. if the path is /home/nhooyr/.config/coder-cloud/session - * then first it will check if /home exists, then /home/nhooyr and so on. - * - * The wait works by first creating a watch promise for the p segment. - * We call fs.watch on the dirname of the p segment. When the dirname has a change, - * we check if the p segment exists and if it does, we resolve the watch promise. - * On any error or the watcher being closed, we reject the watch promise. - * - * Once that promise is setup, we check if the p segment exists with fs.exists - * and if it does, we close the watcher and return. - * - * Now we race the watch promise and a 2000ms delay promise. Once the race - * is complete, we close the watcher. - * - * If the watch promise was the one to resolve, we return. - * Otherwise we setup the watch promise again and retry. - * - * This combination of polling and watching is very reliable and efficient. - */ -async function waitForPath(p: string): Promise { - const segs = p.split(path.sep) - for (let i = 0; i < segs.length; i++) { - const s = path.join("/", ...segs.slice(0, i + 1)) - // We need to wait for each segment to exist. - await _waitForPath(s) - } -} - -async function _waitForPath(p: string): Promise { - const watchDir = path.dirname(p) - - logger.debug(`waiting for ${p}`) - - for (;;) { - const w = fs.watch(watchDir) - const watchPromise = new Promise((res, rej) => { - w.on("change", async () => { - if (await promisify(fs.exists)(p)) { - res() - } - }) - w.on("close", () => rej(new Error("watcher closed"))) - w.on("error", rej) - }) - - // We want to ignore any errors from this promise being rejected if the file - // already exists below. - watchPromise.catch(() => {}) - - if (await promisify(fs.exists)(p)) { - // The path exists! - w.close() - return - } - - // Now we wait for either the watch promise to resolve/reject or 2000ms. - const s = await Promise.race([watchPromise.then(() => "exists"), delay(2000)]) - w.close() - if (s === "exists") { - return - } - } -} diff --git a/src/node/entry.ts b/src/node/entry.ts index 1be886e03..901c732f7 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -142,14 +142,14 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise } if (args["coder-bind"]) { - logger.info(`linking code-server to the cloud with name ${args["coder-bind"]}`) try { + logger.info(`binding code-server to the cloud with name ${args["coder-bind"]}`) await coderCloudBind(args["coder-bind"]) coderCloudProxy(serverAddress!) } catch (err) { logger.error(err.message) - process.exit(1) + ipcMain().exit(1) } } } From c4f1c053bf51536cc3884d6a61b256c457e17dd4 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 03:52:37 -0400 Subject: [PATCH 18/29] Show valid values for --auth in --help See https://github.com/nhooyr/code-server/pull/1/files#r485847134 --- src/node/cli.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index d7ae7fc23..deedf8309 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -192,7 +192,8 @@ export const optionDescriptions = (): string[] => { } return " ".repeat(widths.long + widths.short + 6) + line }) - .join("\n") + .join("\n") + + (typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : "") ) }) } From bfe731f4f30782528a054f7d1e103ef9e95b9b2b Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 12:16:27 -0400 Subject: [PATCH 19/29] Ensure socket is undefined with --coder-bind --- src/node/entry.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/entry.ts b/src/node/entry.ts index 901c732f7..16118a1ef 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -38,11 +38,12 @@ const commit = pkg.commit || "development" const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise => { if (args["coder-bind"]) { // If we're being exposed to the cloud, we listen on a random address and disable auth. - args = { - ...args, + cliArgs = { + ...cliArgs, host: "localhost", port: 0, auth: AuthType.None, + socket: undefined, } logger.info("coder-bind: disabling auth and listening on random localhost port") } From 7cc16ceb3aee37b8fb9a3afc17c21b1cb6f7a2e4 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 13:26:57 -0400 Subject: [PATCH 20/29] Document KEEP_MODULES --- ci/dev/image/run.sh | 2 ++ doc/CONTRIBUTING.md | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/ci/dev/image/run.sh b/ci/dev/image/run.sh index 0557f1b2a..3d5e15dd6 100755 --- a/ci/dev/image/run.sh +++ b/ci/dev/image/run.sh @@ -13,6 +13,8 @@ main() { -e HOME="/src/.home" \ -e USER="coder" \ -e GITHUB_TOKEN \ + -e KEEP_MODULES \ + -e MINIFY \ -w /src \ -p 127.0.0.1:8080:8080 \ -u "$(id -u):$(id -g)" \ diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md index a15e25d20..62c20f915 100644 --- a/doc/CONTRIBUTING.md +++ b/doc/CONTRIBUTING.md @@ -99,6 +99,13 @@ yarn test:standalone-release yarn package ``` +For a faster release build you can also run: + +``` +KEEP_MODULES=1 ./ci/steps/release.sh +node ./release +``` + ## Structure The `code-server` script serves an HTTP API to login and start a remote VS Code process. From df3089f3ad4b8231407e365819ef88aac42fcd76 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 15:54:41 -0400 Subject: [PATCH 21/29] coder-cloud: Use consolidated bind command --- src/node/cli.ts | 4 ++-- src/node/coder-cloud.ts | 44 +++++------------------------------------ src/node/entry.ts | 7 ++----- 3 files changed, 9 insertions(+), 46 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index deedf8309..ed188f281 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -48,7 +48,7 @@ export interface Args extends VsArgs { readonly "reuse-window"?: boolean readonly "new-window"?: boolean - readonly "coder-bind"?: string + readonly "coder-bind"?: OptionalString } interface Option { @@ -160,7 +160,7 @@ const options: Options> = { verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, "coder-bind": { - type: "string", + type: OptionalString, description: ` Securely bind code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index a3c3c5906..b57cf36df 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -5,8 +5,8 @@ import split2 from "split2" const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") -export async function coderCloudBind(serverName: string): Promise { - const agent = spawn(coderCloudAgent, ["link", serverName], { +function runAgent(...args: string[]): Promise { + const agent = spawn(coderCloudAgent, args, { stdio: ["inherit", "inherit", "pipe"], }) @@ -30,43 +30,9 @@ export async function coderCloudBind(serverName: string): Promise { }) } -export function coderCloudProxy(addr: string) { +export function coderCloudBind(csAddr: string, serverName = ""): Promise { // addr needs to be in host:port format. // So we trim the protocol. - addr = addr.replace(/^https?:\/\//, "") - - const _proxy = async () => { - const agent = spawn(coderCloudAgent, ["proxy", "--code-server-addr", addr], { - stdio: ["inherit", "inherit", "pipe"], - }) - - agent.stderr.pipe(split2()).on("data", (line) => { - line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "") - logger.info(line) - }) - - return new Promise((res, rej) => { - agent.on("error", rej) - - agent.on("close", (code) => { - if (code !== 0) { - rej({ - message: `coder cloud agent exited with ${code}`, - }) - return - } - res() - }) - }) - } - - const proxy = async () => { - try { - await _proxy() - } catch (err) { - logger.error(err.message) - } - setTimeout(proxy, 3000) - } - proxy() + csAddr = csAddr.replace(/^https?:\/\//, "") + return runAgent("bind", `--code-server-addr=${csAddr}`, serverName) } diff --git a/src/node/entry.ts b/src/node/entry.ts index 16118a1ef..a39db7efb 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -12,7 +12,7 @@ import { StaticHttpProvider } from "./app/static" import { UpdateHttpProvider } from "./app/update" import { VscodeHttpProvider } from "./app/vscode" import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli" -import { coderCloudBind, coderCloudProxy } from "./coder-cloud" +import { coderCloudBind } from "./coder-cloud" import { AuthType, HttpServer, HttpServerOptions } from "./http" import { loadPlugins } from "./plugin" import { generateCertificate, hash, humanPath, open } from "./util" @@ -143,11 +143,8 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise } if (args["coder-bind"]) { - try { - logger.info(`binding code-server to the cloud with name ${args["coder-bind"]}`) - await coderCloudBind(args["coder-bind"]) - coderCloudProxy(serverAddress!) + await coderCloudBind(serverAddress!, args["coder-bind"].value) } catch (err) { logger.error(err.message) ipcMain().exit(1) From ebbcb8d6a7e2fc0acbe02c2534e11b46650a6a81 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 16:09:40 -0400 Subject: [PATCH 22/29] Update yarn.lock --- yarn.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8e13b6f35..6f388626b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2525,11 +2525,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -delay@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-4.4.0.tgz#71abc745f3ce043fe7f450491236541edec4ad0c" - integrity sha512-txgOrJu3OdtOfTiEOT2e76dJVfG/1dz2NZ4F0Pyt4UGZJryssMRp5vdM5wQoLwSOBNdrJv3F9PAhp/heqd7vrA== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" From 85b0804be5174940851ebdccc7d58b3eba581a32 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 16:21:10 -0400 Subject: [PATCH 23/29] Remove cliArgs from main No purpose when all the args are in the args parameter. We only need configArgs for bindAddrFromAllSources. --- ci/build/build-release.sh | 1 - src/node/entry.ts | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index 88d3fe613..eedda98e2 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -58,7 +58,6 @@ EOF rsync yarn.lock "$RELEASE_PATH" rsync ci/build/npm-postinstall.sh "$RELEASE_PATH/postinstall.sh" - if [ "$KEEP_MODULES" = 1 ]; then rsync node_modules/ "$RELEASE_PATH/node_modules" fi diff --git a/src/node/entry.ts b/src/node/entry.ts index a39db7efb..664134197 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -35,11 +35,11 @@ try { const version = pkg.version || "development" const commit = pkg.commit || "development" -const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise => { +const main = async (args: Args, configArgs: Args): Promise => { if (args["coder-bind"]) { // If we're being exposed to the cloud, we listen on a random address and disable auth. - cliArgs = { - ...cliArgs, + args = { + ...args, host: "localhost", port: 0, auth: AuthType.None, @@ -64,7 +64,7 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise if (args.auth === AuthType.Password && !password) { throw new Error("Please pass in a password via the config file or $PASSWORD") } - const [host, port] = bindAddrFromAllSources(cliArgs, configArgs) + const [host, port] = bindAddrFromAllSources(args, configArgs) // Spawn the main HTTP server. const options: HttpServerOptions = { @@ -153,21 +153,21 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise } async function entry(): Promise { - const tryParse = async (): Promise<[Args, Args, Args]> => { + const tryParse = async (): Promise<[Args, Args]> => { try { const cliArgs = parse(process.argv.slice(2)) const configArgs = await readConfigFile(cliArgs.config) // This prioritizes the flags set in args over the ones in the config file. let args = Object.assign(configArgs, cliArgs) args = await setDefaults(args) - return [args, cliArgs, configArgs] + return [args, configArgs] } catch (error) { console.error(error.message) process.exit(1) } } - const [args, cliArgs, configArgs] = await tryParse() + const [args, configArgs] = await tryParse() if (args.help) { console.log("code-server", version, commit) console.log("") @@ -262,7 +262,7 @@ async function entry(): Promise { vscode.write(JSON.stringify(pipeArgs)) vscode.end() } else { - wrap(() => main(args, cliArgs, configArgs)) + wrap(() => main(args, configArgs)) } } From 3e28ab85a072e8df7cecd295fbd3e01b4d08d40d Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 17:16:20 -0400 Subject: [PATCH 24/29] Add debug log for options passed to the agent --- src/node/coder-cloud.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index b57cf36df..3b36a2f57 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -6,6 +6,8 @@ import split2 from "split2" const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent") function runAgent(...args: string[]): Promise { + logger.debug(`running agent with ${args}`) + const agent = spawn(coderCloudAgent, args, { stdio: ["inherit", "inherit", "pipe"], }) From febf4ead9631a252695c25690f61c416b49c60ec Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 7 Oct 2020 17:28:13 -0400 Subject: [PATCH 25/29] Fix the clean script :facepalm: --- ci/build/clean.sh | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/ci/build/clean.sh b/ci/build/clean.sh index 52d123c9a..b80632278 100755 --- a/ci/build/clean.sh +++ b/ci/build/clean.sh @@ -5,18 +5,7 @@ main() { cd "$(dirname "${0}")/../.." source ./ci/lib.sh - rm -rf \ - out \ - release \ - release-standalone \ - release-packages \ - release-gcp \ - release-images \ - dist \ - .cache \ - node-* \ - .home \ - lib/coder-cloud-agent + git clean -Xffd pushd lib/vscode git clean -xffd From f5489cd3a0c119ddf4ee01eaa02b5051fc042413 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 9 Oct 2020 07:38:38 -0400 Subject: [PATCH 26/29] Hide -coder-bind for now --- src/node/cli.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index ed188f281..80c1e7402 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -65,6 +65,11 @@ interface Option { * Description of the option. Leave blank to hide the option. */ description?: string + + /** + * Whether to print this option in --help output + */ + hidden?: boolean } type OptionType = T extends boolean @@ -166,6 +171,7 @@ const options: Options> = { https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub. `, + hidden: true, }, } @@ -178,7 +184,7 @@ export const optionDescriptions = (): string[] => { }), { short: 0, long: 0 }, ) - return entries.map(([k, v]) => { + return entries.filter(([_, v]) => !v.hidden).map(([k, v]) => { const help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k} ` return ( help + From 9ff37977a8169fcde8a770d1f08b4961841d2b77 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 9 Oct 2020 07:38:58 -0400 Subject: [PATCH 27/29] Make --coder-bind disable HTTPS --- src/node/entry.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/entry.ts b/src/node/entry.ts index 664134197..d05d5d552 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -44,6 +44,7 @@ const main = async (args: Args, configArgs: Args): Promise => { port: 0, auth: AuthType.None, socket: undefined, + cert: undefined, } logger.info("coder-bind: disabling auth and listening on random localhost port") } From a5b6d080bd425280f0c3d2a9c139f835c17f817d Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 9 Oct 2020 07:45:20 -0400 Subject: [PATCH 28/29] Add CS_BETA and note --coder-bind is in beta --- src/node/cli.ts | 52 ++++++++++++++++++++++++----------------- src/node/coder-cloud.ts | 2 ++ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 80c1e7402..5e9e71534 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -67,9 +67,9 @@ interface Option { description?: string /** - * Whether to print this option in --help output + * If marked as beta, the option is not printed unless $CS_BETA is set. */ - hidden?: boolean + beta?: boolean } type OptionType = T extends boolean @@ -170,8 +170,10 @@ const options: Options> = { Securely bind code-server via Coder Cloud with the passed name. You'll get a URL like https://myname.coder-cloud.com at which you can easily access your code-server instance. Authorization is done via GitHub. + This is presently beta and requires being accepted for testing. + See https://github.com/cdr/code-server/discussions/2137 `, - hidden: true, + beta: true, }, } @@ -184,24 +186,32 @@ export const optionDescriptions = (): string[] => { }), { short: 0, long: 0 }, ) - return entries.filter(([_, v]) => !v.hidden).map(([k, v]) => { - const help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k} ` - return ( - help + - v.description - ?.trim() - .split(/\n/) - .map((line, i) => { - line = line.trim() - if (i === 0) { - return " ".repeat(widths.long - k.length) + line - } - return " ".repeat(widths.long + widths.short + 6) + line - }) - .join("\n") + - (typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : "") - ) - }) + return entries + .filter(([, v]) => { + // If CS_BETA is set, we show beta options but if not, then we do not want + // to show beta options. + return process.env.CS_BETA || !v.beta + }) + .map(([k, v]) => { + const help = `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${ + v.short ? `-${v.short}` : " " + } --${k} ` + return ( + help + + v.description + ?.trim() + .split(/\n/) + .map((line, i) => { + line = line.trim() + if (i === 0) { + return " ".repeat(widths.long - k.length) + line + } + return " ".repeat(widths.long + widths.short + 6) + line + }) + .join("\n") + + (typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : "") + ) + }) } export const parse = ( diff --git a/src/node/coder-cloud.ts b/src/node/coder-cloud.ts index 3b36a2f57..f8038cbe9 100644 --- a/src/node/coder-cloud.ts +++ b/src/node/coder-cloud.ts @@ -33,6 +33,8 @@ function runAgent(...args: string[]): Promise { } export function coderCloudBind(csAddr: string, serverName = ""): Promise { + logger.info("Remember --coder-bind is a beta feature and requires being accepted for testing") + logger.info("See https://github.com/cdr/code-server/discussions/2137") // addr needs to be in host:port format. // So we trim the protocol. csAddr = csAddr.replace(/^https?:\/\//, "") From 9002f118c3358a228bd561353cb764a2bfaff7b5 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 9 Oct 2020 07:50:58 -0400 Subject: [PATCH 29/29] Remove the extra releases for autoupdating purposes --- ci/build/build-packages.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ci/build/build-packages.sh b/ci/build/build-packages.sh index c3ad6577a..a5ef794e5 100755 --- a/ci/build/build-packages.sh +++ b/ci/build/build-packages.sh @@ -11,15 +11,6 @@ main() { mkdir -p release-packages release_archive - # Will stop the auto update issues and allow people to upgrade their scripts - # for the new release structure. - if [[ $ARCH == "amd64" ]]; then - if [[ $OS == "linux" ]]; then - ARCH=x86_64 release_archive - elif [[ $OS == "macos" ]]; then - OS=darwin ARCH=x86_64 release_archive - fi - fi if [[ $OS == "linux" ]]; then release_nfpm @@ -30,12 +21,6 @@ release_archive() { local release_name="code-server-$VERSION-$OS-$ARCH" if [[ $OS == "linux" ]]; then tar -czf "release-packages/$release_name.tar.gz" --transform "s/^\.\/release-standalone/$release_name/" ./release-standalone - elif [[ $OS == "darwin" && $ARCH == "x86_64" ]]; then - # Just exists to make autoupdating from 3.2.0 work again. - mv ./release-standalone "./$release_name" - zip -yr "release-packages/$release_name.zip" "./$release_name" - mv "./$release_name" ./release-standalone - return else tar -czf "release-packages/$release_name.tar.gz" -s "/^release-standalone/$release_name/" release-standalone fi