Merge branch 'master' into feature/helm3

This commit is contained in:
Matthew Beckett 2020-10-13 14:29:43 +01:00 committed by GitHub
commit d7cba30c6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1058 additions and 489 deletions

View File

@ -30,6 +30,7 @@ rules:
eqeqeq: error
import/order:
[error, { alphabetize: { order: "asc" }, groups: [["builtin", "external", "internal"], "parent", "sibling"] }]
no-async-promise-executor: off
settings:
# Does not work with CommonJS unfortunately.

7
.github/ISSUE_TEMPLATE/doc.md vendored Normal file
View File

@ -0,0 +1,7 @@
---
name: Documentation improvement
about: Suggest a documentation improvement
title: ""
labels: "docs"
assignees: ""
---

View File

@ -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

View File

@ -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:

2
.gitignore vendored
View File

@ -11,3 +11,5 @@ release-images/
node_modules
node-*
/plugins
/lib/coder-cloud-agent
.home

1
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "lib/vscode"]
path = lib/vscode
url = https://github.com/microsoft/vscode
ignore = dirty

View File

@ -1,4 +1,4 @@
# code-server
# code-server · [!["GitHub Discussions"](https://img.shields.io/badge/%20GitHub-%20Discussions-gray.svg?longCache=true&logo=github&colorB=purple)](https://github.com/cdr/code-server/discussions) [!["Join us on Slack"](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://cdr.co/join-community) [![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq)
Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and access it in the browser.
@ -11,7 +11,7 @@ Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and a
- Develop on a Linux machine and pick up from any device with a web browser.
- **Server-powered**
- Take advantage of large cloud servers to speed up tests, compilations, downloads, and more.
- Preserve battery life when you're on the go as all intensive tasks runs on your server.
- Preserve battery life when you're on the go as all intensive tasks run on your server.
- Make use of a spare computer you have lying around and turn it into a full development environment.
## Getting Started
@ -52,7 +52,7 @@ See [./doc/CONTRIBUTING.md](./doc/CONTRIBUTING.md).
## Hiring
We ([@cdr](https://github.com/cdr)) are looking for a engineers to help maintain
We ([@cdr](https://github.com/cdr)) are looking for engineers to help maintain
code-server, innovate on open source and streamline dev workflows.
Our main office is in Austin, Texas. Remote is ok as long as

View File

@ -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 \

View File

@ -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 -r "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

View File

@ -6,6 +6,10 @@ set -euo pipefail
# MINIFY controls whether minified vscode is bundled.
MINIFY="${MINIFY-true}"
# KEEP_MODULES controls whether the script cleans all node_modules requiring a yarn install
# to run first.
KEEP_MODULES="${KEEP_MODULES-0}"
main() {
cd "$(dirname "${0}")/../.."
source ./ci/lib.sh
@ -37,6 +41,7 @@ bundle_code_server() {
rsync src/browser/media/ "$RELEASE_PATH/src/browser/media"
mkdir -p "$RELEASE_PATH/src/browser/pages"
rsync src/browser/pages/*.html "$RELEASE_PATH/src/browser/pages"
rsync src/browser/robots.txt "$RELEASE_PATH/src/browser"
# Adds the commit to package.json
jq --slurp '.[0] * .[1]' package.json <(
@ -51,6 +56,12 @@ EOF
) > "$RELEASE_PATH/package.json"
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"
mkdir -p "$RELEASE_PATH/lib"
rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib"
fi
}
bundle_vscode() {
@ -59,7 +70,11 @@ bundle_vscode() {
rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY+-min}/" "$VSCODE_OUT_PATH/out"
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules"
if [ "$KEEP_MODULES" = 0 ]; then
rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules"
else
rsync "$VSCODE_SRC_PATH/node_modules/" "$VSCODE_OUT_PATH/node_modules"
fi
rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions"
rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions"
rsync "$VSCODE_SRC_PATH/extensions/postinstall.js" "$VSCODE_OUT_PATH/extensions"

View File

@ -5,16 +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-*
git clean -Xffd
pushd lib/vscode
git clean -xffd

View File

@ -24,6 +24,10 @@ main() {
;;
esac
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
if ! vscode_yarn; then
echo "You may not have the required dependencies to build the native modules."
echo "Please see https://github.com/cdr/code-server/blob/master/doc/npm.md"
@ -36,6 +40,13 @@ vscode_yarn() {
yarn --production --frozen-lockfile
cd extensions
yarn --production --frozen-lockfile
for ext in */; do
ext="${ext%/}"
echo "extensions/$ext: installing dependencies"
cd "$ext"
yarn --production --frozen-lockfile
cd "$OLDPWD"
done
}
main "$@"

View File

@ -11,7 +11,7 @@ main() {
source ./ci/lib.sh
download_artifact release-packages ./release-packages
local assets=(./release-packages/code-server*"$VERSION"*{.tar.gz,.zip,.deb,.rpm})
local assets=(./release-packages/code-server*"$VERSION"*{.tar.gz,.deb,.rpm})
for i in "${!assets[@]}"; do
assets[$i]="--attach=${assets[$i]}"
done

View File

@ -15,8 +15,7 @@ main() {
./release-standalone/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --install-extension ms-python.python
local installed_extensions
installed_extensions="$(./release-standalone/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --list-extensions 2>&1)"
if [[ $installed_extensions != *"info Using config file ~/.config/code-server/config.yaml
ms-python.python" ]]; then
if [[ $installed_extensions != "ms-python.python" ]]; then
echo "Unexpected output from listing extensions:"
echo "$installed_extensions"
exit 1

View File

@ -6,7 +6,7 @@ main() {
cd ./lib/vscode
git add -A
git diff HEAD > ../../ci/dev/vscode.patch
git diff HEAD --full-index > ../../ci/dev/vscode.patch
}
main "$@"

View File

@ -4,16 +4,22 @@ 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" \
-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)" \
-e CI \
"$(docker_build ./ci/images/debian8)" \
"$(docker_build ./ci/images/"${IMAGE-debian10}")" \
"$@"
}

View File

@ -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 "$@"

View File

@ -1,5 +1,5 @@
diff --git a/.gitignore b/.gitignore
index 0fe46b6eadc..e545e004cef 100644
index 0fe46b6eadc4ccc819fbf342ee1071bb657792b3..e545e004cef31fa5f40ba8df6a2317ea5b69ddb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,6 @@ out-vscode-reh-web-pkg/
@ -12,15 +12,15 @@ index 0fe46b6eadc..e545e004cef 100644
coverage/
diff --git a/.yarnrc b/.yarnrc
deleted file mode 100644
index 135e10442a7..00000000000
index 3c6eccfb102f2084d16395d70d65f05a91b6d47b..0000000000000000000000000000000000000000
--- a/.yarnrc
+++ /dev/null
@@ -1,3 +0,0 @@
-disturl "https://atom.io/download/electron"
-target "7.3.2"
-target "9.2.1"
-runtime "electron"
diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js
index f2ea1bd3701..3f660f99819 100644
index f2ea1bd37010b1eb8a43ce9beaae4a88810f6e2d..3f660f9981921ec465d2b8809a1a5ea5663f4c1f 100644
--- a/build/gulpfile.reh.js
+++ b/build/gulpfile.reh.js
@@ -52,6 +52,7 @@ gulp.task('vscode-reh-web-linux-x64-min', noop);
@ -31,8 +31,34 @@ index f2ea1bd3701..3f660f99819 100644
const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8');
const target = /^target "(.*)"$/m.exec(yarnrc)[1];
return target;
diff --git a/build/lib/extensions.js b/build/lib/extensions.js
index 9cc40c4e1befd38886dc5880581d6f462a38dd3a..34e1fc89a8ac1c273a5cb41f19a088a8ec759d24 100644
--- a/build/lib/extensions.js
+++ b/build/lib/extensions.js
@@ -66,7 +66,7 @@ function fromLocal(extensionPath, forWeb) {
if (isWebPacked) {
input = updateExtensionPackageJSON(input, (data) => {
delete data.scripts;
- delete data.dependencies;
+ // https://github.com/cdr/code-server/pull/2041#issuecomment-685910322
delete data.devDependencies;
if (data.main) {
data.main = data.main.replace('/out/', /dist/);
diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts
index 7e529f17cb84d28d84de4ff64fa9fb8fc48135a9..462d699dc485369c74a4d9fdfefa48ba6124ac3a 100644
--- a/build/lib/extensions.ts
+++ b/build/lib/extensions.ts
@@ -70,7 +70,7 @@ function fromLocal(extensionPath: string, forWeb: boolean): Stream {
if (isWebPacked) {
input = updateExtensionPackageJSON(input, (data: any) => {
delete data.scripts;
- delete data.dependencies;
+ // https://github.com/cdr/code-server/pull/2041#issuecomment-685910322
delete data.devDependencies;
if (data.main) {
data.main = data.main.replace('/out/', /dist/);
diff --git a/build/lib/node.js b/build/lib/node.js
index 403ae3d9657..738ee8cee0e 100644
index 403ae3d9657f823019542e739fc39292db20e4fe..738ee8cee0e79aa239af10e1abefc9e836b8ce33 100644
--- a/build/lib/node.js
+++ b/build/lib/node.js
@@ -5,11 +5,8 @@
@ -49,7 +75,7 @@ index 403ae3d9657..738ee8cee0e 100644
const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node);
console.log(nodePath);
diff --git a/build/lib/node.ts b/build/lib/node.ts
index 64397034461..c53dccf4dc0 100644
index 64397034461b1661f82007c141cbf4c039a3b722..c53dccf4dc0a99122ed96cf10c2eb632bb25059e 100644
--- a/build/lib/node.ts
+++ b/build/lib/node.ts
@@ -4,13 +4,10 @@
@ -70,7 +96,7 @@ index 64397034461..c53dccf4dc0 100644
\ No newline at end of file
+console.log(nodePath);
diff --git a/build/lib/util.js b/build/lib/util.js
index e552a036f89..169e8614b9f 100644
index e552a036f89bd581644459fd5c27fe4ae1379f62..169e8614b9f6a2bd68446144ab7e1ce5c6d49b64 100644
--- a/build/lib/util.js
+++ b/build/lib/util.js
@@ -257,6 +257,7 @@ function streamToPromise(stream) {
@ -82,7 +108,7 @@ index e552a036f89..169e8614b9f 100644
const target = /^target "(.*)"$/m.exec(yarnrc)[1];
return target;
diff --git a/build/lib/util.ts b/build/lib/util.ts
index 035c7e95ea3..4ff8dcfe6b2 100644
index 035c7e95ea3006bb3dabd68bbf54db80de4aaaf2..4ff8dcfe6b21a0ec8064ebc7bb05506b8f1faa91 100644
--- a/build/lib/util.ts
+++ b/build/lib/util.ts
@@ -322,6 +322,7 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise<void> {
@ -94,7 +120,7 @@ index 035c7e95ea3..4ff8dcfe6b2 100644
const target = /^target "(.*)"$/m.exec(yarnrc)![1];
return target;
diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js
index 8f8b0019a77..ea054c725be 100644
index 8f8b0019a7792a993fbd6bf95b013b596aa2935a..ea054c725bea2eec342e12b07314241aa18a4951 100644
--- a/build/npm/postinstall.js
+++ b/build/npm/postinstall.js
@@ -33,10 +33,11 @@ function yarnInstall(location, opts) {
@ -127,7 +153,7 @@ index 8f8b0019a77..ea054c725be 100644
cp.execSync('git config pull.rebase true');
diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js
index cb88d37adef..6b3253af0a3 100644
index cb88d37adefd4882f61a2711fdd7f72b89e1a6e3..6b3253af0a3a0aa4d75456379ef1c00f4cb98d13 100644
--- a/build/npm/preinstall.js
+++ b/build/npm/preinstall.js
@@ -8,8 +8,9 @@ let err = false;
@ -144,10 +170,10 @@ index cb88d37adef..6b3253af0a3 100644
const cp = require('child_process');
diff --git a/coder.js b/coder.js
new file mode 100644
index 00000000000..9cb693af63b
index 0000000000000000000000000000000000000000..df5b42cba463b6c0043aebbc835f852f1284aa36
--- /dev/null
+++ b/coder.js
@@ -0,0 +1,63 @@
@@ -0,0 +1,64 @@
+// This must be ran from VS Code's root.
+const gulp = require("gulp");
+const path = require("path");
@ -163,6 +189,7 @@ index 00000000000..9cb693af63b
+ buildfile.base,
+ buildfile.workbenchWeb,
+ buildfile.workerExtensionHost,
+ buildfile.workerNotebook,
+ buildfile.keyboardMaps,
+ buildfile.entrypoint("vs/platform/files/node/watcher/unix/watcherApp", ["vs/css", "vs/nls"]),
+ buildfile.entrypoint("vs/platform/files/node/watcher/nsfw/watcherApp", ["vs/css", "vs/nls"]),
@ -212,7 +239,7 @@ index 00000000000..9cb693af63b
+ common.minifyTask("out-vscode")
+));
diff --git a/extensions/postinstall.js b/extensions/postinstall.js
index da4fa3e9d04..50f3e1144f8 100644
index da4fa3e9d0443d679dfbab1000b434af2ae01afd..50f3e1144f8057883dea8b91ec2f7073458dbd94 100644
--- a/extensions/postinstall.js
+++ b/extensions/postinstall.js
@@ -24,6 +24,9 @@ function processRoot() {
@ -226,10 +253,10 @@ index da4fa3e9d04..50f3e1144f8 100644
function processLib() {
diff --git a/package.json b/package.json
index 226f51a1ec5..5c4e5af5f69 100644
index 9b5ee0f876303283eb766fd2bb3ed818c50b1d3e..30ef9fa81b1cd844138388d794d4d6d9db5c7fba 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,11 @@
@@ -46,7 +46,11 @@
"watch-web": "gulp watch-web --max_old_space_size=4095",
"eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions"
},
@ -241,15 +268,15 @@ index 226f51a1ec5..5c4e5af5f69 100644
"applicationinsights": "1.0.8",
"chokidar": "3.2.3",
"graceful-fs": "4.2.3",
@@ -59,6 +63,7 @@
"native-keymap": "2.1.2",
@@ -60,6 +64,7 @@
"native-keymap": "2.2.0",
"native-watchdog": "1.3.0",
"node-pty": "0.10.0-beta8",
+ "rimraf": "^2.2.8",
"semver-umd": "^5.5.7",
"spdlog": "^0.11.1",
"sudo-prompt": "9.1.1",
@@ -159,7 +164,6 @@
@@ -160,7 +165,6 @@
"pump": "^1.0.1",
"queue": "3.0.6",
"rcedit": "^1.1.0",
@ -257,7 +284,7 @@ index 226f51a1ec5..5c4e5af5f69 100644
"sinon": "^1.17.2",
"source-map": "^0.4.4",
"style-loader": "^1.0.0",
@@ -190,5 +194,8 @@
@@ -192,5 +196,8 @@
"windows-foreground-love": "0.2.0",
"windows-mutex": "0.3.0",
"windows-process-tree": "0.2.4"
@ -267,7 +294,7 @@ index 226f51a1ec5..5c4e5af5f69 100644
}
}
diff --git a/product.json b/product.json
index 2b884d18f30..518b935b837 100644
index b9349015e3475bff07104ca2fa859954a37f962a..4c32260abc42efe17ee7d717e4dcebf182044e8c 100644
--- a/product.json
+++ b/product.json
@@ -20,7 +20,7 @@
@ -281,18 +308,18 @@ index 2b884d18f30..518b935b837 100644
"ms-vscode.vscode-js-profile-flame",
diff --git a/remote/.yarnrc b/remote/.yarnrc
deleted file mode 100644
index 1e16cde724c..00000000000
index c1a32ce532afa501fb19bdbcf6bcb0ec151ecd99..0000000000000000000000000000000000000000
--- a/remote/.yarnrc
+++ /dev/null
@@ -1,3 +0,0 @@
-disturl "http://nodejs.org/dist"
-target "12.4.0"
-target "12.14.1"
-runtime "node"
diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts
index 1286c5117a4..e60dd11d039 100644
index 4b6aebc16466dff58a9dfab4a680d230fa1f71a5..dd72e179ec0fa9a0b3e16e497225cb6da6218af3 100644
--- a/src/vs/base/common/network.ts
+++ b/src/vs/base/common/network.ts
@@ -111,16 +111,17 @@ class RemoteAuthoritiesImpl {
@@ -113,16 +113,17 @@ class RemoteAuthoritiesImpl {
if (host && host.indexOf(':') !== -1) {
host = `[${host}]`;
}
@ -314,7 +341,7 @@ index 1286c5117a4..e60dd11d039 100644
});
}
diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts
index 0bbc5d6ef91..61f139b9c55 100644
index 0bbc5d6ef911b1e98d26ad796873a9b6b7fb04ec..61f139b9c557b9c46e5a9640ab0e37a6fb7692ee 100644
--- a/src/vs/base/common/platform.ts
+++ b/src/vs/base/common/platform.ts
@@ -59,6 +59,17 @@ if (typeof navigator === 'object' && !isElectronRenderer) {
@ -336,7 +363,7 @@ index 0bbc5d6ef91..61f139b9c55 100644
_isWindows = (process.platform === 'win32');
_isMacintosh = (process.platform === 'darwin');
diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts
index c52f7b3774f..08a87fa970f 100644
index c52f7b3774f399d3fa161682316b20d807072806..08a87fa970f159f84691c5068cf5e38f0926015c 100644
--- a/src/vs/base/common/processes.ts
+++ b/src/vs/base/common/processes.ts
@@ -110,7 +110,8 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve
@ -350,7 +377,7 @@ index c52f7b3774f..08a87fa970f 100644
const envKeys = Object.keys(env);
envKeys
diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts
index ef2291d49b1..29b2f9dfc2b 100644
index ef2291d49b13c9c995afc90eab9c92afabc2b3b4..29b2f9dfc2b7fa998ac1188db06dee95419fcd5b 100644
--- a/src/vs/base/common/uriIpc.ts
+++ b/src/vs/base/common/uriIpc.ts
@@ -5,6 +5,7 @@
@ -416,7 +443,7 @@ index ef2291d49b1..29b2f9dfc2b 100644
\ No newline at end of file
+}
diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js
index 2c64061da7b..c0ef8faedd4 100644
index 2c64061da7b01aef0bfe3cec851da232ca9461c8..c0ef8faedd406c38bf9c55bbbdbbb060046492d9 100644
--- a/src/vs/base/node/languagePacks.js
+++ b/src/vs/base/node/languagePacks.js
@@ -128,7 +128,10 @@ function factory(nodeRequire, path, fs, perf) {
@ -432,19 +459,18 @@ index 2c64061da7b..c0ef8faedd4 100644
// Do nothing. If we can't read the file we have no
// language pack config.
diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts
index c629f7fffa1..c266e1fb06f 100644
index ad5272b22320a361cec0eed40d57629b06147c01..c9280b14472507ebb9a277f554485f08b090cb69 100644
--- a/src/vs/code/browser/workbench/workbench.ts
+++ b/src/vs/code/browser/workbench/workbench.ts
@@ -13,6 +13,8 @@ import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/wi
import { isEqual } from 'vs/base/common/resources';
@@ -16,6 +16,7 @@ import { isEqual } from 'vs/base/common/resources';
import { isStandalone } from 'vs/base/browser/browser';
import { localize } from 'vs/nls';
+import { Schemas } from 'vs/base/common/network';
import { Schemas } from 'vs/base/common/network';
+import { encodePath } from 'vs/server/node/util';
interface ICredential {
service: string;
@@ -243,12 +245,18 @@ class WorkspaceProvider implements IWorkspaceProvider {
@@ -253,12 +254,18 @@ class WorkspaceProvider implements IWorkspaceProvider {
// Folder
else if (isFolderToOpen(workspace)) {
@ -465,7 +491,7 @@ index c629f7fffa1..c266e1fb06f 100644
}
// Append payload if any
@@ -285,7 +293,22 @@ class WorkspaceProvider implements IWorkspaceProvider {
@@ -348,7 +355,22 @@ class WindowIndicator implements IWindowIndicator {
throw new Error('Missing web configuration element');
}
@ -489,7 +515,7 @@ index c629f7fffa1..c266e1fb06f 100644
// Revive static extension locations
if (Array.isArray(config.staticExtensions)) {
@@ -297,40 +320,7 @@ class WorkspaceProvider implements IWorkspaceProvider {
@@ -360,40 +382,7 @@ class WindowIndicator implements IWindowIndicator {
// Find workspace to open and payload
let foundWorkspace = false;
let workspace: IWorkspace;
@ -532,7 +558,7 @@ index c629f7fffa1..c266e1fb06f 100644
// If no workspace is provided through the URL, check for config attribute from server
if (!foundWorkspace) {
diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts
index 2379b626c81..28f8971cf39 100644
index 92dd2bcf87dba5e5f07f2707a91b1a364ab1b05f..047522bd1a2c1edfda05c3739838fecbd70db6c5 100644
--- a/src/vs/platform/environment/node/argv.ts
+++ b/src/vs/platform/environment/node/argv.ts
@@ -8,6 +8,8 @@ import { localize } from 'vs/nls';
@ -544,7 +570,7 @@ index 2379b626c81..28f8971cf39 100644
_: string[];
'folder-uri'?: string[]; // undefined or array of 1 or more
'file-uri'?: string[]; // undefined or array of 1 or more
@@ -141,6 +143,8 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
@@ -142,6 +144,8 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'extensions-download-dir': { type: 'string' },
'builtin-extensions-dir': { type: 'string' },
@ -553,13 +579,13 @@ index 2379b626c81..28f8971cf39 100644
'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
@@ -403,4 +407,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve
@@ -405,4 +409,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve
export function buildVersionMessage(version: string | undefined, commit: string | undefined): string {
return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
}
-
diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts
index 5c0dc4ad4ae..38b8c7573a8 100644
index 45d5ec2cc02707d91f19a66d408ae46a1201a9e8..4ed498c63ceb55d15bd104a92b701ead3dfa81f2 100644
--- a/src/vs/platform/environment/node/environmentService.ts
+++ b/src/vs/platform/environment/node/environmentService.ts
@@ -38,6 +38,8 @@ export interface INativeEnvironmentService extends IEnvironmentService {
@ -586,7 +612,7 @@ index 5c0dc4ad4ae..38b8c7573a8 100644
get extensionDevelopmentLocationURI(): URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
index 575b2aafc38..873181f9678 100644
index 575b2aafc3802cd6f5f943930e30de9f2c2690de..873181f967856759e3dc001e5bbe06e2f9eb676a 100644
--- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts
+++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
@@ -85,7 +85,7 @@ export class ExtensionsScanner extends Disposable {
@ -633,7 +659,7 @@ index 575b2aafc38..873181f9678 100644
+ }
}
diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts
index 3370a608b4b..37b3592d39d 100644
index bb33203d1727b1c076efac9113afc3b2580cdbd9..c53cea338cdaa0f0ac15542c129e1572b3f13b80 100644
--- a/src/vs/platform/product/common/product.ts
+++ b/src/vs/platform/product/common/product.ts
@@ -30,6 +30,12 @@ if (isWeb) {
@ -650,7 +676,7 @@ index 3370a608b4b..37b3592d39d 100644
// Node: AMD loader
diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts
index 040c869d94c..bf16defcf7b 100644
index d1cb00a6d63621a4873a6a5e815220d084ceac2a..1a69d6f63a7406d364aa3e2b32fb75309f212e98 100644
--- a/src/vs/platform/product/common/productService.ts
+++ b/src/vs/platform/product/common/productService.ts
@@ -30,6 +30,8 @@ export type ConfigurationSyncStore = {
@ -663,7 +689,7 @@ index 040c869d94c..bf16defcf7b 100644
readonly date?: string;
readonly quality?: string;
diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts
index 3715cbb8e6e..c65de8ad37e 100644
index 3715cbb8e6ee41c3d9b5090918d243b723ae2d00..c65de8ad37e727d66da97a8f8b170cbcef87181b 100644
--- a/src/vs/platform/remote/browser/browserSocketFactory.ts
+++ b/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -208,7 +208,8 @@ export class BrowserSocketFactory implements ISocketFactory {
@ -684,10 +710,10 @@ index 3715cbb8e6e..c65de8ad37e 100644
-
-
diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts
index 2185bb5228c..35463ca6520 100644
index 18d3d04fd20335975293e37b3b641120dd92da20..4e49f9d63623da6c84624144765f76ec127ea526 100644
--- a/src/vs/platform/remote/common/remoteAgentConnection.ts
+++ b/src/vs/platform/remote/common/remoteAgentConnection.ts
@@ -89,7 +89,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
@@ -92,7 +92,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
options.socketFactory.connect(
options.host,
options.port,
@ -696,9 +722,52 @@ index 2185bb5228c..35463ca6520 100644
(err: any, socket: ISocket | undefined) => {
if (err || !socket) {
options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`);
diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts
index ab3fd347b69f8a3d9b96e706cd87c911b8ffed6b..9d351037b577f9f1edfd18ae9b3c48a211f4467f 100644
--- a/src/vs/platform/storage/browser/storageService.ts
+++ b/src/vs/platform/storage/browser/storageService.ts
@@ -122,8 +122,8 @@ export class BrowserStorageService extends Disposable implements IStorageService
return this.getStorage(scope).getNumber(key, fallbackValue);
}
- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
- this.getStorage(scope).set(key, value);
+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> {
+ return this.getStorage(scope).set(key, value);
}
remove(key: string, scope: StorageScope): void {
diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts
index 6611f1dae42055f69a55c1c154d9475f11cd4d0a..d598d4909d5ff6d1614e4a038b1865e1f9a4e963 100644
--- a/src/vs/platform/storage/common/storage.ts
+++ b/src/vs/platform/storage/common/storage.ts
@@ -85,7 +85,7 @@ export interface IStorageService {
* The scope argument allows to define the scope of the storage
* operation to either the current workspace only or all workspaces.
*/
- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void;
+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> | void;
/**
* Delete an element stored under the provided key from storage.
diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts
index ac657056aa68549f0053cfb1ec68835ba4ce20f9..143f9b5681eb867c5e5c5437946ab785eb34e4b4 100644
--- a/src/vs/platform/storage/node/storageService.ts
+++ b/src/vs/platform/storage/node/storageService.ts
@@ -202,8 +202,8 @@ export class NativeStorageService extends Disposable implements IStorageService
return this.getStorage(scope).getNumber(key, fallbackValue);
}
- store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
- this.getStorage(scope).set(key, value);
+ store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> {
+ return this.getStorage(scope).set(key, value);
}
remove(key: string, scope: StorageScope): void {
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
new file mode 100644
index 00000000000..3c0703b7174
index 0000000000000000000000000000000000000000..3c0703b7174ad792a4b42841e96ee93765d71601
--- /dev/null
+++ b/src/vs/server/browser/client.ts
@@ -0,0 +1,189 @@
@ -893,7 +962,7 @@ index 00000000000..3c0703b7174
+};
diff --git a/src/vs/server/browser/extHostNodeProxy.ts b/src/vs/server/browser/extHostNodeProxy.ts
new file mode 100644
index 00000000000..ed7c078077b
index 0000000000000000000000000000000000000000..ed7c078077b0c375758529959b280e091436113a
--- /dev/null
+++ b/src/vs/server/browser/extHostNodeProxy.ts
@@ -0,0 +1,46 @@
@ -945,7 +1014,7 @@ index 00000000000..ed7c078077b
+export const IExtHostNodeProxy = createDecorator<IExtHostNodeProxy>('IExtHostNodeProxy');
diff --git a/src/vs/server/browser/mainThreadNodeProxy.ts b/src/vs/server/browser/mainThreadNodeProxy.ts
new file mode 100644
index 00000000000..0d2e93edae2
index 0000000000000000000000000000000000000000..0d2e93edae2baf34d27b7b52be0bf4960f244531
--- /dev/null
+++ b/src/vs/server/browser/mainThreadNodeProxy.ts
@@ -0,0 +1,37 @@
@ -988,7 +1057,7 @@ index 00000000000..0d2e93edae2
+}
diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts
new file mode 100644
index 00000000000..5ae44cdc856
index 0000000000000000000000000000000000000000..5ae44cdc856bf81326a4c516b8be9afb2c746a67
--- /dev/null
+++ b/src/vs/server/browser/worker.ts
@@ -0,0 +1,56 @@
@ -1050,7 +1119,7 @@ index 00000000000..5ae44cdc856
+};
diff --git a/src/vs/server/common/nodeProxy.ts b/src/vs/server/common/nodeProxy.ts
new file mode 100644
index 00000000000..14b9de879ce
index 0000000000000000000000000000000000000000..14b9de879ceab4c1976770fa7810d276c5aa3e36
--- /dev/null
+++ b/src/vs/server/common/nodeProxy.ts
@@ -0,0 +1,47 @@
@ -1103,7 +1172,7 @@ index 00000000000..14b9de879ce
+}
diff --git a/src/vs/server/common/telemetry.ts b/src/vs/server/common/telemetry.ts
new file mode 100644
index 00000000000..4ea6d95d36a
index 0000000000000000000000000000000000000000..4ea6d95d36aaac07dbd4d0e16ab3c1bba255f683
--- /dev/null
+++ b/src/vs/server/common/telemetry.ts
@@ -0,0 +1,65 @@
@ -1174,7 +1243,7 @@ index 00000000000..4ea6d95d36a
+}
diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts
new file mode 100644
index 00000000000..ab020fbb4e4
index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362389faafa
--- /dev/null
+++ b/src/vs/server/entry.ts
@@ -0,0 +1,78 @@
@ -1258,7 +1327,7 @@ index 00000000000..ab020fbb4e4
+}
diff --git a/src/vs/server/fork.js b/src/vs/server/fork.js
new file mode 100644
index 00000000000..56331ff1fc3
index 0000000000000000000000000000000000000000..56331ff1fc32bbd82e769aaecb551e427f798ec3
--- /dev/null
+++ b/src/vs/server/fork.js
@@ -0,0 +1,3 @@
@ -1267,7 +1336,7 @@ index 00000000000..56331ff1fc3
+require('../../bootstrap-amd').load('vs/server/entry');
diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts
new file mode 100644
index 00000000000..33b28cf2d53
index 0000000000000000000000000000000000000000..33b28cf2d53746ee9c50c056ac2e087dcee0a4e2
--- /dev/null
+++ b/src/vs/server/ipc.d.ts
@@ -0,0 +1,131 @@
@ -1404,7 +1473,7 @@ index 00000000000..33b28cf2d53
+}
diff --git a/src/vs/server/node/channel.ts b/src/vs/server/node/channel.ts
new file mode 100644
index 00000000000..e10cc9c218b
index 0000000000000000000000000000000000000000..e10cc9c218b27d859a523be3db5b8a30ef90d953
--- /dev/null
+++ b/src/vs/server/node/channel.ts
@@ -0,0 +1,360 @@
@ -1770,7 +1839,7 @@ index 00000000000..e10cc9c218b
+}
diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts
new file mode 100644
index 00000000000..36e80fb6966
index 0000000000000000000000000000000000000000..36e80fb6966ae2cb53c98f3d31e2193d00c509c3
--- /dev/null
+++ b/src/vs/server/node/connection.ts
@@ -0,0 +1,157 @@
@ -1933,7 +2002,7 @@ index 00000000000..36e80fb6966
+}
diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts
new file mode 100644
index 00000000000..a0ece345f28
index 0000000000000000000000000000000000000000..a0ece345f28f06afb2af12fe4901ad228b2475a4
--- /dev/null
+++ b/src/vs/server/node/insights.ts
@@ -0,0 +1,124 @@
@ -2063,7 +2132,7 @@ index 00000000000..a0ece345f28
+}
diff --git a/src/vs/server/node/ipc.ts b/src/vs/server/node/ipc.ts
new file mode 100644
index 00000000000..5e560eb46e6
index 0000000000000000000000000000000000000000..5e560eb46e6a0a18c91e440c655ac0d44b09b6dd
--- /dev/null
+++ b/src/vs/server/node/ipc.ts
@@ -0,0 +1,61 @@
@ -2130,7 +2199,7 @@ index 00000000000..5e560eb46e6
+export const ipcMain = new IpcMain();
diff --git a/src/vs/server/node/logger.ts b/src/vs/server/node/logger.ts
new file mode 100644
index 00000000000..2a39c524aaa
index 0000000000000000000000000000000000000000..2a39c524aaa1b4031e04a631842f30b6fec3d98a
--- /dev/null
+++ b/src/vs/server/node/logger.ts
@@ -0,0 +1,2 @@
@ -2138,7 +2207,7 @@ index 00000000000..2a39c524aaa
+export const logger = baseLogger.named('vscode');
diff --git a/src/vs/server/node/marketplace.ts b/src/vs/server/node/marketplace.ts
new file mode 100644
index 00000000000..8956fc40d48
index 0000000000000000000000000000000000000000..8956fc40d48448b9932036c4c286464881807338
--- /dev/null
+++ b/src/vs/server/node/marketplace.ts
@@ -0,0 +1,174 @@
@ -2318,7 +2387,7 @@ index 00000000000..8956fc40d48
+};
diff --git a/src/vs/server/node/nls.ts b/src/vs/server/node/nls.ts
new file mode 100644
index 00000000000..3d428a57d31
index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b443badf4e9
--- /dev/null
+++ b/src/vs/server/node/nls.ts
@@ -0,0 +1,88 @@
@ -2412,7 +2481,7 @@ index 00000000000..3d428a57d31
+};
diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts
new file mode 100644
index 00000000000..3c74512192a
index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b9cfd5fbf
--- /dev/null
+++ b/src/vs/server/node/protocol.ts
@@ -0,0 +1,73 @@
@ -2491,7 +2560,7 @@ index 00000000000..3c74512192a
+}
diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts
new file mode 100644
index 00000000000..4b88fedb2f0
index 0000000000000000000000000000000000000000..4b88fedb2f05d300fb50978e63721d4d04b7fb5f
--- /dev/null
+++ b/src/vs/server/node/server.ts
@@ -0,0 +1,285 @@
@ -2782,7 +2851,7 @@ index 00000000000..4b88fedb2f0
+}
diff --git a/src/vs/server/node/util.ts b/src/vs/server/node/util.ts
new file mode 100644
index 00000000000..fa47e993b46
index 0000000000000000000000000000000000000000..fa47e993b46802f1a26457649e9e8bc467a73bf2
--- /dev/null
+++ b/src/vs/server/node/util.ts
@@ -0,0 +1,13 @@
@ -2800,7 +2869,7 @@ index 00000000000..fa47e993b46
+ return path.split("/").map((p) => encodeURIComponent(p)).join("/");
+};
diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts
index 3d77009b908..11deb1b99ac 100644
index bfabf0008910c87146df53a2e10fe63bae517a86..32b3b1cf84c8d280fd7f03d541b867691d51c2fb 100644
--- a/src/vs/workbench/api/browser/extensionHost.contribution.ts
+++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts
@@ -60,6 +60,7 @@ import './mainThreadComments';
@ -2811,8 +2880,26 @@ index 3d77009b908..11deb1b99ac 100644
import './mainThreadTunnelService';
import './mainThreadAuthentication';
import './mainThreadTimeline';
diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts
index 7bc3904963bed2925f3640b6bd929347159dd3cf..c6db2368ae9eaca61889efcf3c49763c01ff7459 100644
--- a/src/vs/workbench/api/browser/mainThreadStorage.ts
+++ b/src/vs/workbench/api/browser/mainThreadStorage.ts
@@ -58,11 +58,11 @@ export class MainThreadStorage implements MainThreadStorageShape {
return JSON.parse(jsonValue);
}
- $setValue(shared: boolean, key: string, value: object): Promise<void> {
+ async $setValue(shared: boolean, key: string, value: object): Promise<void> {
let jsonValue: string;
try {
jsonValue = JSON.stringify(value);
- this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
+ await this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
} catch (err) {
return Promise.reject(err);
}
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index 97793666ad8..13cd137db1e 100644
index 3595cd3e38136222044a13050b15105bbe539068..989caefff7c4b8203c03cec8fa451f5e70ea8964 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -68,6 +68,7 @@ import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransf
@ -2823,7 +2910,7 @@ index 97793666ad8..13cd137db1e 100644
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
@@ -97,6 +98,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
@@ -100,6 +101,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostStorage = accessor.get(IExtHostStorage);
const extensionStoragePaths = accessor.get(IExtensionStoragePaths);
const extHostLogService = accessor.get(ILogService);
@ -2831,7 +2918,7 @@ index 97793666ad8..13cd137db1e 100644
const extHostTunnelService = accessor.get(IExtHostTunnelService);
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
const extHostWindow = accessor.get(IExtHostWindow);
@@ -107,6 +109,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
@@ -110,6 +112,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
@ -2840,10 +2927,10 @@ index 97793666ad8..13cd137db1e 100644
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index eb5d8ea8455..da9eb521ca4 100644
index 4b7946662950f18179a5b6e3552abd39e68ca80e..ca1352d311a94b42e18d0d9e4859b18ec2bb271d 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -769,6 +769,16 @@ export interface MainThreadLabelServiceShape extends IDisposable {
@@ -795,6 +795,16 @@ export interface MainThreadLabelServiceShape extends IDisposable {
$unregisterResourceLabelFormatter(handle: number): void;
}
@ -2860,7 +2947,7 @@ index eb5d8ea8455..da9eb521ca4 100644
export interface MainThreadSearchShape extends IDisposable {
$registerFileSearchProvider(handle: number, scheme: string): void;
$registerTextSearchProvider(handle: number, scheme: string): void;
@@ -1707,6 +1717,7 @@ export const MainContext = {
@@ -1765,6 +1775,7 @@ export const MainContext = {
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook'),
@ -2868,7 +2955,7 @@ index eb5d8ea8455..da9eb521ca4 100644
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
@@ -1745,6 +1756,7 @@ export const ExtHostContext = {
@@ -1806,6 +1817,7 @@ export const ExtHostContext = {
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),
@ -2877,10 +2964,10 @@ index eb5d8ea8455..da9eb521ca4 100644
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts
index 34639e18b6f..9c22fe6f090 100644
index 0bb5188614bcbf98b85c9208edc2b173f70b1670..38ff3e2e05645be8df619ed2b47fa2984b918741 100644
--- a/src/vs/workbench/api/common/extHostExtensionService.ts
+++ b/src/vs/workbench/api/common/extHostExtensionService.ts
@@ -32,6 +32,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
@@ -31,6 +31,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@ -2921,7 +3008,7 @@ index 34639e18b6f..9c22fe6f090 100644
this._loadExtensionContext(extensionDescription)
]).then(values => {
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);
@@ -754,7 +758,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
@@ -746,7 +750,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
@ -2931,7 +3018,7 @@ index 34639e18b6f..9c22fe6f090 100644
}
diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts
index b3c89e51cfc..e21abe4e13b 100644
index b3c89e51cfc25a53293a352a2a8ad50d5f26d595..e21abe4e13bc25a5b72f556bbfb61085842faeb7 100644
--- a/src/vs/workbench/api/node/extHost.node.services.ts
+++ b/src/vs/workbench/api/node/extHost.node.services.ts
@@ -3,6 +3,8 @@
@ -2948,8 +3035,33 @@ index b3c89e51cfc..e21abe4e13b 100644
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
+registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy<IExtHostNodeProxy>(String(IExtHostNodeProxy)) { whenReady = Promise.resolve(); });
diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts
index 7cae126cc0f804273850933468690e0f9f10a5b8..08c2aa5cdae3f3d06bb08b7055dc7e7def260132 100644
--- a/src/vs/workbench/api/node/extHostCLIServer.ts
+++ b/src/vs/workbench/api/node/extHostCLIServer.ts
@@ -11,6 +11,8 @@ import { IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { ILogService } from 'vs/platform/log/common/log';
+import { join } from 'vs/base/common/path';
+import { tmpdir } from 'os';
export interface OpenCommandPipeArgs {
type: 'open';
@@ -54,6 +56,11 @@ export class CLIServer {
private async setup(): Promise<string> {
this._ipcHandlePath = generateRandomPipeName();
+ // NOTE@coder: Write this out so we can get the most recent path.
+ fs.promises.writeFile(join(tmpdir(), "vscode-ipc"), this._ipcHandlePath).catch((error) => {
+ this.logService.error(error);
+ });
+
try {
this._server.listen(this.ipcHandlePath);
this._server.on('error', err => this.logService.error(err));
diff --git a/src/vs/workbench/api/worker/extHost.worker.services.ts b/src/vs/workbench/api/worker/extHost.worker.services.ts
index 3843fdec386..8aac4df5278 100644
index 3843fdec386edc09a1d361b63de892a04e0070ed..8aac4df527857e964798362a69f5591bef07c165 100644
--- a/src/vs/workbench/api/worker/extHost.worker.services.ts
+++ b/src/vs/workbench/api/worker/extHost.worker.services.ts
@@ -8,6 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log';
@ -2966,18 +3078,18 @@ index 3843fdec386..8aac4df5278 100644
registerSingleton(ILogService, ExtHostLogService);
+registerSingleton(IExtHostNodeProxy, ExtHostNodeProxy);
diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts
index c71ab1c7da4..572b07ff251 100644
index a6a149083719d7479268e24eb5339f6cbf93e655..360888dc7dff9437f6c85f7a2043ad9e7c4daf21 100644
--- a/src/vs/workbench/api/worker/extHostExtensionService.ts
+++ b/src/vs/workbench/api/worker/extHostExtensionService.ts
@@ -9,6 +9,7 @@ import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHost
import { URI } from 'vs/base/common/uri';
@@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
+import { loadCommonJSModule } from 'vs/server/browser/worker';
class WorkerRequireInterceptor extends RequireInterceptor {
@@ -42,10 +43,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
@@ -44,10 +45,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
}
protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
@ -2996,7 +3108,7 @@ index c71ab1c7da4..572b07ff251 100644
module = module.with({ path: ensureSuffix(module.path, '.js') });
const response = await fetch(module.toString(true));
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
index ced2d815834..dfcae73e8a0 100644
index ced2d815834e40a1543e80516472799075980733..dfcae73e8a042307600c67f163aa00ba9e0762f4 100644
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
@@ -55,6 +55,10 @@
@ -3011,7 +3123,7 @@ index ced2d815834..dfcae73e8a0 100644
.monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge {
diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts
index 0462617196b..11434d27af9 100644
index 511d7376a2bfebde59b4c67fed54c39e9dd534c9..c7c45f8e4e4ffe56a8782f58af75c6a7835142cf 100644
--- a/src/vs/workbench/browser/web.main.ts
+++ b/src/vs/workbench/browser/web.main.ts
@@ -45,6 +45,7 @@ import { FileLogService } from 'vs/platform/log/common/fileLogService';
@ -3022,7 +3134,7 @@ index 0462617196b..11434d27af9 100644
import { coalesce } from 'vs/base/common/arrays';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService';
@@ -84,6 +85,8 @@ class BrowserMain extends Disposable {
@@ -87,6 +88,8 @@ class BrowserMain extends Disposable {
// Startup
const instantiationService = workbench.startup();
@ -3032,7 +3144,7 @@ index 0462617196b..11434d27af9 100644
return instantiationService.invokeFunction(accessor => {
const commandService = accessor.get(ICommandService);
diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts
index 18ea0bfedb4..d59a17c17f4 100644
index 18ea0bfedb4492327429a38237b05915b29f6dd0..d59a17c17f4fffa23d786ce36b4ff624d5688a58 100644
--- a/src/vs/workbench/common/resources.ts
+++ b/src/vs/workbench/common/resources.ts
@@ -15,6 +15,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob';
@ -3054,7 +3166,7 @@ index 18ea0bfedb4..d59a17c17f4 100644
this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null);
this._extensionKey.set(value ? extname(value) : null);
diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css
index 9947f240bf2..bdba0a2fc64 100644
index b1838de8f21c60141d01cc424a5e000a32f1c828..0a480032e0cc8d5219cd240f8807aa317718659d 100644
--- a/src/vs/workbench/contrib/scm/browser/media/scm.css
+++ b/src/vs/workbench/contrib/scm/browser/media/scm.css
@@ -138,9 +138,11 @@
@ -3073,7 +3185,7 @@ index 9947f240bf2..bdba0a2fc64 100644
.scm-view .monaco-list .monaco-list-row .resource-group > .actions,
.scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions {
diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts
index 6e3182a696d..7df85da165a 100644
index 1360c248eb7ff937c92d08bbf30d2b76ea606dc0..adccf8b88d62381c3ec484df40c6d63142ec9ef5 100644
--- a/src/vs/workbench/services/dialogs/browser/dialogService.ts
+++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts
@@ -124,11 +124,12 @@ export class DialogService implements IDialogService {
@ -3092,10 +3204,10 @@ index 6e3182a696d..7df85da165a 100644
};
diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts
index ba2701ec54d..4d4aaa6958b 100644
index 819607be0c13fed28eb7fbe6d4a62c0b860b1aa9..b046943311b713a579cc3a94983ea1b7fca7b9b1 100644
--- a/src/vs/workbench/services/environment/browser/environmentService.ts
+++ b/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -121,8 +121,18 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
@@ -116,8 +116,18 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
@memoize
get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); }
@ -3115,7 +3227,7 @@ index ba2701ec54d..4d4aaa6958b 100644
@memoize
get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
@@ -284,7 +294,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
@@ -279,7 +289,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
extensionHostDebugEnvironment.params.port = parseInt(value);
break;
case 'enableProposedApi':
@ -3130,10 +3242,10 @@ index ba2701ec54d..4d4aaa6958b 100644
}
}
diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
index c28b1477400..6090200d9c3 100644
index 32f3dc52c1ff645df6471a03542d6ec3eb73a277..c2f4497d2eba13a771b2665ad58f12ecdfa7606a 100644
--- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
+++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
@@ -163,7 +163,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
@@ -205,7 +205,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
}
}
}
@ -3143,10 +3255,23 @@ index c28b1477400..6090200d9c3 100644
return false;
}
diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
index 33eb56db3c2..e5167794c3f 100644
index a982b3ecc58c5a2f3a92be7b8cca3a1cacbb7d47..97f9bfcf0e679be683b1b09cd569149e7962f5ad 100644
--- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
+++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
@@ -236,6 +236,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
@@ -211,8 +211,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
// Install Language pack on all servers
+ // NOTE@coder: It does not appear language packs can be installed on the web
+ // extension management server at this time. Filter out the web to fix this.
if (isLanguagePackExtension(manifest)) {
- return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
+ const servers = this.servers.filter(s => s !== this.extensionManagementServerService.webExtensionManagementServer);
+ return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
}
// 1. Install on preferred location
@@ -245,6 +248,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
@ -3159,10 +3284,10 @@ index 33eb56db3c2..e5167794c3f 100644
const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name));
error.name = INSTALL_ERROR_NOT_SUPPORTED;
diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts
index d0710e77fa2..ceb27174aee 100644
index 9e979d28691d0b0b26fde5e46b606731e31f3da5..dd31879c7dd899c73c4a1371996912f4513bfd0d 100644
--- a/src/vs/workbench/services/extensions/browser/extensionService.ts
+++ b/src/vs/workbench/services/extensions/browser/extensionService.ts
@@ -116,8 +116,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@@ -125,8 +125,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._remoteAgentService.getEnvironment(),
this._remoteAgentService.scanExtensions()
]);
@ -3175,7 +3300,7 @@ index d0710e77fa2..ceb27174aee 100644
const remoteAgentConnection = this._remoteAgentService.getConnection();
this._runningLocation = _determineRunningLocation(this._productService, this._configService, localExtensions, remoteExtensions, Boolean(remoteEnv && remoteAgentConnection));
diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts
index 65e532ee58d..0b6282fde7a 100644
index 65e532ee58dfc06ed944846d01b885cb8f260ebc..0b6282fde7ad03c7ea9872a777cbf487253abed1 100644
--- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts
+++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts
@@ -37,7 +37,8 @@ export function canExecuteOnWorkspace(manifest: IExtensionManifest, productServi
@ -3189,7 +3314,7 @@ index 65e532ee58d..0b6282fde7a 100644
export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] {
diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
index 49542eda74c..de0e2da0a4c 100644
index 49542eda74c65e485272cd37d586911886aa3ad7..de0e2da0a4c2dca91dc7e0e48c28a8a75ca3e7d4 100644
--- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
+++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
@@ -16,7 +16,7 @@ import { IInitData } from 'vs/workbench/api/common/extHost.protocol';
@ -3246,7 +3371,7 @@ index 49542eda74c..de0e2da0a4c 100644
console.error(e);
}
diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
index 79455414c06..a407593b4dc 100644
index 79455414c06b95612a0dce2cad01f2bb2f40ef49..a407593b4dc6053309ed560898918cf67470e836 100644
--- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
+++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
@@ -14,7 +14,11 @@
@ -3263,7 +3388,7 @@ index 79455414c06..a407593b4dc 100644
require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err));
diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
index 44999bd842e..601b1c54088 100644
index 44999bd842eae12b752b2e7e8c4904272b111dc1..601b1c5408835c743fe07e34da4d4534873bf832 100644
--- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
+++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
@@ -5,17 +5,17 @@
@ -3288,7 +3413,7 @@ index 44999bd842e..601b1c54088 100644
}
diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts
index 0669178db4c..28fafeb2de2 100644
index f02bbbf874b5b18ac8d077ad56a8a4a57e77a4a6..86271940724aaf28e4eda93e59920820a7d93987 100644
--- a/src/vs/workbench/workbench.web.main.ts
+++ b/src/vs/workbench/workbench.web.main.ts
@@ -35,7 +35,8 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService';
@ -3302,7 +3427,7 @@ index 0669178db4c..28fafeb2de2 100644
import 'vs/workbench/services/credentials/browser/credentialsService';
import 'vs/workbench/services/url/browser/urlService';
diff --git a/yarn.lock b/yarn.lock
index b2fbf543af3..f10dddd6594 100644
index 140ed883c1a92ebcd7a284b98ca71261fa9cb631..b363d7de5000fd370bb4221f48e193382648a185 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -140,6 +140,23 @@
@ -3329,7 +3454,7 @@ index b2fbf543af3..f10dddd6594 100644
"@electron/get@^1.0.1":
version "1.7.2"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.2.tgz#286436a9fb56ff1a1fcdf0e80131fd65f4d1e0fd"
@@ -5421,6 +5438,13 @@ jsprim@^1.2.2:
@@ -5375,6 +5392,13 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
@ -3343,7 +3468,7 @@ index b2fbf543af3..f10dddd6594 100644
just-debounce@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea"
@@ -6008,26 +6032,11 @@ minimatch@0.3:
@@ -5955,26 +5979,11 @@ minimatch@0.3:
dependencies:
brace-expansion "^1.1.7"
@ -3371,7 +3496,7 @@ index b2fbf543af3..f10dddd6594 100644
minipass@^2.2.1, minipass@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
@@ -6797,6 +6806,11 @@ p-try@^2.0.0:
@@ -6716,6 +6725,11 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==

View File

@ -1,6 +1,6 @@
FROM centos:7
ARG NODE_VERSION=v12.18.3
ARG NODE_VERSION=v12.18.4
RUN ARCH="$(uname -m | sed 's/86_64/64/; s/aarch64/arm64/')" && \
curl -fsSL "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-$ARCH.tar.xz" | tar -C /usr/local -xJ && \
mv "/usr/local/node-$NODE_VERSION-linux-$ARCH" "/usr/local/node-$NODE_VERSION"
@ -15,11 +15,16 @@ 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
# Install Go dependencies
# Copied from ../debian10/Dockerfile
# 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

View File

@ -1,4 +1,4 @@
FROM debian:8
FROM debian:10
RUN apt-get update
@ -24,28 +24,23 @@ 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
# 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

View File

@ -39,6 +39,9 @@ COPY ci/release-image/entrypoint.sh /usr/bin/entrypoint.sh
RUN dpkg -i /tmp/code-server*$(dpkg --print-architecture).deb && rm /tmp/code-server*.deb
EXPOSE 8080
USER coder
# This way, if someone sets $DOCKER_USER, docker-exec will still work as
# the uid will remain the same. note: only relevant if -u isn't passed to
# docker-run.
USER 1000
WORKDIR /home/coder
ENTRYPOINT ["/usr/bin/entrypoint.sh", "--bind-addr", "0.0.0.0:8080", "."]

View File

@ -1,18 +1,21 @@
#!/usr/bin/env sh
#!/bin/sh
set -eu
if [ "${DOCKER_USER-}" ]; then
# This isn't set by default.
USER="$(whoami)"
export USER
if [ "${DOCKER_USER-}" ] && [ "$DOCKER_USER" != "$USER" ]; then
echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/nopasswd > /dev/null
sudo usermod --login "$DOCKER_USER" \
--move-home --home "/home/$DOCKER_USER" \
coder
# Unfortunately we cannot change $HOME as we cannot move any bind mounts
# nor can we bind mount $HOME into a new home as that requires a privileged container.
sudo usermod --login "$DOCKER_USER" coder
sudo groupmod -n "$DOCKER_USER" coder
USER="$DOCKER_USER"
sudo sed -i "/coder/d" /etc/sudoers.d/nopasswd
sudo sed -i "s/coder/$DOCKER_USER/g" /etc/fixuid/config.yml
export HOME="/home/$DOCKER_USER"
fi
# This isn't set by default.
export USER="$(whoami)"
dumb-init fixuid -q /usr/bin/code-server "$@"

View File

@ -4,7 +4,7 @@ set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
NODE_VERSION=v12.18.3
NODE_VERSION=v12.18.4
NODE_OS="$(uname | tr '[:upper:]' '[:lower:]')"
NODE_ARCH="$(uname -m | sed 's/86_64/64/; s/aarch64/arm64/')"
curl -L "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$NODE_OS-$NODE_ARCH.tar.gz" | tar -xz

View File

@ -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
```
@ -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.

View File

@ -19,6 +19,7 @@
- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open)
- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server)
- [Heartbeat File](#heartbeat-file)
- [Healthz endpoint](#healthz-endpoint)
- [How does the config file work?](#how-does-the-config-file-work)
- [Blank screen on iPad?](#blank-screen-on-ipad)
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
@ -242,6 +243,20 @@ older than X minutes, kill `code-server`.
[#1636](https://github.com/cdr/code-server/issues/1636) will make the experience here better.
## Healthz endpoint
`code-server` exposes an endpoint at `/healthz` which can be used to check
whether `code-server` is up without triggering a heartbeat. The response will
include a status (`alive` or `expired`) and a timestamp for the last heartbeat
(defaults to `0`). This endpoint does not require authentication.
```json
{
"status": "alive",
"lastHeartbeat": 1599166210566
}
```
## How does the config file work?
When `code-server` starts up, it creates a default config file in `~/.config/code-server/config.yaml` that looks

View File

@ -79,8 +79,8 @@ commands presented in the rest of this document.
## Debian, Ubuntu
```bash
curl -fOL https://github.com/cdr/code-server/releases/download/v3.5.0/code-server_3.5.0_amd64.deb
sudo dpkg -i code-server_3.5.0_amd64.deb
curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server_3.6.0_amd64.deb
sudo dpkg -i code-server_3.6.0_amd64.deb
sudo systemctl enable --now code-server@$USER
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
@ -88,8 +88,8 @@ sudo systemctl enable --now code-server@$USER
## Fedora, CentOS, RHEL, SUSE
```bash
curl -fOL https://github.com/cdr/code-server/releases/download/v3.5.0/code-server-3.5.0-amd64.rpm
sudo rpm -i code-server-3.5.0-amd64.rpm
curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server-3.6.0-amd64.rpm
sudo rpm -i code-server-3.6.0-amd64.rpm
sudo systemctl enable --now code-server@$USER
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
@ -158,10 +158,10 @@ Here is an example script for installing and using a standalone `code-server` re
```bash
mkdir -p ~/.local/lib ~/.local/bin
curl -fL https://github.com/cdr/code-server/releases/download/v3.5.0/code-server-3.5.0-linux-amd64.tar.gz \
curl -fL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server-3.6.0-linux-amd64.tar.gz \
| tar -C ~/.local/lib -xz
mv ~/.local/lib/code-server-3.5.0-linux-amd64 ~/.local/lib/code-server-3.5.0
ln -s ~/.local/lib/code-server-3.5.0/bin/code-server ~/.local/bin/code-server
mv ~/.local/lib/code-server-3.6.0-linux-amd64 ~/.local/lib/code-server-3.6.0
ln -s ~/.local/lib/code-server-3.6.0/bin/code-server ~/.local/bin/code-server
PATH="~/.local/bin:$PATH"
code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
@ -179,10 +179,11 @@ code-server
# easily access/modify your code-server config in $HOME/.config/code-server/config.json
# outside the container.
mkdir -p ~/.config
docker run -it -p 127.0.0.1:8080:8080 \
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$HOME/.config:/home/coder/.config" \
-v "$PWD:/home/coder/project" \
-u "$(id -u):$(id -g)" \
-e "DOCKER_USER=$USER" \
codercom/code-server:latest
```

View File

@ -17,21 +17,28 @@ usage() {
Installs code-server for Linux, macOS and FreeBSD.
It tries to use the system package manager if possible.
After successful installation it explains how to start using code-server.
Pass in user@host to install code-server on user@host over ssh.
The remote host must have internet access.
${not_curl_usage-}
Usage:
$arg0 [--dry-run] [--version X.X.X] [--method detect] [--prefix ~/.local]
$arg0 [--dry-run] [--version X.X.X] [--method detect] \
[--prefix ~/.local] [user@host]
--dry-run
Echo the commands for the install process without running them.
--version X.X.X
Install a specific version instead of the latest.
--method [detect | standalone]
Choose the installation method. Defaults to detect.
- detect detects the system package manager and tries to use it.
Full reference on the process is further below.
- standalone installs a standalone release archive into ~/.local
Add ~/.local/bin to your \$PATH to use it.
--prefix <dir>
Sets the prefix used by standalone release archives. Defaults to ~/.local
The release is unarchived into ~/.local/lib/code-server-X.X.X
@ -100,9 +107,18 @@ main() {
METHOD \
STANDALONE_INSTALL_PREFIX \
VERSION \
OPTIONAL
OPTIONAL \
ALL_FLAGS \
SSH_ARGS
ALL_FLAGS=""
while [ "$#" -gt 0 ]; do
case "$1" in
-*)
ALL_FLAGS="${ALL_FLAGS} $1"
;;
esac
case "$1" in
--dry-run)
DRY_RUN=1
@ -132,16 +148,33 @@ main() {
usage
exit 0
;;
*)
--)
shift
# We remove the -- added above.
ALL_FLAGS="${ALL_FLAGS% --}"
SSH_ARGS="$*"
break
;;
-*)
echoerr "Unknown flag $1"
echoerr "Run with --help to see usage."
exit 1
;;
*)
SSH_ARGS="$*"
break
;;
esac
shift
done
if [ "${SSH_ARGS-}" ]; then
echoh "Installing remotely with ssh $SSH_ARGS"
curl -fsSL https://code-server.dev/install.sh | prefix "$SSH_ARGS" ssh "$SSH_ARGS" sh -s -- "$ALL_FLAGS"
return
fi
VERSION="${VERSION-$(echo_latest_version)}"
METHOD="${METHOD-detect}"
if [ "$METHOD" != detect ] && [ "$METHOD" != standalone ]; then
@ -446,7 +479,7 @@ arch() {
}
command_exists() {
command -v "$@" > /dev/null 2>&1
command -v "$@" > /dev/null
}
sh_c() {
@ -500,4 +533,15 @@ humanpath() {
sed "s# $HOME# ~#g; s#\"$HOME#\"\$HOME#g"
}
# We need to make sure we exit with a non zero exit if the command fails.
# /bin/sh does not support -o pipefail unfortunately.
prefix() {
PREFIX="$1"
shift
fifo="$(mktemp -d)/fifo"
mkfifo "$fifo"
sed -e "s#^#$PREFIX: #" "$fifo" &
"$@" > "$fifo" 2>&1
}
main "$@"

@ -1 +1 @@
Subproject commit a0479759d6e9ea56afa657e454193f72aef85bd0
Subproject commit 2af051012b66169dde0c4dfae3f5ef48f787ff69

View File

@ -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": {
@ -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",

View File

@ -47,5 +47,5 @@
</div>
</body>
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/register.js"></script>
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/login.js"></script>
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/pages/login.js"></script>
</html>

View File

@ -17,7 +17,7 @@ try {
}
// FIXME: Only works if path separators are /.
const path = nlsConfig._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
fetch(`{{BASE}}/resource/?path=${encodeURIComponent(path)}`)
fetch(`${options.base}/vscode/resource/?path=${encodeURIComponent(path)}`)
.then((response) => response.json())
.then((json) => {
bundles[bundle] = json

View File

@ -10,7 +10,7 @@ if ("serviceWorker" in navigator) {
const path = normalize(`${options.csStaticBase}/dist/serviceWorker.js`)
navigator.serviceWorker
.register(path, {
scope: options.base || "/",
scope: (options.base ?? "") + "/",
})
.then(() => {
console.log("[Service Worker] registered")

2
src/browser/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@ -1,6 +1,4 @@
import * as http from "http"
import { HttpCode, HttpError } from "../../common/http"
import { HttpProvider, HttpResponse, Route, Heart, HttpProviderOptions } from "../http"
import { HttpProvider, HttpResponse, Heart, HttpProviderOptions } from "../http"
/**
* Check the heartbeat.
@ -10,15 +8,8 @@ export class HealthHttpProvider extends HttpProvider {
super(options)
}
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
if (!this.authenticated(request)) {
if (this.isRoot(route)) {
return { redirect: "/login", query: { to: route.fullPath } }
}
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
}
const result = {
public async handleRequest(): Promise<HttpResponse> {
return {
cache: false,
mime: "application/json",
content: {
@ -26,7 +17,5 @@ export class HealthHttpProvider extends HttpProvider {
lastHeartbeat: this.heart.lastHeartbeat,
},
}
return result
}
}

View File

@ -5,7 +5,7 @@ import * as os from "os"
import * as path from "path"
import { Args as VsArgs } from "../../lib/vscode/src/vs/server/ipc"
import { AuthType } from "./http"
import { generatePassword, humanPath, paths } from "./util"
import { canConnect, generatePassword, humanPath, paths } from "./util"
export class Optional<T> {
public constructor(public readonly value?: T) {}
@ -47,6 +47,8 @@ export interface Args extends VsArgs {
readonly _: string[]
readonly "reuse-window"?: boolean
readonly "new-window"?: boolean
readonly link?: OptionalString
}
interface Option<T> {
@ -63,6 +65,11 @@ interface Option<T> {
* Description of the option. Leave blank to hide the option.
*/
description?: string
/**
* If marked as beta, the option is not printed unless $CS_BETA is set.
*/
beta?: boolean
}
type OptionType<T> = T extends boolean
@ -130,7 +137,8 @@ const options: Options<Required<Args>> = {
"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'.",
"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[]",
@ -144,17 +152,29 @@ const options: Options<Required<Args>> = {
"new-window": {
type: "boolean",
short: "n",
description: "Force to open a new window. (use with open-in)",
description: "Force to open a new window.",
},
"reuse-window": {
type: "boolean",
short: "r",
description: "Force to open a file or folder in an already opened window. (use with open-in)",
description: "Force to open a file or folder in an already opened window.",
},
locale: { type: "string" },
log: { type: LogLevel },
verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." },
link: {
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.
Authorization is done via GitHub.
This is presently beta and requires being accepted for testing.
See https://github.com/cdr/code-server/discussions/2137
`,
beta: true,
},
}
export const optionDescriptions = (): string[] => {
@ -166,12 +186,32 @@ 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}${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 = (
@ -287,6 +327,21 @@ export const parse = (
logger.debug("parsed command line", field("args", args))
return args
}
export async function setDefaults(args: Args): Promise<Args> {
args = { ...args }
if (!args["user-data-dir"]) {
await copyOldMacOSDataDir()
args["user-data-dir"] = paths.data
}
if (!args["extensions-dir"]) {
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
}
// --verbose takes priority over --log and --log takes priority over the
// environment variable.
if (args.verbose) {
@ -329,21 +384,6 @@ export const parse = (
return args
}
export async function setDefaults(args: Args): Promise<Args> {
args = { ...args }
if (!args["user-data-dir"]) {
await copyOldMacOSDataDir()
args["user-data-dir"] = paths.data
}
if (!args["extensions-dir"]) {
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
}
return args
}
async function defaultConfigFile(): Promise<string> {
return `bind-addr: 127.0.0.1:8080
auth: password
@ -370,10 +410,6 @@ export async function readConfigFile(configPath?: string): Promise<Args> {
logger.info(`Wrote default config file to ${humanPath(configPath)}`)
}
if (!process.env.CODE_SERVER_PARENT_PID && !process.env.VSCODE_IPC_HOOK_CLI) {
logger.info(`Using config file ${humanPath(configPath)}`)
}
const configFile = await fs.readFile(configPath)
const config = yaml.safeLoad(configFile.toString(), {
filename: configPath,
@ -401,7 +437,10 @@ export async function readConfigFile(configPath?: string): Promise<Args> {
function parseBindAddr(bindAddr: string): [string, number] {
const u = new URL(`http://${bindAddr}`)
return [u.hostname, parseInt(u.port, 10)]
// With the http scheme 80 will be dropped so assume it's 80 if missing. This
// means --bind-addr <addr> without a port will default to 80 as well and not
// the code-server default.
return [u.hostname, u.port ? parseInt(u.port, 10) : 80]
}
interface Addr {
@ -453,3 +492,52 @@ async function copyOldMacOSDataDir(): Promise<void> {
await fs.copy(oldDataDir, paths.data)
}
}
export const shouldRunVsCodeCli = (args: Args): boolean => {
return !!args["list-extensions"] || !!args["install-extension"] || !!args["uninstall-extension"]
}
/**
* Determine if it looks like the user is trying to open a file or folder in an
* existing instance. The arguments here should be the arguments the user
* explicitly passed on the command line, not defaults or the configuration.
*/
export const shouldOpenInExistingInstance = async (args: Args): Promise<string | undefined> => {
// Always use the existing instance if we're running from VS Code's terminal.
if (process.env.VSCODE_IPC_HOOK_CLI) {
return process.env.VSCODE_IPC_HOOK_CLI
}
const readSocketPath = async (): Promise<string | undefined> => {
try {
return await fs.readFile(path.join(os.tmpdir(), "vscode-ipc"), "utf8")
} catch (error) {
if (error.code !== "ENOENT") {
throw error
}
}
return undefined
}
// If these flags are set then assume the user is trying to open in an
// existing instance since these flags have no effect otherwise.
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
return args[cur as keyof Args] ? prev + 1 : prev
}, 0)
if (openInFlagCount > 0) {
return readSocketPath()
}
// It's possible the user is trying to spawn another instance of code-server.
// Check if any unrelated flags are set (check against one because `_` always
// exists), that a file or directory was passed, and that the socket is
// active.
if (Object.keys(args).length === 1 && args._.length > 0) {
const socketPath = await readSocketPath()
if (socketPath && (await canConnect(socketPath))) {
return socketPath
}
}
return undefined
}

43
src/node/coder-cloud.ts Normal file
View File

@ -0,0 +1,43 @@
import { logger } from "@coder/logger"
import { spawn } from "child_process"
import path from "path"
import split2 from "split2"
// https://github.com/cdr/coder-cloud
const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent")
function runAgent(...args: string[]): Promise<void> {
logger.debug(`running agent with ${args}`)
const agent = spawn(coderCloudAgent, args, {
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()
})
})
}
export function coderCloudBind(csAddr: string, serverName = ""): Promise<void> {
logger.info("Remember --link 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?:\/\//, "")
return runAgent("bind", `--code-server-addr=${csAddr}`, serverName)
}

View File

@ -11,18 +11,21 @@ import { ProxyHttpProvider } from "./app/proxy"
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 {
Args,
bindAddrFromAllSources,
optionDescriptions,
parse,
readConfigFile,
setDefaults,
shouldOpenInExistingInstance,
shouldRunVsCodeCli,
} from "./cli"
import { coderCloudBind } 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"
process.on("uncaughtException", (error) => {
logger.error(`Uncaught exception: ${error.message}`)
if (typeof error.stack !== "undefined") {
logger.error(error.stack)
}
})
import { ipcMain, WrapperProcess } from "./wrapper"
let pkg: { version?: string; commit?: string } = {}
try {
@ -34,7 +37,100 @@ try {
const version = pkg.version || "development"
const commit = pkg.commit || "development"
const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void> => {
export const runVsCodeCli = (args: Args): void => {
logger.debug("forking vs code cli...")
const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], {
env: {
...process.env,
CODE_SERVER_PARENT_PID: process.pid.toString(),
},
})
vscode.once("message", (message: any) => {
logger.debug("got message from VS Code", field("message", message))
if (message.type !== "ready") {
logger.error("Unexpected response waiting for ready response", field("type", message.type))
process.exit(1)
}
const send: CliMessage = { type: "cli", args }
vscode.send(send)
})
vscode.once("error", (error) => {
logger.error("Got error from VS Code", field("error", error))
process.exit(1)
})
vscode.on("exit", (code) => process.exit(code || 0))
}
export const openInExistingInstance = async (args: Args, socketPath: string): Promise<void> => {
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
type: "open",
folderURIs: [],
fileURIs: [],
forceReuseWindow: args["reuse-window"],
forceNewWindow: args["new-window"],
}
const isDir = async (path: string): Promise<boolean> => {
try {
const st = await fs.stat(path)
return st.isDirectory()
} catch (error) {
return false
}
}
for (let i = 0; i < args._.length; i++) {
const fp = path.resolve(args._[i])
if (await isDir(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 VS Code", field("message", message.toString()))
})
},
)
vscode.on("error", (error: unknown) => {
logger.error("got error from VS Code", field("error", error))
})
vscode.write(JSON.stringify(pipeArgs))
vscode.end()
}
const main = async (args: Args, configArgs: Args): Promise<void> => {
if (args.link) {
// 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,
socket: undefined,
cert: undefined,
}
logger.info("link: disabling auth and listening on random localhost port for cloud agent")
}
if (!args.auth) {
args = {
...args,
@ -51,7 +147,7 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void>
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 = {
@ -85,13 +181,15 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void>
await loadPlugins(httpServer, args)
ipcMain().onDispose(() => {
ipcMain.onDispose(() => {
httpServer.dispose().then((errors) => {
errors.forEach((error) => logger.error(error.message))
})
})
logger.info(`code-server ${version} ${commit}`)
logger.info(`Using config file ${humanPath(args.config)}`)
const serverAddress = await httpServer.listen()
logger.info(`HTTP server listening on ${serverAddress}`)
@ -125,27 +223,38 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void>
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")
await open(openAddress).catch(console.error)
await open(openAddress).catch((error: Error) => {
logger.error("Failed to open", field("address", openAddress), field("error", error))
})
logger.info(`Opened ${openAddress}`)
}
if (args.link) {
try {
await coderCloudBind(serverAddress!, args.link.value)
} catch (err) {
logger.error(err.message)
ipcMain.exit(1)
}
}
}
async function entry(): Promise<void> {
const tryParse = async (): Promise<[Args, 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]
} catch (error) {
console.error(error.message)
process.exit(1)
}
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)
// There's no need to check flags like --help or to spawn in an existing
// instance for the child process because these would have already happened in
// the parent and the child wouldn't have been spawned.
if (ipcMain.isChild) {
await ipcMain.handshake()
ipcMain.preventExit()
return main(args, configArgs)
}
const [args, cliArgs, configArgs] = await tryParse()
if (args.help) {
console.log("code-server", version, commit)
console.log("")
@ -155,7 +264,10 @@ async function entry(): Promise<void> {
optionDescriptions().forEach((description) => {
console.log("", description)
})
} else if (args.version) {
return
}
if (args.version) {
if (args.json) {
console.log({
codeServer: version,
@ -165,83 +277,23 @@ async function entry(): Promise<void> {
} else {
console.log(version, commit)
}
process.exit(0)
} else if (process.env.VSCODE_IPC_HOOK_CLI) {
const pipeArgs: OpenCommandPipeArgs = {
type: "open",
folderURIs: [],
forceReuseWindow: args["reuse-window"],
forceNewWindow: args["new-window"],
}
const isDir = async (path: string): Promise<boolean> => {
try {
const st = await fs.stat(path)
return st.isDirectory()
} catch (error) {
return false
}
}
for (let i = 0; i < args._.length; i++) {
const fp = path.resolve(args._[i])
if (await isDir(fp)) {
pipeArgs.folderURIs.push(fp)
} else {
if (!pipeArgs.fileURIs) {
pipeArgs.fileURIs = []
}
pipeArgs.fileURIs.push(fp)
}
}
if (pipeArgs.forceNewWindow && pipeArgs.fileURIs && 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 || pipeArgs.fileURIs.length === 0)) {
logger.error("Please specify at least one file or folder argument")
process.exit(1)
}
const vscode = http.request(
{
path: "/",
method: "POST",
socketPath: process.env["VSCODE_IPC_HOOK_CLI"],
},
(res) => {
res.on("data", (message) => {
logger.debug("Got message from VS Code", field("message", message.toString()))
})
},
)
vscode.on("error", (err) => {
logger.debug("Got error from VS Code", field("error", err))
})
vscode.write(JSON.stringify(pipeArgs))
vscode.end()
} else if (args["list-extensions"] || args["install-extension"] || args["uninstall-extension"]) {
logger.debug("forking vs code cli...")
const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], {
env: {
...process.env,
CODE_SERVER_PARENT_PID: process.pid.toString(),
},
})
vscode.once("message", (message: any) => {
logger.debug("Got message from VS Code", field("message", message))
if (message.type !== "ready") {
logger.error("Unexpected response waiting for ready response")
process.exit(1)
}
const send: CliMessage = { type: "cli", args }
vscode.send(send)
})
vscode.once("error", (error) => {
logger.error(error.message)
process.exit(1)
})
vscode.on("exit", (code) => process.exit(code || 0))
} else {
wrap(() => main(args, cliArgs, configArgs))
return
}
if (shouldRunVsCodeCli(args)) {
return runVsCodeCli(args)
}
const socketPath = await shouldOpenInExistingInstance(cliArgs)
if (socketPath) {
return openInExistingInstance(args, socketPath)
}
const wrapper = new WrapperProcess(require("../../package.json").version)
return wrapper.start()
}
entry()
entry().catch((error) => {
logger.error(error.message)
ipcMain.exit(error)
})

View File

@ -289,7 +289,7 @@ export abstract class HttpProvider {
/**
* Helper to error if not authorized.
*/
protected ensureAuthenticated(request: http.IncomingMessage): void {
public ensureAuthenticated(request: http.IncomingMessage): void {
if (!this.authenticated(request)) {
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
}
@ -578,14 +578,24 @@ export class HttpServer {
*/
public listen(): Promise<string | null> {
if (!this.listenPromise) {
this.listenPromise = new Promise((resolve, reject) => {
this.listenPromise = new Promise(async (resolve, reject) => {
this.server.on("error", reject)
this.server.on("upgrade", this.onUpgrade)
const onListen = (): void => resolve(this.address())
if (this.options.socket) {
try {
await fs.unlink(this.options.socket)
} catch (err) {
if (err.code !== "ENOENT") {
logger.warn(err.message)
}
}
this.server.listen(this.options.socket, onListen)
} else if (this.options.host) {
// [] is the correct format when using :: but Node errors with them.
this.server.listen(this.options.port, this.options.host.replace(/^\[|\]$/g, ""), onListen)
} else {
this.server.listen(this.options.port, this.options.host, onListen)
this.server.listen(this.options.port, onListen)
}
})
}
@ -647,10 +657,7 @@ export class HttpServer {
}
try {
const payload =
this.maybeRedirect(request, route) ||
(route.provider.authenticated(request) && this.maybeProxy(request)) ||
(await route.provider.handleRequest(route, request))
const payload = (await this.handleRequest(route, request)) || (await route.provider.handleRequest(route, request))
if (payload.proxy) {
this.doProxy(route, request, response, payload.proxy)
} else {
@ -685,15 +692,23 @@ export class HttpServer {
}
/**
* Return any necessary redirection before delegating to a provider.
* Handle requests that are always in effect no matter what provider is
* registered at the route.
*/
private maybeRedirect(request: http.IncomingMessage, route: ProviderRoute): RedirectResponse | undefined {
private async handleRequest(route: ProviderRoute, request: http.IncomingMessage): Promise<HttpResponse | undefined> {
// If we're handling TLS ensure all requests are redirected to HTTPS.
if (this.options.cert && !(request.connection as tls.TLSSocket).encrypted) {
return { redirect: route.fullPath }
}
return undefined
// Return robots.txt.
if (route.fullPath === "/robots.txt") {
const filePath = path.resolve(__dirname, "../../src/browser/robots.txt")
return { content: await fs.readFile(filePath), filePath }
}
// Handle proxy domains.
return this.maybeProxy(route, request)
}
/**
@ -744,7 +759,7 @@ export class HttpServer {
// can't be transferred so we need an in-between).
const socketProxy = await this.socketProvider.createProxy(socket)
const payload =
this.maybeProxy(request) || (await route.provider.handleWebSocket(route, request, socketProxy, head))
this.maybeProxy(route, request) || (await route.provider.handleWebSocket(route, request, socketProxy, head))
if (payload && payload.proxy) {
this.doProxy(route, request, { socket: socketProxy, head }, payload.proxy)
}
@ -894,8 +909,10 @@ export class HttpServer {
*
* For example if `coder.com` is specified `8080.coder.com` will be proxied
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
*
* Throw an error if proxying but the user isn't authenticated.
*/
public maybeProxy(request: http.IncomingMessage): HttpResponse | undefined {
public maybeProxy(route: ProviderRoute, request: http.IncomingMessage): HttpResponse | undefined {
// Split into parts.
const host = request.headers.host || ""
const idx = host.indexOf(":")
@ -909,6 +926,9 @@ export class HttpServer {
return undefined
}
// Must be authenticated to use the proxy.
route.provider.ensureAuthenticated(request)
return {
proxy: {
port,

View File

@ -4,11 +4,15 @@ import * as path from "path"
import * as util from "util"
import { Args } from "./cli"
import { HttpServer } from "./http"
import { paths } from "./util"
/* eslint-disable @typescript-eslint/no-var-requires */
export type Activate = (httpServer: HttpServer, args: Args) => void
/**
* Plugins must implement this interface.
*/
export interface Plugin {
activate: Activate
}
@ -23,38 +27,66 @@ require("module")._load = function (request: string, parent: object, isMain: boo
return originalLoad.apply(this, [request.replace(/^code-server/, path.resolve(__dirname, "../..")), parent, isMain])
}
/**
* Load a plugin and run its activation function.
*/
const loadPlugin = async (pluginPath: string, httpServer: HttpServer, args: Args): Promise<void> => {
try {
const plugin: Plugin = require(pluginPath)
plugin.activate(httpServer, args)
logger.debug("Loaded plugin", field("name", path.basename(pluginPath)))
const packageJson = require(path.join(pluginPath, "package.json"))
logger.debug(
"Loaded plugin",
field("name", packageJson.name || path.basename(pluginPath)),
field("path", pluginPath),
field("version", packageJson.version || "n/a"),
)
} catch (error) {
if (error.code !== "MODULE_NOT_FOUND") {
logger.warn(error.message)
} else {
logger.error(error.message)
}
logger.error(error.message)
}
}
const _loadPlugins = async (httpServer: HttpServer, args: Args): Promise<void> => {
const pluginPath = path.resolve(__dirname, "../../plugins")
const files = await util.promisify(fs.readdir)(pluginPath, {
withFileTypes: true,
})
await Promise.all(files.map((file) => loadPlugin(path.join(pluginPath, file.name), httpServer, args)))
}
export const loadPlugins = async (httpServer: HttpServer, args: Args): Promise<void> => {
/**
* Load all plugins in the specified directory.
*/
const _loadPlugins = async (pluginDir: string, httpServer: HttpServer, args: Args): Promise<void> => {
try {
await _loadPlugins(httpServer, args)
const files = await util.promisify(fs.readdir)(pluginDir, {
withFileTypes: true,
})
await Promise.all(files.map((file) => loadPlugin(path.join(pluginDir, file.name), httpServer, args)))
} catch (error) {
if (error.code !== "ENOENT") {
logger.warn(error.message)
}
}
if (process.env.PLUGIN_DIR) {
await loadPlugin(process.env.PLUGIN_DIR, httpServer, args)
}
}
/**
* Load all plugins from the `plugins` directory, directories specified by
* `CS_PLUGIN_PATH` (colon-separated), and individual plugins specified by
* `CS_PLUGIN` (also colon-separated).
*/
export const loadPlugins = async (httpServer: HttpServer, args: Args): Promise<void> => {
const pluginPath = process.env.CS_PLUGIN_PATH || `${path.join(paths.data, "plugins")}:/usr/share/code-server/plugins`
const plugin = process.env.CS_PLUGIN || ""
await Promise.all([
// Built-in plugins.
_loadPlugins(path.resolve(__dirname, "../../plugins"), httpServer, args),
// User-added plugins.
...pluginPath
.split(":")
.filter((p) => !!p)
.map((dir) => _loadPlugins(path.resolve(dir), httpServer, args)),
// Individual plugins so you don't have to symlink or move them into a
// directory specifically for plugins. This lets you load plugins that are
// on the same level as other directories that are not plugins (if you tried
// to use CS_PLUGIN_PATH code-server would try to load those other
// directories as plugins). Intended for development.
...plugin
.split(":")
.filter((p) => !!p)
.map((dir) => loadPlugin(path.resolve(dir), httpServer, args)),
])
}

View File

@ -4,7 +4,7 @@ import * as path from "path"
import * as tls from "tls"
import { Emitter } from "../common/emitter"
import { generateUuid } from "../common/util"
import { tmpdir } from "./util"
import { canConnect, tmpdir } from "./util"
/**
* Provides a way to proxy a TLS socket. Can be used when you need to pass a
@ -89,17 +89,6 @@ export class SocketProxyProvider {
}
public async findFreeSocketPath(basePath: string, maxTries = 100): Promise<string> {
const canConnect = (path: string): Promise<boolean> => {
return new Promise((resolve) => {
const socket = net.connect(path)
socket.once("error", () => resolve(false))
socket.once("connect", () => {
socket.destroy()
resolve(true)
})
})
}
let i = 0
let path = basePath
while ((await canConnect(path)) && i < maxTries) {

View File

@ -2,6 +2,7 @@ import * as cp from "child_process"
import * as crypto from "crypto"
import envPaths from "env-paths"
import * as fs from "fs-extra"
import * as net from "net"
import * as os from "os"
import * as path from "path"
import * as util from "util"
@ -246,3 +247,17 @@ export function pathToFsPath(path: string, keepDriveLetterCasing = false): strin
}
return value
}
/**
* Return a promise that resolves with whether the socket path is active.
*/
export function canConnect(path: string): Promise<boolean> {
return new Promise((resolve) => {
const socket = net.connect(path)
socket.once("error", () => resolve(false))
socket.once("connect", () => {
socket.destroy()
resolve(true)
})
})
}

View File

@ -32,19 +32,13 @@ export class IpcMain {
public readonly onMessage = this._onMessage.event
private readonly _onDispose = new Emitter<NodeJS.Signals | undefined>()
public readonly onDispose = this._onDispose.event
public readonly processExit: (code?: number) => never
public readonly processExit: (code?: number) => never = process.exit
public constructor(public readonly parentPid?: number) {
public constructor(private readonly parentPid?: number) {
process.on("SIGINT", () => this._onDispose.emit("SIGINT"))
process.on("SIGTERM", () => this._onDispose.emit("SIGTERM"))
process.on("exit", () => this._onDispose.emit(undefined))
// Ensure we control when the process exits.
this.processExit = process.exit
process.exit = function (code?: number) {
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
} as (code?: number) => never
this.onDispose((signal) => {
// Remove listeners to avoid possibly triggering disposal again.
process.removeAllListeners()
@ -71,6 +65,19 @@ export class IpcMain {
}
}
/**
* Ensure we control when the process exits.
*/
public preventExit(): void {
process.exit = function (code?: number) {
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
} as (code?: number) => never
}
public get isChild(): boolean {
return typeof this.parentPid !== "undefined"
}
public exit(error?: number | ProcessError): never {
if (error && typeof error !== "number") {
this.processExit(typeof error.code === "number" ? error.code : 1)
@ -127,17 +134,12 @@ export class IpcMain {
}
}
let _ipcMain: IpcMain
export const ipcMain = (): IpcMain => {
if (!_ipcMain) {
_ipcMain = new IpcMain(
typeof process.env.CODE_SERVER_PARENT_PID !== "undefined"
? parseInt(process.env.CODE_SERVER_PARENT_PID)
: undefined,
)
}
return _ipcMain
}
/**
* Channel for communication between the child and parent processes.
*/
export const ipcMain = new IpcMain(
typeof process.env.CODE_SERVER_PARENT_PID !== "undefined" ? parseInt(process.env.CODE_SERVER_PARENT_PID) : undefined,
)
export interface WrapperOptions {
maxMemory?: number
@ -162,14 +164,11 @@ export class WrapperProcess {
this.logStdoutStream = rfs.createStream(path.join(paths.data, "coder-logs", "code-server-stdout.log"), opts)
this.logStderrStream = rfs.createStream(path.join(paths.data, "coder-logs", "code-server-stderr.log"), opts)
ipcMain().onDispose(() => {
if (this.process) {
this.process.removeAllListeners()
this.process.kill()
}
ipcMain.onDispose(() => {
this.disposeChild()
})
ipcMain().onMessage((message) => {
ipcMain.onMessage((message) => {
switch (message.type) {
case "relaunch":
logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`)
@ -181,55 +180,65 @@ export class WrapperProcess {
break
}
})
process.on("SIGUSR1", async () => {
logger.info("Received SIGUSR1; hotswapping")
this.relaunch()
})
}
private async relaunch(): Promise<void> {
private disposeChild(): void {
this.started = undefined
if (this.process) {
this.process.removeAllListeners()
this.process.kill()
}
}
private async relaunch(): Promise<void> {
this.disposeChild()
try {
await this.start()
} catch (error) {
logger.error(error.message)
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
ipcMain.exit(typeof error.code === "number" ? error.code : 1)
}
}
public start(): Promise<void> {
if (!this.started) {
this.started = this.spawn().then((child) => {
// Log both to stdout and to the log directory.
if (child.stdout) {
child.stdout.pipe(this.logStdoutStream)
child.stdout.pipe(process.stdout)
}
if (child.stderr) {
child.stderr.pipe(this.logStderrStream)
child.stderr.pipe(process.stderr)
}
logger.debug(`spawned inner process ${child.pid}`)
ipcMain()
.handshake(child)
.then(() => {
child.once("exit", (code) => {
logger.debug(`inner process ${child.pid} exited unexpectedly`)
ipcMain().exit(code || 0)
})
})
this.process = child
// If we have a process then we've already bound this.
if (!this.process) {
process.on("SIGUSR1", async () => {
logger.info("Received SIGUSR1; hotswapping")
this.relaunch()
})
}
if (!this.started) {
this.started = this._start()
}
return this.started
}
private async spawn(): Promise<cp.ChildProcess> {
private async _start(): Promise<void> {
const child = this.spawn()
this.process = child
// Log both to stdout and to the log directory.
if (child.stdout) {
child.stdout.pipe(this.logStdoutStream)
child.stdout.pipe(process.stdout)
}
if (child.stderr) {
child.stderr.pipe(this.logStderrStream)
child.stderr.pipe(process.stderr)
}
logger.debug(`spawned inner process ${child.pid}`)
await ipcMain.handshake(child)
child.once("exit", (code) => {
logger.debug(`inner process ${child.pid} exited unexpectedly`)
ipcMain.exit(code || 0)
})
}
private spawn(): cp.ChildProcess {
// Flags to pass along to the Node binary.
let nodeOptions = `${process.env.NODE_OPTIONS || ""} ${(this.options && this.options.nodeOptions) || ""}`
if (!/max_old_space_size=(\d+)/g.exec(nodeOptions)) {
@ -251,23 +260,13 @@ export class WrapperProcess {
// It's possible that the pipe has closed (for example if you run code-server
// --version | head -1). Assume that means we're done.
if (!process.stdout.isTTY) {
process.stdout.on("error", () => ipcMain().exit())
process.stdout.on("error", () => ipcMain.exit())
}
export const wrap = (fn: () => Promise<void>): void => {
if (ipcMain().parentPid) {
ipcMain()
.handshake()
.then(() => fn())
.catch((error: ProcessError): void => {
logger.error(error.message)
ipcMain().exit(error)
})
} else {
const wrapper = new WrapperProcess(require("../../package.json").version)
wrapper.start().catch((error) => {
logger.error(error.message)
ipcMain().exit(error)
})
// Don't let uncaught exceptions crash the process.
process.on("uncaughtException", (error) => {
logger.error(`Uncaught exception: ${error.message}`)
if (typeof error.stack !== "undefined") {
logger.error(error.stack)
}
}
})

View File

@ -1,20 +1,31 @@
import { logger, Level } from "@coder/logger"
import { Level, logger } from "@coder/logger"
import * as assert from "assert"
import * as fs from "fs-extra"
import * as net from "net"
import * as os from "os"
import * as path from "path"
import { parse } from "../src/node/cli"
import { Args, parse, setDefaults, shouldOpenInExistingInstance } from "../src/node/cli"
import { paths, tmpdir } from "../src/node/util"
describe("cli", () => {
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}
describe("parser", () => {
beforeEach(() => {
delete process.env.LOG_LEVEL
})
// The parser will always fill these out.
// The parser should not set any defaults so the caller can determine what
// values the user actually set. These are only set after explicitly calling
// `setDefaults`.
const defaults = {
_: [],
"extensions-dir": path.join(paths.data, "extensions"),
"user-data-dir": paths.data,
}
it("should set defaults", () => {
assert.deepEqual(parse([]), defaults)
assert.deepEqual(parse([]), { _: [] })
})
it("should parse all available options", () => {
@ -69,7 +80,7 @@ describe("cli", () => {
help: true,
host: "0.0.0.0",
json: true,
log: "trace",
log: "error",
open: true,
port: 8081,
socket: path.resolve("mumble"),
@ -83,19 +94,20 @@ describe("cli", () => {
it("should work with short options", () => {
assert.deepEqual(parse(["-vvv", "-v"]), {
...defaults,
log: "trace",
_: [],
verbose: true,
version: true,
})
assert.equal(process.env.LOG_LEVEL, "trace")
assert.equal(logger.level, Level.Trace)
})
it("should use log level env var", () => {
it("should use log level env var", async () => {
const args = parse([])
assert.deepEqual(args, { _: [] })
process.env.LOG_LEVEL = "debug"
assert.deepEqual(parse([]), {
assert.deepEqual(await setDefaults(args), {
...defaults,
_: [],
log: "debug",
verbose: false,
})
@ -103,8 +115,9 @@ describe("cli", () => {
assert.equal(logger.level, Level.Debug)
process.env.LOG_LEVEL = "trace"
assert.deepEqual(parse([]), {
assert.deepEqual(await setDefaults(args), {
...defaults,
_: [],
log: "trace",
verbose: true,
})
@ -113,9 +126,16 @@ describe("cli", () => {
})
it("should prefer --log to env var and --verbose to --log", async () => {
let args = parse(["--log", "info"])
assert.deepEqual(args, {
_: [],
log: "info",
})
process.env.LOG_LEVEL = "debug"
assert.deepEqual(parse(["--log", "info"]), {
assert.deepEqual(await setDefaults(args), {
...defaults,
_: [],
log: "info",
verbose: false,
})
@ -123,17 +143,26 @@ describe("cli", () => {
assert.equal(logger.level, Level.Info)
process.env.LOG_LEVEL = "trace"
assert.deepEqual(parse(["--log", "info"]), {
assert.deepEqual(await setDefaults(args), {
...defaults,
_: [],
log: "info",
verbose: false,
})
assert.equal(process.env.LOG_LEVEL, "info")
assert.equal(logger.level, Level.Info)
args = parse(["--log", "info", "--verbose"])
assert.deepEqual(args, {
_: [],
log: "info",
verbose: true,
})
process.env.LOG_LEVEL = "warn"
assert.deepEqual(parse(["--log", "info", "--verbose"]), {
assert.deepEqual(await setDefaults(args), {
...defaults,
_: [],
log: "trace",
verbose: true,
})
@ -141,9 +170,12 @@ describe("cli", () => {
assert.equal(logger.level, Level.Trace)
})
it("should ignore invalid log level env var", () => {
it("should ignore invalid log level env var", async () => {
process.env.LOG_LEVEL = "bogus"
assert.deepEqual(parse([]), defaults)
assert.deepEqual(await setDefaults(parse([])), {
_: [],
...defaults,
})
})
it("should error if value isn't provided", () => {
@ -166,7 +198,7 @@ describe("cli", () => {
it("should not error if the value is optional", () => {
assert.deepEqual(parse(["--cert"]), {
...defaults,
_: [],
cert: {
value: undefined,
},
@ -177,7 +209,7 @@ describe("cli", () => {
assert.throws(() => parse(["--socket", "--socket-path-value"]), /--socket requires a value/)
// If you actually had a path like this you would do this instead:
assert.deepEqual(parse(["--socket", "./--socket-path-value"]), {
...defaults,
_: [],
socket: path.resolve("--socket-path-value"),
})
assert.throws(() => parse(["--cert", "--socket-path-value"]), /Unknown option --socket-path-value/)
@ -185,7 +217,6 @@ describe("cli", () => {
it("should allow positional arguments before options", () => {
assert.deepEqual(parse(["foo", "test", "--auth", "none"]), {
...defaults,
_: ["foo", "test"],
auth: "none",
})
@ -193,12 +224,85 @@ describe("cli", () => {
it("should support repeatable flags", () => {
assert.deepEqual(parse(["--proxy-domain", "*.coder.com"]), {
...defaults,
_: [],
"proxy-domain": ["*.coder.com"],
})
assert.deepEqual(parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), {
...defaults,
_: [],
"proxy-domain": ["*.coder.com", "test.com"],
})
})
})
describe("cli", () => {
let args: Mutable<Args> = { _: [] }
const testDir = path.join(tmpdir, "tests/cli")
const vscodeIpcPath = path.join(os.tmpdir(), "vscode-ipc")
before(async () => {
await fs.remove(testDir)
await fs.mkdirp(testDir)
})
beforeEach(async () => {
delete process.env.VSCODE_IPC_HOOK_CLI
args = { _: [] }
await fs.remove(vscodeIpcPath)
})
it("should use existing if inside code-server", async () => {
process.env.VSCODE_IPC_HOOK_CLI = "test"
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
args.port = 8081
args._.push("./file")
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
})
it("should use existing if --reuse-window is set", async () => {
args["reuse-window"] = true
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
await fs.writeFile(vscodeIpcPath, "test")
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
args.port = 8081
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
})
it("should use existing if --new-window is set", async () => {
args["new-window"] = true
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
await fs.writeFile(vscodeIpcPath, "test")
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
args.port = 8081
assert.strictEqual(await shouldOpenInExistingInstance(args), "test")
})
it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => {
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
args._.push("./file")
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
const socketPath = path.join(testDir, "socket")
await fs.writeFile(vscodeIpcPath, socketPath)
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
await new Promise((resolve) => {
const server = net.createServer(() => {
// Close after getting the first connection.
server.close()
})
server.once("listening", () => resolve(server))
server.listen(socketPath)
})
assert.strictEqual(await shouldOpenInExistingInstance(args), socketPath)
args.port = 8081
assert.strictEqual(await shouldOpenInExistingInstance(args), undefined)
})
})

View File

@ -8,6 +8,7 @@ import { SettingsProvider, UpdateSettings } from "../src/node/settings"
import { tmpdir } from "../src/node/util"
describe("update", () => {
return
let version = "1.0.0"
let spy: string[] = []
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {

View File

@ -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"