mirror of
https://github.com/coder/code-server.git
synced 2025-08-02 22:58:50 +08:00

* Update VS Code to 1.92.2 * Use server-main.js to load VS Code It looks like the bootstrap files are now bundled so we can no longer require them. We could make them included again, but maybe it is better to go through the main entrypoint anyway because it includes some nls stuff which is maybe necessary. This also fixes what looks like a bug where we could create two servers if two requests came in. I am not sure what the practical consequences of that would be, but it will no longer do that. * Drop es2020 patch Unfortunately, VS Code will not load with this. It seems to be because `this` is being used in static properties, and it becomes `void 0` for some reason under the es2020 target. For example: static PREFIX_BY_CATEGORY = `${this.PREFIX}${this.SCOPE_PREFIX}`; becomes AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY = `${(void 0).PREFIX}${(void 0).SCOPE_PREFIX}`; Which, obviously, will not work. Older versions of Safari (and maybe other browsers) are likely affected. * Fix display language * Update Playwright I think maybe because of the dropped es2020 patch that Webkit is now failing because it is too old. * Do not wait for networkidle in e2e tests I am not sure what is going on but some tests on Webkit are timing out and it seems the page is loaded but something is still trying to download. Not good, but for now try to at least get the tests passing.
204 lines
6.7 KiB
TypeScript
204 lines
6.7 KiB
TypeScript
import { field, logger } from "@coder/logger"
|
|
import http from "http"
|
|
import * as path from "path"
|
|
import { Disposable } from "../common/emitter"
|
|
import { plural } from "../common/util"
|
|
import { createApp, ensureAddress } from "./app"
|
|
import { AuthType, DefaultedArgs, Feature, toCodeArgs, UserProvidedArgs } from "./cli"
|
|
import { commit, version, vsRootPath } from "./constants"
|
|
import { register } from "./routes"
|
|
import { VSCodeModule } from "./routes/vscode"
|
|
import { isDirectory, open } from "./util"
|
|
|
|
/**
|
|
* Return true if the user passed an extension-related VS Code flag.
|
|
*/
|
|
export const shouldSpawnCliProcess = (args: UserProvidedArgs): boolean => {
|
|
return (
|
|
!!args["list-extensions"] ||
|
|
!!args["install-extension"] ||
|
|
!!args["uninstall-extension"] ||
|
|
!!args["locate-extension"]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* This is copy of OpenCommandPipeArgs from
|
|
* ../../lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts:15
|
|
*
|
|
* Arguments supported by Code's socket. It can be used to perform actions from
|
|
* the CLI in a running instance of Code (for example to open a file).
|
|
*
|
|
* TODO: Can we import this (and other types) directly?
|
|
*/
|
|
export interface OpenCommandPipeArgs {
|
|
type: "open"
|
|
fileURIs?: string[]
|
|
folderURIs: string[]
|
|
forceNewWindow?: boolean
|
|
diffMode?: boolean
|
|
addMode?: boolean
|
|
gotoLineMode?: boolean
|
|
forceReuseWindow?: boolean
|
|
waitMarkerFilePath?: string
|
|
}
|
|
|
|
/**
|
|
* Run Code's CLI for things like managing extensions.
|
|
*/
|
|
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
|
logger.debug("Running Code CLI")
|
|
try {
|
|
const mod = require(path.join(vsRootPath, "out/server-main")) as VSCodeModule
|
|
const serverModule = await mod.loadCodeWithNls()
|
|
await serverModule.spawnCli(await toCodeArgs(args))
|
|
// Rather than have the caller handle errors and exit, spawnCli will exit
|
|
// itself. Additionally, it does this on a timeout set to 0. So, try
|
|
// waiting for VS Code to exit before giving up and doing it ourselves.
|
|
await new Promise((r) => setTimeout(r, 1000))
|
|
logger.warn("Code never exited")
|
|
process.exit(0)
|
|
} catch (error: any) {
|
|
// spawnCli catches all errors, but just in case that changes.
|
|
logger.error("Got error from Code", error)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
|
|
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
|
|
type: "open",
|
|
folderURIs: [],
|
|
fileURIs: [],
|
|
forceReuseWindow: args["reuse-window"],
|
|
forceNewWindow: args["new-window"],
|
|
gotoLineMode: true,
|
|
}
|
|
for (let i = 0; i < args._.length; i++) {
|
|
const fp = args._[i]
|
|
if (await isDirectory(fp)) {
|
|
pipeArgs.folderURIs.push(fp)
|
|
} else {
|
|
pipeArgs.fileURIs.push(fp)
|
|
}
|
|
}
|
|
if (pipeArgs.forceNewWindow && pipeArgs.fileURIs.length > 0) {
|
|
logger.error("--new-window can only be used with folder paths")
|
|
process.exit(1)
|
|
}
|
|
if (pipeArgs.folderURIs.length === 0 && pipeArgs.fileURIs.length === 0) {
|
|
logger.error("Please specify at least one file or folder")
|
|
process.exit(1)
|
|
}
|
|
const vscode = http.request(
|
|
{
|
|
path: "/",
|
|
method: "POST",
|
|
socketPath,
|
|
},
|
|
(response) => {
|
|
response.on("data", (message) => {
|
|
logger.debug("got message from Code", field("message", message.toString()))
|
|
})
|
|
},
|
|
)
|
|
vscode.on("error", (error: unknown) => {
|
|
logger.error("got error from Code", field("error", error))
|
|
})
|
|
vscode.write(JSON.stringify(pipeArgs))
|
|
vscode.end()
|
|
}
|
|
|
|
export const runCodeServer = async (
|
|
args: DefaultedArgs,
|
|
): Promise<{ dispose: Disposable["dispose"]; server: http.Server }> => {
|
|
logger.info(`code-server ${version} ${commit}`)
|
|
|
|
logger.info(`Using user-data-dir ${args["user-data-dir"]}`)
|
|
logger.debug(`Using extensions-dir ${args["extensions-dir"]}`)
|
|
|
|
if (args.auth === AuthType.Password && !args.password && !args["hashed-password"]) {
|
|
throw new Error(
|
|
"Please pass in a password via the config file or environment variable ($PASSWORD or $HASHED_PASSWORD)",
|
|
)
|
|
}
|
|
|
|
const app = await createApp(args)
|
|
const protocol = args.cert ? "https" : "http"
|
|
const serverAddress = ensureAddress(app.server, protocol)
|
|
const disposeRoutes = await register(app, args)
|
|
|
|
logger.info(`Using config file ${args.config}`)
|
|
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
|
|
if (args.auth === AuthType.Password) {
|
|
logger.info(" - Authentication is enabled")
|
|
if (args.usingEnvPassword) {
|
|
logger.info(" - Using password from $PASSWORD")
|
|
} else if (args.usingEnvHashedPassword) {
|
|
logger.info(" - Using password from $HASHED_PASSWORD")
|
|
} else {
|
|
logger.info(` - Using password from ${args.config}`)
|
|
}
|
|
} else {
|
|
logger.info(" - Authentication is disabled")
|
|
}
|
|
|
|
if (args.cert) {
|
|
logger.info(` - Using certificate for HTTPS: ${args.cert.value}`)
|
|
} else {
|
|
logger.info(" - Not serving HTTPS")
|
|
}
|
|
|
|
if (args["disable-proxy"]) {
|
|
logger.info(" - Proxy disabled")
|
|
} else if (args["proxy-domain"].length > 0) {
|
|
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
|
|
args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`))
|
|
}
|
|
if (process.env.VSCODE_PROXY_URI) {
|
|
logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`)
|
|
}
|
|
|
|
const sessionServerAddress = app.editorSessionManagerServer.address()
|
|
if (sessionServerAddress) {
|
|
logger.info(`Session server listening on ${sessionServerAddress.toString()}`)
|
|
}
|
|
|
|
if (process.env.EXTENSIONS_GALLERY) {
|
|
logger.info("Using custom extensions gallery")
|
|
logger.debug(` - ${process.env.EXTENSIONS_GALLERY}`)
|
|
}
|
|
|
|
if (args.enable && args.enable.length > 0) {
|
|
logger.info("Enabling the following experimental features:")
|
|
args.enable.forEach((feature) => {
|
|
if (Object.values(Feature).includes(feature as Feature)) {
|
|
logger.info(` - "${feature}"`)
|
|
} else {
|
|
logger.error(` X "${feature}" (unknown feature)`)
|
|
}
|
|
})
|
|
// TODO: Could be nice to add wrapping to the logger?
|
|
logger.info(
|
|
" The code-server project does not provide stability guarantees or commit to fixing bugs relating to these experimental features. When filing bug reports, please ensure that you can reproduce the bug with all experimental features turned off.",
|
|
)
|
|
}
|
|
|
|
if (args.open) {
|
|
try {
|
|
await open(serverAddress)
|
|
logger.info(`Opened ${serverAddress}`)
|
|
} catch (error) {
|
|
logger.error("Failed to open", field("address", serverAddress.toString()), field("error", error))
|
|
}
|
|
}
|
|
|
|
return {
|
|
server: app.server,
|
|
dispose: async () => {
|
|
disposeRoutes()
|
|
await app.dispose()
|
|
},
|
|
}
|
|
}
|