Skip to content

Commit 1b60ef4

Browse files
Teffencode-asher
Teffen
andauthored
Use upstream server (#4414)
* Flesh out fixes to align with upstream. * Update route handlers to better reflect fallback behavior. * Add platform to vscode-reh-web task Our strategy has been to build once and then recompile native modules for individual platforms. It looks like VS Code builds from scratch for each platform. But we can target any platform, grab the pre-packaged folder, then continue with own packaging. In the future we may want to rework to match upstream. * Fix issue where workspace args are not parsed. * Fix issues surrounding opening files within code-server's terminal. * Readd parent wrapper for hot reload. * Allow more errors. * Fix issues surrounding Coder link. * Add dir creation and fix cli It seems VS Code explodes when certain directories do not exist so import the reh agent instead of the server component since it creates the directories (require patching thus the VS Code update). Also the CLI (for installing extensions) did not seem to be working so point that to the same place since it also exports a function for running that part of the CLI. * Remove hardcoded VSCODE_DEV=1 This causes VS Code to use the development HTML file. Move this to the watch command instead. I deleted the other stuff before it as well since in the latest main.js they do not have this code so I figure we should be safe to omit it. * Fix mismatching commit between client and server * Mostly restore command-line parity Restore most everything and remove the added server arguments. This will let us add and remove options after later so we can contain the number of breaking changes. To accomplish this a hard separation is added between the CLI arguments and the server arguments. The separation between user-provided arguments and arguments with defaults is also made more clear. The extra directory flags have been left out as they were buggy and should be implemented upstream although I think there are better solutions anyway. locale and install-source are unsupported with the web remote and are left removed. It is unclear whether they were used before anyway. Some restored flags still need to have their behavior re-implemented. * Fix static endpoint not emitting 404s This fixes the last failing unit test. Fix a missing dependency, add some generic reverse proxy support for the protocol, and add back a missing nfpm fix. * Import missing logError * Fix 403 errors * Add code-server version to about dialog * Use user settings to disable welcome page The workspace setting seems to be recognized but if so it is having no effect. * Update VS Code cache step with new build directories Co-authored-by: Asher <[email protected]>
1 parent 31d5823 commit 1b60ef4

File tree

18 files changed

+286
-276
lines changed

18 files changed

+286
-276
lines changed

.github/workflows/ci.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ jobs:
142142
path: |
143143
vendor/modules/code-oss-dev/.build
144144
vendor/modules/code-oss-dev/out-build
145-
vendor/modules/code-oss-dev/out-vscode-server
146-
vendor/modules/code-oss-dev/out-vscode-server-min
147-
key: vscode-server-build-${{ steps.vscode-rev.outputs.rev }}
145+
vendor/modules/code-oss-dev/out-vscode-reh-web
146+
vendor/modules/code-oss-dev/out-vscode-reh-web-min
147+
key: vscode-reh-build-${{ steps.vscode-rev.outputs.rev }}
148148

149149
- name: Build vscode
150150
if: steps.cache-vscode.outputs.cache-hit != 'true'

ci/build/build-release.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ EOF
6767
bundle_vscode() {
6868
mkdir -p "$VSCODE_OUT_PATH"
6969
rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH"
70-
rsync "$VSCODE_SRC_PATH/out-vscode-server${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out"
70+
rsync "$VSCODE_SRC_PATH/out-vscode-reh-web${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out"
7171

7272
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
7373
if [ "$KEEP_MODULES" = 0 ]; then
@@ -88,7 +88,7 @@ bundle_vscode() {
8888
cat << EOF
8989
{
9090
"enableTelemetry": true,
91-
"commit": "$(git rev-parse HEAD)",
91+
"commit": "$(cd "$VSCODE_SRC_PATH" && git rev-parse HEAD)",
9292
"quality": "stable",
9393
"date": $(jq -n 'now | todate')
9494
}

ci/build/build-vscode.sh

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ main() {
1111

1212
cd vendor/modules/code-oss-dev
1313

14-
# extensions-ci compiles extensions and includes their media.
15-
# compile-web compiles web extensions. TODO: Unsure if used.
16-
yarn gulp extensions-ci compile-web "vscode-server${MINIFY:+-min}"
14+
# Any platform works since we have our own packaging step (for now).
15+
yarn gulp "vscode-reh-web-linux-x64${MINIFY:+-min}"
1716
}
1817

1918
main "$@"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"lint": "./ci/dev/lint.sh",
2929
"test": "echo 'Run yarn test:unit or yarn test:e2e' && exit 1",
3030
"ci": "./ci/dev/ci.sh",
31-
"watch": "VSCODE_IPC_HOOK_CLI= NODE_OPTIONS='--max_old_space_size=32384 --trace-warnings' ts-node ./ci/dev/watch.ts",
31+
"watch": "VSCODE_DEV=1 VSCODE_IPC_HOOK_CLI= NODE_OPTIONS='--max_old_space_size=32384 --trace-warnings' ts-node ./ci/dev/watch.ts",
3232
"icons": "./ci/dev/gen_icons.sh",
3333
"coverage": "codecov"
3434
},

src/node/cli.ts

+97-50
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import { promises as fs } from "fs"
33
import yaml from "js-yaml"
44
import * as os from "os"
55
import * as path from "path"
6-
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util"
6+
import {
7+
canConnect,
8+
generateCertificate,
9+
generatePassword,
10+
humanPath,
11+
paths,
12+
isNodeJSErrnoException,
13+
isFile,
14+
} from "./util"
715

816
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
917

@@ -31,53 +39,53 @@ export enum LogLevel {
3139

3240
export class OptionalString extends Optional<string> {}
3341

34-
export interface Args
35-
extends Pick<
36-
CodeServerLib.NativeParsedArgs,
37-
| "_"
38-
| "user-data-dir"
39-
| "enable-proposed-api"
40-
| "extensions-dir"
41-
| "builtin-extensions-dir"
42-
| "extra-extensions-dir"
43-
| "extra-builtin-extensions-dir"
44-
| "ignore-last-opened"
45-
| "locale"
46-
| "log"
47-
| "verbose"
48-
| "install-source"
49-
| "list-extensions"
50-
| "install-extension"
51-
| "uninstall-extension"
52-
| "locate-extension"
53-
// | "telemetry"
54-
> {
42+
/**
43+
* Arguments that the user explicitly provided on the command line. All
44+
* arguments must be optional.
45+
*
46+
* For arguments with defaults see DefaultedArgs.
47+
*/
48+
export interface UserProvidedArgs {
5549
config?: string
5650
auth?: AuthType
5751
password?: string
5852
"hashed-password"?: string
5953
cert?: OptionalString
6054
"cert-host"?: string
6155
"cert-key"?: string
62-
"disable-telemetry"?: boolean
6356
"disable-update-check"?: boolean
6457
enable?: string[]
6558
help?: boolean
6659
host?: string
60+
port?: number
6761
json?: boolean
6862
log?: LogLevel
6963
open?: boolean
70-
port?: number
7164
"bind-addr"?: string
7265
socket?: string
7366
version?: boolean
74-
force?: boolean
75-
"show-versions"?: boolean
7667
"proxy-domain"?: string[]
7768
"reuse-window"?: boolean
7869
"new-window"?: boolean
79-
70+
"ignore-last-opened"?: boolean
8071
link?: OptionalString
72+
verbose?: boolean
73+
/* Positional arguments. */
74+
_?: string[]
75+
76+
// VS Code flags.
77+
"disable-telemetry"?: boolean
78+
force?: boolean
79+
"user-data-dir"?: string
80+
"enable-proposed-api"?: string[]
81+
"extensions-dir"?: string
82+
"builtin-extensions-dir"?: string
83+
"install-extension"?: string[]
84+
"uninstall-extension"?: string[]
85+
"list-extensions"?: boolean
86+
"locate-extension"?: string[]
87+
"show-versions"?: boolean
88+
category?: string
8189
}
8290

8391
interface Option<T> {
@@ -121,7 +129,7 @@ type Options<T> = {
121129
[P in keyof T]: Option<OptionType<T[P]>>
122130
}
123131

124-
const options: Options<Required<Args>> = {
132+
const options: Options<Required<UserProvidedArgs>> = {
125133
auth: { type: AuthType, description: "The type of authentication to use." },
126134
password: {
127135
type: "string",
@@ -178,12 +186,10 @@ const options: Options<Required<Args>> = {
178186
"user-data-dir": { type: "string", path: true, description: "Path to the user data directory." },
179187
"extensions-dir": { type: "string", path: true, description: "Path to the extensions directory." },
180188
"builtin-extensions-dir": { type: "string", path: true },
181-
"extra-extensions-dir": { type: "string[]", path: true },
182-
"extra-builtin-extensions-dir": { type: "string[]", path: true },
183189
"list-extensions": { type: "boolean", description: "List installed VS Code extensions." },
184190
force: { type: "boolean", description: "Avoid prompts when installing VS Code extensions." },
185-
"install-source": { type: "string" },
186191
"locate-extension": { type: "string[]" },
192+
category: { type: "string" },
187193
"install-extension": {
188194
type: "string[]",
189195
description:
@@ -214,7 +220,6 @@ const options: Options<Required<Args>> = {
214220
description: "Force to open a file or folder in an already opened window.",
215221
},
216222

217-
locale: { type: "string" },
218223
log: { type: LogLevel },
219224
verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." },
220225

@@ -271,12 +276,16 @@ export function splitOnFirstEquals(str: string): string[] {
271276
return split
272277
}
273278

279+
/**
280+
* Parse arguments into UserProvidedArgs. This should not go beyond checking
281+
* that arguments are valid types and have values when required.
282+
*/
274283
export const parse = (
275284
argv: string[],
276285
opts?: {
277286
configFile?: string
278287
},
279-
): Args => {
288+
): UserProvidedArgs => {
280289
const error = (msg: string): Error => {
281290
if (opts?.configFile) {
282291
msg = `error reading ${opts.configFile}: ${msg}`
@@ -285,7 +294,7 @@ export const parse = (
285294
return new Error(msg)
286295
}
287296

288-
const args: Args = { _: [] }
297+
const args: UserProvidedArgs = {}
289298
let ended = false
290299

291300
for (let i = 0; i < argv.length; ++i) {
@@ -299,17 +308,17 @@ export const parse = (
299308

300309
// Options start with a dash and require a value if non-boolean.
301310
if (!ended && arg.startsWith("-")) {
302-
let key: keyof Args | undefined
311+
let key: keyof UserProvidedArgs | undefined
303312
let value: string | undefined
304313
if (arg.startsWith("--")) {
305314
const split = splitOnFirstEquals(arg.replace(/^--/, ""))
306-
key = split[0] as keyof Args
315+
key = split[0] as keyof UserProvidedArgs
307316
value = split[1]
308317
} else {
309318
const short = arg.replace(/^-/, "")
310319
const pair = Object.entries(options).find(([, v]) => v.short === short)
311320
if (pair) {
312-
key = pair[0] as keyof Args
321+
key = pair[0] as keyof UserProvidedArgs
313322
}
314323
}
315324

@@ -384,6 +393,10 @@ export const parse = (
384393
}
385394

386395
// Everything else goes into _.
396+
if (typeof args._ === "undefined") {
397+
args._ = []
398+
}
399+
387400
args._.push(arg)
388401
}
389402

@@ -397,6 +410,11 @@ export const parse = (
397410
return args
398411
}
399412

413+
/**
414+
* User-provided arguments with defaults. The distinction between user-provided
415+
* args and defaulted args exists so we can tell the difference between end
416+
* values and what the user actually provided on the command line.
417+
*/
400418
export interface DefaultedArgs extends ConfigArgs {
401419
auth: AuthType
402420
cert?: {
@@ -410,14 +428,18 @@ export interface DefaultedArgs extends ConfigArgs {
410428
usingEnvHashedPassword: boolean
411429
"extensions-dir": string
412430
"user-data-dir": string
431+
/* Positional arguments. */
432+
_: []
433+
folder: string
434+
workspace: string
413435
}
414436

415437
/**
416438
* Take CLI and config arguments (optional) and return a single set of arguments
417439
* with the defaults set. Arguments from the CLI are prioritized over config
418440
* arguments.
419441
*/
420-
export async function setDefaults(cliArgs: Args, configArgs?: ConfigArgs): Promise<DefaultedArgs> {
442+
export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: ConfigArgs): Promise<DefaultedArgs> {
421443
const args = Object.assign({}, configArgs || {}, cliArgs)
422444

423445
if (!args["user-data-dir"]) {
@@ -472,7 +494,7 @@ export async function setDefaults(cliArgs: Args, configArgs?: ConfigArgs): Promi
472494
args.auth = AuthType.Password
473495
}
474496

475-
const addr = bindAddrFromAllSources(configArgs || { _: [] }, cliArgs)
497+
const addr = bindAddrFromAllSources(configArgs || {}, cliArgs)
476498
args.host = addr.host
477499
args.port = addr.port
478500

@@ -513,8 +535,29 @@ export async function setDefaults(cliArgs: Args, configArgs?: ConfigArgs): Promi
513535
const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
514536
args["proxy-domain"] = Array.from(proxyDomains)
515537

538+
if (typeof args._ === "undefined") {
539+
args._ = []
540+
}
541+
542+
let workspace = ""
543+
let folder = ""
544+
if (args._.length) {
545+
const lastEntry = path.resolve(process.cwd(), args._[args._.length - 1])
546+
const entryIsFile = await isFile(lastEntry)
547+
548+
if (entryIsFile && path.extname(lastEntry) === ".code-workspace") {
549+
workspace = lastEntry
550+
args._.pop()
551+
} else if (!entryIsFile) {
552+
folder = lastEntry
553+
args._.pop()
554+
}
555+
}
556+
516557
return {
517558
...args,
559+
workspace,
560+
folder,
518561
usingEnvPassword,
519562
usingEnvHashedPassword,
520563
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
@@ -539,7 +582,7 @@ cert: false
539582
`
540583
}
541584

542-
interface ConfigArgs extends Args {
585+
interface ConfigArgs extends UserProvidedArgs {
543586
config: string
544587
}
545588

@@ -581,7 +624,7 @@ export async function readConfigFile(configPath?: string): Promise<ConfigArgs> {
581624
*/
582625
export function parseConfigFile(configFile: string, configPath: string): ConfigArgs {
583626
if (!configFile) {
584-
return { _: [], config: configPath }
627+
return { config: configPath }
585628
}
586629

587630
const config = yaml.load(configFile, {
@@ -628,7 +671,7 @@ interface Addr {
628671
* This function creates the bind address
629672
* using the CLI args.
630673
*/
631-
export function bindAddrFromArgs(addr: Addr, args: Args): Addr {
674+
export function bindAddrFromArgs(addr: Addr, args: UserProvidedArgs): Addr {
632675
addr = { ...addr }
633676
if (args["bind-addr"]) {
634677
addr = parseBindAddr(args["bind-addr"])
@@ -646,7 +689,7 @@ export function bindAddrFromArgs(addr: Addr, args: Args): Addr {
646689
return addr
647690
}
648691

649-
function bindAddrFromAllSources(...argsConfig: Args[]): Addr {
692+
function bindAddrFromAllSources(...argsConfig: UserProvidedArgs[]): Addr {
650693
let addr: Addr = {
651694
host: "localhost",
652695
port: 8080,
@@ -683,30 +726,34 @@ export async function readSocketPath(path: string): Promise<string | undefined>
683726
/**
684727
* Determine if it looks like the user is trying to open a file or folder in an
685728
* existing instance. The arguments here should be the arguments the user
686-
* explicitly passed on the command line, not defaults or the configuration.
729+
* explicitly passed on the command line, *NOT DEFAULTS* or the configuration.
687730
*/
688-
export const shouldOpenInExistingInstance = async (args: Args): Promise<string | undefined> => {
731+
export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Promise<string | undefined> => {
689732
// Always use the existing instance if we're running from VS Code's terminal.
690733
if (process.env.VSCODE_IPC_HOOK_CLI) {
734+
logger.debug("Found VSCODE_IPC_HOOK_CLI")
691735
return process.env.VSCODE_IPC_HOOK_CLI
692736
}
693737

694738
// If these flags are set then assume the user is trying to open in an
695739
// existing instance since these flags have no effect otherwise.
696740
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
697-
return args[cur as keyof Args] ? prev + 1 : prev
741+
return args[cur as keyof UserProvidedArgs] ? prev + 1 : prev
698742
}, 0)
699743
if (openInFlagCount > 0) {
744+
logger.debug("Found --reuse-window or --new-window")
700745
return readSocketPath(DEFAULT_SOCKET_PATH)
701746
}
702747

703748
// It's possible the user is trying to spawn another instance of code-server.
704-
// Check if any unrelated flags are set (check against one because `_` always
705-
// exists), that a file or directory was passed, and that the socket is
706-
// active.
707-
if (Object.keys(args).length === 1 && args._.length > 0) {
749+
// 1. Check if any unrelated flags are set (this should only run when
750+
// code-server is invoked exactly like this: `code-server my-file`).
751+
// 2. That a file or directory was passed.
752+
// 3. That the socket is active.
753+
if (Object.keys(args).length === 1 && typeof args._ !== "undefined" && args._.length > 0) {
708754
const socketPath = await readSocketPath(DEFAULT_SOCKET_PATH)
709755
if (socketPath && (await canConnect(socketPath))) {
756+
logger.debug("Found existing code-server socket")
710757
return socketPath
711758
}
712759
}

src/node/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function getPackageJson(relativePath: string): JSONSchemaForNPMPackageJso
1818

1919
const pkg = getPackageJson("../../package.json")
2020

21+
export const pkgName = pkg.name || "code-server"
2122
export const version = pkg.version || "development"
2223
export const commit = pkg.commit || "development"
2324
export const rootPath = path.resolve(__dirname, "../..")

0 commit comments

Comments
 (0)