Merge branch 'master' into feature/helm3
This commit is contained in:
commit
d7cba30c6a
@ -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
7
.github/ISSUE_TEMPLATE/doc.md
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Documentation improvement
|
||||
about: Suggest a documentation improvement
|
||||
title: ""
|
||||
labels: "docs"
|
||||
assignees: ""
|
||||
---
|
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@ -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
|
||||
|
4
.github/workflows/publish.yaml
vendored
4
.github/workflows/publish.yaml
vendored
@ -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
2
.gitignore
vendored
@ -11,3 +11,5 @@ release-images/
|
||||
node_modules
|
||||
node-*
|
||||
/plugins
|
||||
/lib/coder-cloud-agent
|
||||
.home
|
||||
|
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,3 +1,4 @@
|
||||
[submodule "lib/vscode"]
|
||||
path = lib/vscode
|
||||
url = https://github.com/microsoft/vscode
|
||||
ignore = dirty
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
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"
|
||||
|
@ -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
|
||||
|
@ -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 "$@"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 "$@"
|
||||
|
@ -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}")" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
@ -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==
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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", "."]
|
||||
|
@ -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 "$@"
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
15
doc/FAQ.md
15
doc/FAQ.md
@ -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
|
||||
|
@ -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
|
||||
```
|
||||
|
||||
|
52
install.sh
52
install.sh
@ -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
|
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
2
src/browser/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
@ -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
|
||||
}
|
||||
}
|
||||
|
146
src/node/cli.ts
146
src/node/cli.ts
@ -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
43
src/node/coder-cloud.ts
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
entry()
|
||||
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().catch((error) => {
|
||||
logger.error(error.message)
|
||||
ipcMain.exit(error)
|
||||
})
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _loadPlugins = async (httpServer: HttpServer, args: Args): Promise<void> => {
|
||||
const pluginPath = path.resolve(__dirname, "../../plugins")
|
||||
const files = await util.promisify(fs.readdir)(pluginPath, {
|
||||
/**
|
||||
* Load all plugins in the specified directory.
|
||||
*/
|
||||
const _loadPlugins = async (pluginDir: string, httpServer: HttpServer, args: Args): Promise<void> => {
|
||||
try {
|
||||
const files = await util.promisify(fs.readdir)(pluginDir, {
|
||||
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> => {
|
||||
try {
|
||||
await _loadPlugins(httpServer, args)
|
||||
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)),
|
||||
])
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -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,
|
||||
/**
|
||||
* 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,
|
||||
)
|
||||
}
|
||||
return _ipcMain
|
||||
}
|
||||
|
||||
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,30 +180,44 @@ 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 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.spawn().then((child) => {
|
||||
this.started = this._start()
|
||||
}
|
||||
return this.started
|
||||
}
|
||||
|
||||
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)
|
||||
@ -214,22 +227,18 @@ export class WrapperProcess {
|
||||
child.stderr.pipe(this.logStderrStream)
|
||||
child.stderr.pipe(process.stderr)
|
||||
}
|
||||
|
||||
logger.debug(`spawned inner process ${child.pid}`)
|
||||
ipcMain()
|
||||
.handshake(child)
|
||||
.then(() => {
|
||||
|
||||
await ipcMain.handshake(child)
|
||||
|
||||
child.once("exit", (code) => {
|
||||
logger.debug(`inner process ${child.pid} exited unexpectedly`)
|
||||
ipcMain().exit(code || 0)
|
||||
ipcMain.exit(code || 0)
|
||||
})
|
||||
})
|
||||
this.process = child
|
||||
})
|
||||
}
|
||||
return this.started
|
||||
}
|
||||
|
||||
private async spawn(): Promise<cp.ChildProcess> {
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
152
test/cli.test.ts
152
test/cli.test.ts
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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) => {
|
||||
|
16
yarn.lock
16
yarn.lock
@ -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"
|
||||
|
Reference in New Issue
Block a user