diff --git a/.eslintrc.yaml b/.eslintrc.yaml index fbb9ac0e9..58112a671 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -1,7 +1,7 @@ parser: "@typescript-eslint/parser" env: browser: true - es6: true # Map, etc. + es6: true # Map, etc. mocha: true node: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5279a3ee..4a9c0579c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,13 @@ name: ci -on: [push, pull_request] +on: [push, pull_request, release] jobs: - lint: + fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - name: get yarn cache directory path + - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - uses: actions/cache@v1 @@ -18,7 +18,49 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: yarn lint + - name: Run yarn fmt + uses: ./ci/image + with: + args: yarn && yarn fmt + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v1 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Run yarn lint uses: ./ci/image with: args: yarn && yarn lint + + release-amd64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v1 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Run release.sh + uses: ./ci/image + with: + args: yarn && yarn vscode && ./ci/release.sh diff --git a/README.md b/README.md index b2a396a58..1abcd9387 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ reset VS Code then run `yarn patch:apply`. ## Security ### Authentication + By default `code-server` enables password authentication using a randomly generated password. You can set the `PASSWORD` environment variable to use your own instead or use `--auth none` to disable password authentication. @@ -82,6 +83,7 @@ Do not expose `code-server` to the open internet without some form of authentication. ### Encrypting traffic with HTTPS + If you aren't doing SSL termination elsewhere you can directly give `code-server` a certificate with `code-server --cert` followed by the path to your certificate. Additionally, you can use certificate keys with `--cert-key` diff --git a/ci/build.ts b/ci/build.ts index 937dc8cb7..470791619 100644 --- a/ci/build.ts +++ b/ci/build.ts @@ -26,7 +26,7 @@ class Builder { this.ensureArgument("rootPath", this.rootPath) this.codeServerVersion = this.ensureArgument( "codeServerVersion", - process.env.VERSION || require(path.join(this.rootPath, "package.json")).version + process.env.VERSION || require(path.join(this.rootPath, "package.json")).version, ) } @@ -208,7 +208,7 @@ class Builder { await Promise.all([ fs.move( path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`), - path.join(vscodeBuildPath, "out") + path.join(vscodeBuildPath, "out"), ), fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")), ]) @@ -225,7 +225,7 @@ class Builder { return Promise.all( ["node_modules", "package.json", "yarn.lock"].map((fileName) => { return fs.copy(path.join(sourcePath, fileName), path.join(buildPath, fileName)) - }) + }), ) }) @@ -240,8 +240,8 @@ class Builder { ...merge, }, null, - 2 - ) + 2, + ), ) }) @@ -290,7 +290,7 @@ class Builder { await fs.copyFile(path.join(this.vscodeSourcePath, "LICENSE.txt"), path.join(archivePath, "LICENSE.txt")) await fs.copyFile( path.join(this.vscodeSourcePath, "ThirdPartyNotices.txt"), - path.join(archivePath, "ThirdPartyNotices.txt") + path.join(archivePath, "ThirdPartyNotices.txt"), ) if ((await this.target()) === "darwin") { diff --git a/ci/fmt.sh b/ci/fmt.sh index 9f9c23155..762dbdd27 100755 --- a/ci/fmt.sh +++ b/ci/fmt.sh @@ -3,15 +3,35 @@ set -euo pipefail main() { - shfmt -i 2 -w -s -sr $$(git ls-files "*.sh") - prettier --write --loglevel=warn $$(git ls-files "*.js" "*.ts" "*.tsx" "*.html" "*.json" "*.css" "*.md" "*.toml" "*.yaml" "*.yml") - if [[ "$CI" != "" && $$(git ls-files --other --modified --exclude-standard) != "" ]]; then - echo "Files need generation or are formatted incorrectly:" - git -c color.ui=always status | grep --color=no '\[31m' - echo "Please run the following locally:" - echo " make fmt" - exit 1 - fi + if [[ ${CI-} ]]; then + cd "$(dirname "$0")/.." + ./ci/vscode.sh + fi + + shfmt -i 2 -w -s -sr $(git ls-files "*.sh") + + local prettierExts + prettierExts=( + "*.js" + "*.ts" + "*.tsx" + "*.html" + "*.json" + "*.css" + "*.md" + "*.toml" + "*.yaml" + "*.yml" + ) + prettier --write --loglevel=warn $(git ls-files "${prettierExts[@]}") + + if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then + echo "Files need generation or are formatted incorrectly:" + git -c color.ui=always status | grep --color=no '\[31m' + echo "Please run the following locally:" + echo " yarn fmt" + exit 1 + fi } main "$@" diff --git a/ci/image/Dockerfile b/ci/image/Dockerfile index 6bef9e512..0f4977b30 100644 --- a/ci/image/Dockerfile +++ b/ci/image/Dockerfile @@ -9,4 +9,7 @@ RUN curl -L https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_li COPY entrypoint.sh /bin/entrypoint.sh +ENV PAGER=cat +ENV CI=true + ENTRYPOINT ["dumb-init", "/bin/entrypoint.sh"] diff --git a/ci/lint.sh b/ci/lint.sh index 5b60c894b..bae79a1fe 100755 --- a/ci/lint.sh +++ b/ci/lint.sh @@ -3,8 +3,14 @@ set -euo pipefail main() { - eslint --max-warnings=0 --fix $$(git ls-files "*.ts" "*.tsx" "*.js") - stylelint --fix $$(git ls-files "*.css") + if [[ ${CI-} ]]; then + cd "$(dirname "$0")/.." + ./ci/vscode.sh + fi + + eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js") + stylelint --fix $(git ls-files "*.css") + tsc --noEmit } main "$@" diff --git a/ci/release.bash b/ci/release.sh similarity index 79% rename from ci/release.bash rename to ci/release.sh index c288a10ad..e84561b91 100755 --- a/ci/release.bash +++ b/ci/release.sh @@ -8,7 +8,7 @@ function main() { cd "$(dirname "${0}")/.." local code_server_version=${VERSION:-${TRAVIS_TAG:-${DRONE_TAG:-}}} - if [[ -z $code_server_version ]] ; then + if [[ -z $code_server_version ]]; then code_server_version=$(grep version ./package.json | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[:space:]') fi export VERSION=$code_server_version @@ -17,33 +17,33 @@ function main() { export YARN_CACHE_FOLDER # Always minify and package on tags since that's when releases are pushed. - if [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]] ; then + if [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]]; then export MINIFY="true" export PACKAGE="true" fi yarn build yarn binary - if [[ -n ${PACKAGE:-} ]] ; then + if [[ -n ${PACKAGE:-} ]]; then yarn package fi cd binaries - if [[ -n ${STRIP_BIN_TARGET:-} ]] ; then + if [[ -n ${STRIP_BIN_TARGET:-} ]]; then # In this case provide plainly named binaries. - for binary in code-server* ; do + for binary in code-server*; do echo "Moving $binary to code-server" mv "$binary" code-server done - elif [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]] ; then + elif [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]]; then # Prepare directory for uploading binaries on release. - for binary in code-server* ; do + for binary in code-server*; do mkdir -p "../binary-upload" local prefix="code-server-$code_server_version-" local target="${binary#$prefix}" - if [[ $target == "linux-x86_64" ]] ; then + if [[ $target == "linux-x86_64" ]]; then echo "Copying $binary to ../binary-upload/latest-linux" cp "$binary" "../binary-upload/latest-linux" fi diff --git a/ci/tsconfig.json b/ci/tsconfig.json index d4b886575..5197ce276 100644 --- a/ci/tsconfig.json +++ b/ci/tsconfig.json @@ -1,6 +1,4 @@ { "extends": "../tsconfig.json", - "include": [ - "./**/*.ts" - ] + "include": ["./**/*.ts"] } diff --git a/ci/vscode.sh b/ci/vscode.sh index 4fd5ec780..ffacd8128 100755 --- a/ci/vscode.sh +++ b/ci/vscode.sh @@ -10,7 +10,7 @@ main() { git submodule update --init # If the patch fails to apply, then it's likely already applied - yarn vscode:patch &>/dev/null || true + yarn vscode:patch &> /dev/null || true # Install VS Code dependencies. (cd lib/vscode && yarn) diff --git a/package.json b/package.json index 34a37365f..1c1a3557f 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,9 @@ "vscode": "ci/vscode.sh", "vscode:patch": "cd ./lib/vscode && git apply ../../ci/vscode.patch", "vscode:diff": "cd ./lib/vscode && git diff HEAD > ../../ci/vscode.patch", - "test": "mocha -r ts-node/register ./test/*.test.ts", - "lint": "ci/lint.sh", "fmt": "ci/fmt.sh", - "runner": "cd ./scripts && NODE_OPTIONS=--max_old_space_size=32384 ts-node ./build.ts", "build": "yarn runner build", "watch": "yarn runner watch", diff --git a/src/browser/media/manifest.json b/src/browser/media/manifest.json index 513304e13..c18799260 100644 --- a/src/browser/media/manifest.json +++ b/src/browser/media/manifest.json @@ -5,9 +5,11 @@ "display": "fullscreen", "background-color": "#fff", "description": "Run editors on a remote server.", - "icons": [{ - "src": "./code-server.png", - "sizes": "384x384", - "type": "image/png" - }] + "icons": [ + { + "src": "./code-server.png", + "sizes": "384x384", + "type": "image/png" + } + ] } diff --git a/src/browser/pages/app.html b/src/browser/pages/app.html index ce1b49b1a..6f23348cf 100644 --- a/src/browser/pages/app.html +++ b/src/browser/pages/app.html @@ -2,14 +2,21 @@ - - + + code-server — {{APP_NAME}} - + - - + + diff --git a/src/browser/pages/error.html b/src/browser/pages/error.html index 2bb44c008..6231b5791 100644 --- a/src/browser/pages/error.html +++ b/src/browser/pages/error.html @@ -2,13 +2,20 @@ - - + + code-server {{ERROR_TITLE}} - + - +
@@ -18,7 +25,7 @@ {{ERROR_BODY}}
diff --git a/src/browser/pages/global.css b/src/browser/pages/global.css index e4c031343..b5a77207a 100644 --- a/src/browser/pages/global.css +++ b/src/browser/pages/global.css @@ -10,7 +10,17 @@ body { background: #272727; color: #f4f4f4; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-family: + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Helvetica, + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol"; overflow: hidden; } diff --git a/src/browser/pages/home.html b/src/browser/pages/home.html index abe33a1a5..2bd93ee4a 100644 --- a/src/browser/pages/home.html +++ b/src/browser/pages/home.html @@ -2,14 +2,21 @@ - - + + code-server - + - - + +
diff --git a/src/browser/pages/login.html b/src/browser/pages/login.html index 2cbd86dd4..c9b1a9bee 100644 --- a/src/browser/pages/login.html +++ b/src/browser/pages/login.html @@ -2,13 +2,23 @@ - - + + code-server login - + - +
@@ -22,7 +32,8 @@
+ /> diff --git a/src/browser/pages/vscode.html b/src/browser/pages/vscode.html index b44539571..78c88eeaa 100644 --- a/src/browser/pages/vscode.html +++ b/src/browser/pages/vscode.html @@ -4,28 +4,38 @@ - + - + - + - - - - + + + + - + - + - - + diff --git a/src/browser/socket.ts b/src/browser/socket.ts index 23d969f43..764f8c90f 100644 --- a/src/browser/socket.ts +++ b/src/browser/socket.ts @@ -159,7 +159,7 @@ export class ReconnectingSocket { const socket = new WebSocket( `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}${this.customPath || location.pathname}${ location.search ? `?${location.search}` : "" - }` + }`, ) const reject = (): void => { diff --git a/src/node/app/api.ts b/src/node/app/api.ts index f2d7aa2a7..b0989bb7f 100644 --- a/src/node/app/api.ts +++ b/src/node/app/api.ts @@ -68,7 +68,7 @@ export class ApiHttpProvider extends HttpProvider { route: Route, request: http.IncomingMessage, socket: net.Socket, - head: Buffer + head: Buffer, ): Promise { if (!this.authenticated(request)) { throw new Error("not authenticated") @@ -120,7 +120,7 @@ export class ApiHttpProvider extends HttpProvider { route: Route, request: http.IncomingMessage, socket: net.Socket, - head: Buffer + head: Buffer, ): Promise { const sessionId = route.requestPath.replace(/^\//, "") logger.debug("connecting session", field("sessionId", sessionId)) @@ -149,8 +149,8 @@ export class ApiHttpProvider extends HttpProvider { Buffer.from( JSON.stringify({ protocol: "TODO", - }) - ) + }), + ), ) return true diff --git a/src/node/app/app.ts b/src/node/app/app.ts index ff17b252a..7f112df1e 100644 --- a/src/node/app/app.ts +++ b/src/node/app/app.ts @@ -63,7 +63,7 @@ export class MainHttpProvider extends HttpProvider { base: this.base(route), logLevel: logger.level, }, - (app && app.name) || "" + (app && app.name) || "", ) } @@ -80,11 +80,11 @@ export class MainHttpProvider extends HttpProvider { .replace(/{{APP_LIST:RUNNING}}/g, this.getAppRows(recent.running)) .replace( /{{APP_LIST:EDITORS}}/g, - this.getAppRows(apps.filter((app) => app.categories && app.categories.includes("Editor"))) + this.getAppRows(apps.filter((app) => app.categories && app.categories.includes("Editor"))), ) .replace( /{{APP_LIST:OTHER}}/g, - this.getAppRows(apps.filter((app) => !app.categories || !app.categories.includes("Editor"))) + this.getAppRows(apps.filter((app) => !app.categories || !app.categories.includes("Editor"))), ) return response } diff --git a/src/node/app/login.ts b/src/node/app/login.ts index 207d70848..354a636f1 100644 --- a/src/node/app/login.ts +++ b/src/node/app/login.ts @@ -109,7 +109,7 @@ export class LoginHttpProvider extends HttpProvider { remoteAddress: request.connection.remoteAddress, userAgent: request.headers["user-agent"], timestamp: Math.floor(new Date().getTime() / 1000), - }) + }), ) throw new Error("Incorrect password") diff --git a/src/node/app/vscode.ts b/src/node/app/vscode.ts index ac8c973a3..b4c145d56 100644 --- a/src/node/app/vscode.ts +++ b/src/node/app/vscode.ts @@ -102,7 +102,7 @@ export class VscodeHttpProvider extends HttpProvider { "Upgrade: websocket", "Connection: Upgrade", `Sec-WebSocket-Accept: ${reply}`, - ].join("\r\n") + "\r\n\r\n" + ].join("\r\n") + "\r\n\r\n", ) const vscode = await this._vscode @@ -146,7 +146,7 @@ export class VscodeHttpProvider extends HttpProvider { const response = await this.getUtf8Resource(this.vsRootPath, route.requestPath) response.content = response.content.replace( /{{COMMIT}}/g, - this.workbenchOptions ? this.workbenchOptions.commit : "" + this.workbenchOptions ? this.workbenchOptions.commit : "", ) response.cache = true return response @@ -186,7 +186,7 @@ export class VscodeHttpProvider extends HttpProvider { settings.lastVisited, this.args._ && this.args._.length > 0 ? { url: this.args._[0] } : undefined, ], - remoteAuthority + remoteAuthority, ) const [response, options] = await Promise.all([ await this.getUtf8Resource(this.rootPath, "src/browser/pages/vscode.html"), @@ -229,7 +229,7 @@ export class VscodeHttpProvider extends HttpProvider { */ private async getFirstValidPath( startPaths: Array<{ url?: string | string[]; workspace?: boolean } | undefined>, - remoteAuthority: string + remoteAuthority: string, ): Promise { for (let i = 0; i < startPaths.length; ++i) { const startPath = startPaths[i] diff --git a/src/node/cli.ts b/src/node/cli.ts index 8026209f8..2ddf71df8 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -92,13 +92,13 @@ export const optionDescriptions = (): string[] => { long: k.length > prev.long ? k.length : prev.long, short: v.short && v.short.length > prev.short ? v.short.length : prev.short, }), - { short: 0, long: 0 } + { 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}${typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : ""}` + widths.long - k.length, + )} ${v.description}${typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : ""}`, ) } diff --git a/src/node/entry.ts b/src/node/entry.ts index d0b7c87d2..5be0a061c 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -66,7 +66,7 @@ const main = async (args: Args): Promise => { logger.info( typeof args.cert === "string" ? ` - Using provided certificate${args["cert-key"] ? " and key" : ""} for HTTPS` - : ` - Using generated certificate and key for HTTPS` + : ` - Using generated certificate and key for HTTPS`, ) } else { logger.info(" - Not serving HTTPS") diff --git a/src/node/http.ts b/src/node/http.ts index 9d2783b67..ac50bc2c5 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -142,7 +142,7 @@ export abstract class HttpProvider { route: Route, request: http.IncomingMessage, socket: net.Socket, - head: Buffer + head: Buffer, ): Promise /** @@ -380,7 +380,7 @@ export class HttpServer { cert: this.options.cert && fs.readFileSync(this.options.cert), key: this.options.certKey && fs.readFileSync(this.options.certKey), }, - this.onRequest + this.onRequest, ) } else { this.server = http.createServer(this.onRequest) @@ -420,7 +420,7 @@ export class HttpServer { commit: this.options.commit, password: this.options.password, }, - a1 + a1, ) this.providers.set(`/${endpoint}`, p) return p diff --git a/src/node/util.ts b/src/node/util.ts index e1ae65f20..16b3940d4 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -14,7 +14,7 @@ const getXdgDataDir = (): string => { case "darwin": return path.join( process.env.XDG_DATA_HOME || path.join(os.homedir(), "Library/Application Support"), - "code-server" + "code-server", ) default: return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local/share"), "code-server") diff --git a/src/node/wrapper.ts b/src/node/wrapper.ts index 293e9d39c..e8e9c573e 100644 --- a/src/node/wrapper.ts +++ b/src/node/wrapper.ts @@ -76,7 +76,7 @@ export class IpcMain { `${child ? "wrapper" : "inner process"} ${process.pid} received message from ${ child ? child.pid : this.parentPid }`, - field("message", message) + field("message", message), ) if (message.type === "handshake") { target.removeListener("message", onMessage) @@ -122,7 +122,7 @@ export const ipcMain = (): IpcMain => { _ipcMain = new IpcMain( typeof process.env.CODE_SERVER_PARENT_PID !== "undefined" ? parseInt(process.env.CODE_SERVER_PARENT_PID) - : undefined + : undefined, ) } return _ipcMain diff --git a/test/cli.test.ts b/test/cli.test.ts index c069cc0af..cf2acb347 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -73,7 +73,7 @@ describe("cli", () => { "user-data-dir": path.resolve("bar"), verbose: true, version: true, - } + }, ) }) diff --git a/tsconfig.json b/tsconfig.json index 9510fea9e..2c7db3ea3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,8 +20,5 @@ "rootDir": "./src", "typeRoots": ["./node_modules/@types", "./typings"] }, - "include": [ - "./src/**/*.ts", - "./src/**/*.tsx" - ] + "include": ["./src/**/*.ts", "./src/**/*.tsx"] } diff --git a/typings/httpolyglot/index.d.ts b/typings/httpolyglot/index.d.ts index 7b644a8b3..176e038ce 100644 --- a/typings/httpolyglot/index.d.ts +++ b/typings/httpolyglot/index.d.ts @@ -5,6 +5,6 @@ declare module "httpolyglot" { function createServer(requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): http.Server function createServer( options: https.ServerOptions, - requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void + requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void, ): https.Server }