Archived
1
0

chore: move to patches (#4997)

* Move integration types into code-server

This will be easier to maintain than to have it as a patch.

* Disable connection token

Using a flag means we will not need to patch it out.  I think this is
new from 1.64?

* Add product.json to build process

This way we do not have to patch it.

* Ship with remote agent package.json

Instead of the root one.  This contains fewer dependencies.

* Let Code handle errors

This way we will not have to patch Code to make this work and I think it
makes sense to let Code handle the request.

If we do want to handle errors we can do it cleanly by patching their
error handler to throw instead.

* Move manifest override into code-server

This way we will not have to patch it.

* Move to patches

- Switch submodule to track upstream
- Add quilt to the process
- Add patches

The node-* ignore was ignoring one of the diffs so I removed it.  This
was added when we were curling Node as node-v{version}-darwin-x64 for
the macOS build but this no longer happens (we use the Node action to
install a specific version now so we just use the system-wide Node).

* Use pre-packaged Code
This commit is contained in:
Asher
2022-03-22 15:07:14 -05:00
committed by GitHub
parent be727871f6
commit a1af9e2a56
37 changed files with 2240 additions and 205 deletions

View File

@ -0,0 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
self.addEventListener("install", () => {
console.debug("[Service Worker] installed")
})
self.addEventListener("activate", (event: any) => {
event.waitUntil((self as any).clients.claim())
console.debug("[Service Worker] activated")
})
self.addEventListener("fetch", () => {
// Without this event handler we won't be recognized as a PWA.
})

View File

@ -31,13 +31,33 @@ export enum LogLevel {
export class OptionalString extends Optional<string> {}
/**
* Code flags provided by the user.
*/
export interface UserProvidedCodeArgs {
"disable-telemetry"?: boolean
force?: boolean
"user-data-dir"?: string
"enable-proposed-api"?: string[]
"extensions-dir"?: string
"builtin-extensions-dir"?: string
"install-extension"?: string[]
"uninstall-extension"?: string[]
"list-extensions"?: boolean
"locate-extension"?: string[]
"show-versions"?: boolean
category?: string
"github-auth"?: string
"disable-update-check"?: boolean
}
/**
* Arguments that the user explicitly provided on the command line. All
* arguments must be optional.
*
* For arguments with defaults see DefaultedArgs.
*/
export interface UserProvidedArgs {
export interface UserProvidedArgs extends UserProvidedCodeArgs {
config?: string
auth?: AuthType
password?: string
@ -45,7 +65,6 @@ export interface UserProvidedArgs {
cert?: OptionalString
"cert-host"?: string
"cert-key"?: string
"disable-update-check"?: boolean
enable?: string[]
help?: boolean
host?: string
@ -66,21 +85,6 @@ export interface UserProvidedArgs {
verbose?: boolean
/* Positional arguments. */
_?: string[]
// VS Code flags.
"disable-telemetry"?: boolean
force?: boolean
"user-data-dir"?: string
"enable-proposed-api"?: string[]
"extensions-dir"?: string
"builtin-extensions-dir"?: string
"install-extension"?: string[]
"uninstall-extension"?: string[]
"list-extensions"?: boolean
"locate-extension"?: string[]
"show-versions"?: boolean
category?: string
"github-auth"?: string
}
interface Option<T> {
@ -761,14 +765,37 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
return undefined
}
/**
* Arguments for running Code's server.
*
* A subset of ../../lib/vscode/src/vs/server/node/serverEnvironmentService.ts:90
*/
export interface CodeArgs extends UserProvidedCodeArgs {
"accept-server-license-terms"?: boolean
"connection-token"?: string
help: boolean
port?: string
version: boolean
"without-connection-token"?: boolean
"without-browser-env-var"?: boolean
compatibility: string
}
/**
* Types for ../../lib/vscode/src/vs/server/node/server.main.ts:65.
*/
export type SpawnCodeCli = (args: CodeArgs) => Promise<void>
/**
* Convert our arguments to VS Code server arguments.
*/
export const toVsCodeArgs = async (args: DefaultedArgs): Promise<CodeServerLib.ServerParsedArgs> => {
export const toCodeArgs = async (args: DefaultedArgs): Promise<CodeArgs> => {
return {
"connection-token": "0000",
...args,
"accept-server-license-terms": true,
// This seems to be used to make the connection token flags optional (when
// set to 1.63) but we have always included them.
compatibility: "1.64",
/** Type casting. */
help: !!args.help,
version: !!args.version,

View File

@ -1,7 +1,7 @@
import { logger } from "@coder/logger"
import { optionDescriptions, parse, readConfigFile, setDefaults, shouldOpenInExistingInstance } from "./cli"
import { getVersionString, getVersionJsonString } from "./constants"
import { openInExistingInstance, runCodeServer, runVsCodeCli, shouldSpawnCliProcess } from "./main"
import { openInExistingInstance, runCodeServer, runCodeCli, shouldSpawnCliProcess } from "./main"
import { isChild, wrapper } from "./wrapper"
async function entry(): Promise<void> {
@ -48,7 +48,7 @@ async function entry(): Promise<void> {
if (shouldSpawnCliProcess(args)) {
logger.debug("Found VS Code arguments; spawning VS Code CLI")
return runVsCodeCli(args)
return runCodeCli(args)
}
const socketPath = await shouldOpenInExistingInstance(cliArgs)

View File

@ -5,7 +5,7 @@ import path from "path"
import { Disposable } from "../common/emitter"
import { plural } from "../common/util"
import { createApp, ensureAddress } from "./app"
import { AuthType, DefaultedArgs, Feature, toVsCodeArgs, UserProvidedArgs } from "./cli"
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
import { coderCloudBind } from "./coder_cloud"
import { commit, version } from "./constants"
import { register } from "./routes"
@ -24,27 +24,46 @@ export const shouldSpawnCliProcess = (args: UserProvidedArgs): boolean => {
}
/**
* This is useful when an CLI arg should be passed to VS Code directly,
* such as when managing extensions.
* @deprecated This should be removed when code-server merges with lib/vscode.
* 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 const runVsCodeCli = async (args: DefaultedArgs): Promise<void> => {
logger.debug("Running VS Code CLI")
export interface OpenCommandPipeArgs {
type: "open"
fileURIs?: string[]
folderURIs: string[]
forceNewWindow?: boolean
diffMode?: boolean
addMode?: boolean
gotoLineMode?: boolean
forceReuseWindow?: boolean
waitMarkerFilePath?: string
}
// See ../../lib/vscode/src/vs/server/node/server.main.js.
const spawnCli = await loadAMDModule<CodeServerLib.SpawnCli>("vs/server/node/server.main", "spawnCli")
/**
* Run Code's CLI for things like managing extensions.
*/
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
logger.debug("Running Code CLI")
// See ../../lib/vscode/src/vs/server/node/server.main.ts:65.
const spawnCli = await loadAMDModule<SpawnCodeCli>("vs/server/node/server.main", "spawnCli")
try {
await spawnCli(await toVsCodeArgs(args))
await spawnCli(await toCodeArgs(args))
} catch (error: any) {
logger.error("Got error from VS Code", error)
logger.error("Got error from Code", error)
}
process.exit(0)
}
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
const pipeArgs: CodeServerLib.OpenCommandPipeArgs & { fileURIs: string[] } = {
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
type: "open",
folderURIs: [],
fileURIs: [],
@ -76,12 +95,12 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
},
(response) => {
response.on("data", (message) => {
logger.debug("got message from VS Code", field("message", message.toString()))
logger.debug("got message from Code", field("message", message.toString()))
})
},
)
vscode.on("error", (error: unknown) => {
logger.error("got error from VS Code", field("error", error))
logger.error("got error from Code", field("error", error))
})
vscode.write(JSON.stringify(pipeArgs))
vscode.end()

View File

@ -129,6 +129,14 @@ export const register = async (app: App, args: DefaultedArgs): Promise<Disposabl
express.static(rootPath, {
cacheControl: commit !== "development",
fallthrough: false,
setHeaders: (res, path, stat) => {
// The service worker is served from a sub-path on the static route so
// this is required to allow it to register a higher scope (by default
// the browser only allows it to register from its own path or lower).
if (path.endsWith("/serviceWorker.js")) {
res.setHeader("Service-Worker-Allowed", "/")
}
},
}),
)

View File

@ -1,19 +1,34 @@
import { logger } from "@coder/logger"
import * as express from "express"
import * as http from "http"
import * as net from "net"
import * as path from "path"
import { WebsocketRequest } from "../../../typings/pluginapi"
import { logError } from "../../common/util"
import { toVsCodeArgs } from "../cli"
import { CodeArgs, toCodeArgs } from "../cli"
import { isDevMode } from "../constants"
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
import { authenticated, ensureAuthenticated, redirect, replaceTemplates, self } from "../http"
import { SocketProxyProvider } from "../socket"
import { isFile, loadAMDModule } from "../util"
import { Router as WsRouter } from "../wsRouter"
import { errorHandler } from "./errors"
/**
* This is the API of Code's web client server. code-server delegates requests
* to Code here.
*/
export interface IServerAPI {
handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>
handleUpgrade(req: http.IncomingMessage, socket: net.Socket): void
handleServerError(err: Error): void
dispose(): void
}
// Types for ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
export type CreateServer = (address: string | net.AddressInfo | null, args: CodeArgs) => Promise<IServerAPI>
export class CodeServerRouteWrapper {
/** Assigned in `ensureCodeServerLoaded` */
private _codeServerMain!: CodeServerLib.IServerAPI
private _codeServerMain!: IServerAPI
private _wsRouterWrapper = WsRouter()
private _socketProxyProvider = new SocketProxyProvider()
public router = express.Router()
@ -24,6 +39,32 @@ export class CodeServerRouteWrapper {
//#region Route Handlers
private manifest: express.Handler = async (req, res, next) => {
res.writeHead(200, { "Content-Type": "application/manifest+json" })
return res.end(
replaceTemplates(
req,
JSON.stringify(
{
name: "code-server",
short_name: "code-server",
start_url: ".",
display: "fullscreen",
description: "Run Code on a remote server.",
icons: [192, 512].map((size) => ({
src: `{{BASE}}/_static/src/browser/media/pwa-icon-${size}.png`,
type: "image/png",
sizes: `${size}x${size}`,
})),
},
null,
2,
),
),
)
}
private $root: express.Handler = async (req, res, next) => {
const isAuthenticated = await authenticated(req)
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
@ -81,17 +122,6 @@ export class CodeServerRouteWrapper {
}
private $proxyRequest: express.Handler = async (req, res, next) => {
// We allow certain errors to propagate so that other routers may handle requests
// outside VS Code
const requestErrorHandler = (error: any) => {
if (error instanceof Error && ["EntryNotFound", "FileNotFound", "HttpError"].includes(error.message)) {
next()
}
errorHandler(error, req, res, next)
}
req.once("error", requestErrorHandler)
this._codeServerMain.handleRequest(req, res)
}
@ -117,15 +147,14 @@ export class CodeServerRouteWrapper {
const { args } = req
/**
* @file ../../../lib/vscode/src/vs/server/node/server.main.js
*/
const createVSServer = await loadAMDModule<CodeServerLib.CreateServer>("vs/server/node/server.main", "createServer")
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")
try {
this._codeServerMain = await createVSServer(null, {
...(await toVsCodeArgs(args)),
...(await toCodeArgs(args)),
// TODO: Make the browser helper script work.
"without-connection-token": true,
"without-browser-env-var": true,
})
} catch (error) {
@ -141,6 +170,7 @@ export class CodeServerRouteWrapper {
constructor() {
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
this.router.get(/manifest.json$/, this.manifest)
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
this._wsRouterWrapper.ws("/", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
}