chore: move to patches (#4997)
* Move integration types into code-server This will be easier to maintain than to have it as a patch. * Disable connection token Using a flag means we will not need to patch it out. I think this is new from 1.64? * Add product.json to build process This way we do not have to patch it. * Ship with remote agent package.json Instead of the root one. This contains fewer dependencies. * Let Code handle errors This way we will not have to patch Code to make this work and I think it makes sense to let Code handle the request. If we do want to handle errors we can do it cleanly by patching their error handler to throw instead. * Move manifest override into code-server This way we will not have to patch it. * Move to patches - Switch submodule to track upstream - Add quilt to the process - Add patches The node-* ignore was ignoring one of the diffs so I removed it. This was added when we were curling Node as node-v{version}-darwin-x64 for the macOS build but this no longer happens (we use the Node action to install a specific version now so we just use the system-wide Node). * Use pre-packaged Code
This commit is contained in:
parent
be727871f6
commit
a1af9e2a56
25
.github/workflows/ci.yaml
vendored
25
.github/workflows/ci.yaml
vendored
@ -110,6 +110,12 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install quilt
|
||||||
|
run: sudo apt update && sudo apt install quilt
|
||||||
|
|
||||||
|
- name: Patch Code
|
||||||
|
run: quilt push -a
|
||||||
|
|
||||||
- name: Install Node.js v14
|
- name: Install Node.js v14
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
@ -132,25 +138,22 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
# Get Code's git hash. When this changes it means the content is
|
# Get Code's git hash. When this changes it means the content is
|
||||||
# different and we need to rebuild. Use VSCODE_CACHE_VERSION to force a
|
# different and we need to rebuild.
|
||||||
# rebuild.
|
|
||||||
- name: Get latest lib/vscode rev
|
- name: Get latest lib/vscode rev
|
||||||
id: vscode-rev
|
id: vscode-rev
|
||||||
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)"
|
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)"
|
||||||
|
|
||||||
- name: Fetch Code build from cache
|
# We need to rebuild when we have a new version of Code or when any of
|
||||||
id: cache-vscode-2
|
# the patches changed. Use VSCODE_CACHE_VERSION to force a rebuild.
|
||||||
|
- name: Fetch prebuilt Code package from cache
|
||||||
|
id: cache-vscode
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: lib/vscode-reh-web-*
|
||||||
lib/vscode/.build
|
key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ hashFiles('patches/*.diff') }}
|
||||||
lib/vscode/out-build
|
|
||||||
lib/vscode/out-vscode-reh-web
|
|
||||||
lib/vscode/out-vscode-reh-web-min
|
|
||||||
key: vscode-reh-build-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}
|
|
||||||
|
|
||||||
- name: Build vscode
|
- name: Build vscode
|
||||||
if: steps.cache-vscode-2.outputs.cache-hit != 'true'
|
if: steps.cache-vscode.outputs.cache-hit != 'true'
|
||||||
run: yarn build:vscode
|
run: yarn build:vscode
|
||||||
|
|
||||||
# Our code imports code from VS Code's `out` directory meaning VS Code
|
# Our code imports code from VS Code's `out` directory meaning VS Code
|
||||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -8,12 +8,17 @@ release-packages/
|
|||||||
release-gcp/
|
release-gcp/
|
||||||
release-images/
|
release-images/
|
||||||
node_modules
|
node_modules
|
||||||
vendor/modules
|
|
||||||
node-*
|
|
||||||
/plugins
|
/plugins
|
||||||
/lib/coder-cloud-agent
|
/lib/coder-cloud-agent
|
||||||
.home
|
.home
|
||||||
coverage
|
coverage
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
|
# Code packages itself here.
|
||||||
|
/lib/vscode-reh-web-*
|
||||||
|
|
||||||
# Failed e2e test videos are saved here
|
# Failed e2e test videos are saved here
|
||||||
test/test-results
|
test/test-results
|
||||||
|
|
||||||
|
# Quilt's internal data.
|
||||||
|
/.pc
|
||||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "lib/vscode"]
|
[submodule "lib/vscode"]
|
||||||
path = lib/vscode
|
path = lib/vscode
|
||||||
url = https://github.com/coder/vscode
|
url = https://github.com/microsoft/vscode
|
||||||
|
@ -50,6 +50,11 @@ release_nfpm() {
|
|||||||
|
|
||||||
export NFPM_ARCH
|
export NFPM_ARCH
|
||||||
|
|
||||||
|
# Code deletes some files from the extension node_modules directory which
|
||||||
|
# leaves broken symlinks in the corresponding .bin directory. nfpm will fail
|
||||||
|
# on these broken symlinks so clean them up.
|
||||||
|
rm -fr "./release-standalone/lib/vscode/extensions/node_modules/.bin"
|
||||||
|
|
||||||
PKG_FORMAT="deb"
|
PKG_FORMAT="deb"
|
||||||
NFPM_ARCH="$(get_nfpm_arch $PKG_FORMAT "$ARCH")"
|
NFPM_ARCH="$(get_nfpm_arch $PKG_FORMAT "$ARCH")"
|
||||||
nfpm_config="$(envsubst < ./ci/build/nfpm.yaml)"
|
nfpm_config="$(envsubst < ./ci/build/nfpm.yaml)"
|
||||||
|
@ -66,31 +66,10 @@ EOF
|
|||||||
|
|
||||||
bundle_vscode() {
|
bundle_vscode() {
|
||||||
mkdir -p "$VSCODE_OUT_PATH"
|
mkdir -p "$VSCODE_OUT_PATH"
|
||||||
rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH"
|
rsync ./lib/vscode-reh-web-*/ "$VSCODE_OUT_PATH"
|
||||||
rsync "$VSCODE_SRC_PATH/out-vscode-reh-web${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out"
|
|
||||||
|
|
||||||
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
|
# Add the commit, date, our name, links, and enable telemetry. This just makes
|
||||||
if [ "$KEEP_MODULES" = 0 ]; then
|
# telemetry available; telemetry can still be disabled by flag or setting.
|
||||||
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"
|
|
||||||
|
|
||||||
mkdir -p "$VSCODE_OUT_PATH/resources/"
|
|
||||||
rsync "$VSCODE_SRC_PATH/resources/" "$VSCODE_OUT_PATH/resources/"
|
|
||||||
|
|
||||||
# TODO: We should look into using VS Code's packaging task (see
|
|
||||||
# gulpfile.reh.js). For now copy this directory into the right spot (for some
|
|
||||||
# reason VS Code uses a different path in production).
|
|
||||||
mkdir -p "$VSCODE_OUT_PATH/bin/helpers"
|
|
||||||
rsync "$VSCODE_SRC_PATH/resources/server/bin/helpers/" "$VSCODE_OUT_PATH/bin/helpers"
|
|
||||||
chmod +x "$VSCODE_OUT_PATH/bin/helpers/browser.sh"
|
|
||||||
|
|
||||||
# Add the commit and date and enable telemetry. This just makes telemetry
|
|
||||||
# available; telemetry can still be disabled by flag or setting.
|
|
||||||
jq --slurp '.[0] * .[1]' "$VSCODE_SRC_PATH/product.json" <(
|
jq --slurp '.[0] * .[1]' "$VSCODE_SRC_PATH/product.json" <(
|
||||||
cat << EOF
|
cat << EOF
|
||||||
{
|
{
|
||||||
@ -98,15 +77,48 @@ bundle_vscode() {
|
|||||||
"commit": "$(cd "$VSCODE_SRC_PATH" && git rev-parse HEAD)",
|
"commit": "$(cd "$VSCODE_SRC_PATH" && git rev-parse HEAD)",
|
||||||
"quality": "stable",
|
"quality": "stable",
|
||||||
"date": $(jq -n 'now | todate'),
|
"date": $(jq -n 'now | todate'),
|
||||||
"codeServerVersion": "$VERSION"
|
"codeServerVersion": "$VERSION",
|
||||||
|
"nameShort": "code-server",
|
||||||
|
"nameLong": "code-server",
|
||||||
|
"applicationName": "code-server",
|
||||||
|
"dataFolderName": ".code-server",
|
||||||
|
"win32MutexName": "codeserver",
|
||||||
|
"licenseUrl": "https://github.com/coder/code-server/blob/main/LICENSE.txt",
|
||||||
|
"win32DirName": "code-server",
|
||||||
|
"win32NameVersion": "code-server",
|
||||||
|
"win32AppUserModelId": "coder.code-server",
|
||||||
|
"win32ShellNameShort": "c&ode-server",
|
||||||
|
"darwinBundleIdentifier": "com.coder.code.server",
|
||||||
|
"linuxIconName": "com.coder.code.server",
|
||||||
|
"reportIssueUrl": "https://github.com/coder/code-server/issues/new",
|
||||||
|
"documentationUrl": "https://go.microsoft.com/fwlink/?LinkID=533484#vscode",
|
||||||
|
"keyboardShortcutsUrlMac": "https://go.microsoft.com/fwlink/?linkid=832143",
|
||||||
|
"keyboardShortcutsUrlLinux": "https://go.microsoft.com/fwlink/?linkid=832144",
|
||||||
|
"keyboardShortcutsUrlWin": "https://go.microsoft.com/fwlink/?linkid=832145",
|
||||||
|
"introductoryVideosUrl": "https://go.microsoft.com/fwlink/?linkid=832146",
|
||||||
|
"tipsAndTricksUrl": "https://go.microsoft.com/fwlink/?linkid=852118",
|
||||||
|
"newsletterSignupUrl": "https://www.research.net/r/vsc-newsletter",
|
||||||
|
"linkProtectionTrustedDomains": [
|
||||||
|
"https://open-vsx.org"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
) > "$VSCODE_OUT_PATH/product.json"
|
) > "$VSCODE_OUT_PATH/product.json"
|
||||||
|
|
||||||
# We remove the scripts field so that later on we can run
|
# Use the package.json for the web/remote server. It does not have the right
|
||||||
# yarn to fetch node_modules if necessary without build scripts running.
|
# version though so pull that from the main package.json. Also remove keytar
|
||||||
# We cannot use --no-scripts because we still want dependent package scripts to run.
|
# since the web does not rely on it and that removes the dependency on
|
||||||
jq 'del(.scripts)' < "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json"
|
# libsecret.
|
||||||
|
jq --slurp '.[0] * {version: .[1].version} | del(.dependencies.keytar)' \
|
||||||
|
"$VSCODE_SRC_PATH/remote/package.json" \
|
||||||
|
"$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json"
|
||||||
|
|
||||||
|
rsync "$VSCODE_SRC_PATH/remote/yarn.lock" "$VSCODE_OUT_PATH/yarn.lock"
|
||||||
|
|
||||||
|
if [ "$KEEP_MODULES" = 0 ]; then
|
||||||
|
rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules"
|
||||||
|
rm -Rf "$VSCODE_OUT_PATH/node_modules"
|
||||||
|
fi
|
||||||
|
|
||||||
pushd "$VSCODE_OUT_PATH"
|
pushd "$VSCODE_OUT_PATH"
|
||||||
symlink_asar
|
symlink_asar
|
||||||
|
@ -29,11 +29,6 @@ main() {
|
|||||||
|
|
||||||
cd "$RELEASE_PATH"
|
cd "$RELEASE_PATH"
|
||||||
yarn --production --frozen-lockfile
|
yarn --production --frozen-lockfile
|
||||||
|
|
||||||
# HACK: the version of Typescript vscode 1.57 uses in extensions/
|
|
||||||
# leaves a few stray symlinks. Clean them up so nfpm does not fail.
|
|
||||||
# Remove this line when its no longer needed.
|
|
||||||
rm -fr "$RELEASE_PATH/lib/vscode/extensions/node_modules/.bin"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
@ -14,11 +14,17 @@ main() {
|
|||||||
# Our code imports from `out` in order to work during development but if you
|
# Our code imports from `out` in order to work during development but if you
|
||||||
# have only built for production you will have not have this directory. In
|
# have only built for production you will have not have this directory. In
|
||||||
# that case symlink `out` to a production build directory.
|
# that case symlink `out` to a production build directory.
|
||||||
local vscode="lib/vscode"
|
if [[ ! -e lib/vscode/out ]]; then
|
||||||
local link="$vscode/out"
|
pushd lib
|
||||||
local target="out-build"
|
local out=(vscode-reh-web-*)
|
||||||
if [[ ! -e $link ]] && [[ -d $vscode/$target ]]; then
|
if [[ -d "${out[0]}" ]]; then
|
||||||
ln -s "$target" "$link"
|
ln -s "../${out[0]}/out" ./vscode/out
|
||||||
|
else
|
||||||
|
echo "Could not find lib/vscode/out or lib/vscode-reh-web-*"
|
||||||
|
echo "Code must be built before running unit tests"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# We must keep jest in a sub-directory. See ../../test/package.json for more
|
# We must keep jest in a sub-directory. See ../../test/package.json for more
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
- [Creating pull requests](#creating-pull-requests)
|
- [Creating pull requests](#creating-pull-requests)
|
||||||
- [Commits and commit history](#commits-and-commit-history)
|
- [Commits and commit history](#commits-and-commit-history)
|
||||||
- [Development workflow](#development-workflow)
|
- [Development workflow](#development-workflow)
|
||||||
- [Updates to VS Code](#updates-to-vs-code)
|
- [Version updates to Code](#version-updates-to-code)
|
||||||
|
- [Patching Code](#patching-code)
|
||||||
- [Build](#build)
|
- [Build](#build)
|
||||||
- [Help](#help)
|
- [Help](#help)
|
||||||
- [Test](#test)
|
- [Test](#test)
|
||||||
@ -16,7 +17,7 @@
|
|||||||
- [Integration tests](#integration-tests)
|
- [Integration tests](#integration-tests)
|
||||||
- [End-to-end tests](#end-to-end-tests)
|
- [End-to-end tests](#end-to-end-tests)
|
||||||
- [Structure](#structure)
|
- [Structure](#structure)
|
||||||
- [Modifications to VS Code](#modifications-to-vs-code)
|
- [Modifications to Code](#modifications-to-code)
|
||||||
- [Currently Known Issues](#currently-known-issues)
|
- [Currently Known Issues](#currently-known-issues)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
@ -44,6 +45,8 @@ Here is what is needed:
|
|||||||
signature
|
signature
|
||||||
verification](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification)
|
verification](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification)
|
||||||
or follow [this tutorial](https://joeprevite.com/verify-commits-on-github)
|
or follow [this tutorial](https://joeprevite.com/verify-commits-on-github)
|
||||||
|
- `quilt`
|
||||||
|
- Used to manage patches to Code
|
||||||
- `rsync` and `unzip`
|
- `rsync` and `unzip`
|
||||||
- Used for code-server releases
|
- Used for code-server releases
|
||||||
- `bats`
|
- `bats`
|
||||||
@ -57,7 +60,7 @@ If you're developing code-server on Linux, make sure you have installed or insta
|
|||||||
sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev python-is-python3
|
sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev python-is-python3
|
||||||
```
|
```
|
||||||
|
|
||||||
These are required by VS Code. See [their Wiki](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites) for more information.
|
These are required by Code. See [their Wiki](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites) for more information.
|
||||||
|
|
||||||
## Creating pull requests
|
## Creating pull requests
|
||||||
|
|
||||||
@ -78,26 +81,44 @@ we'll guide you.
|
|||||||
|
|
||||||
## Development workflow
|
## Development workflow
|
||||||
|
|
||||||
The current development workflow is a bit tricky because we have this repo and we use our `coder/vscode` fork inside it with [`yarn link`](https://classic.yarnpkg.com/lang/en/docs/cli/link/).
|
|
||||||
|
|
||||||
Here are these steps you should follow to get your dev environment setup:
|
|
||||||
|
|
||||||
1. `git clone https://github.com/coder/code-server.git` - Clone `code-server`
|
1. `git clone https://github.com/coder/code-server.git` - Clone `code-server`
|
||||||
2. `git submodule update --init` - Clone `vscode` submodule
|
2. `git submodule update --init` - Clone `vscode` submodule
|
||||||
3. `yarn` - Install dependencies
|
3. `quilt push -a` - Apply patches to the `vscode` submodule.
|
||||||
4. `yarn watch` - This will spin up code-server on localhost:8080 which you can start developing. It will live reload changes to the source.
|
4. `yarn` - Install dependencies
|
||||||
|
5. `yarn watch` - Launch code-server localhost:8080. code-server will be live
|
||||||
|
reloaded when changes are made; the browser needs to be refreshed manually.
|
||||||
|
|
||||||
### Updates to VS Code
|
When pulling down changes that include modifications to the patches you will
|
||||||
|
need to apply them with `quilt`. If you pull down changes that update the
|
||||||
|
`vscode` submodule you will need to run `git submodule update --init` and
|
||||||
|
re-apply the patches.
|
||||||
|
|
||||||
If changes are made and merged into `main` in the [`coder/vscode`](https://github.com/coder/vscode) repo, then you'll need to update the version in the `code-server` repo by following these steps:
|
### Version updates to Code
|
||||||
|
|
||||||
1. Update the `lib/vscode` submodule to the latest `main`.
|
1. Update the `lib/vscode` submodule to the desired upstream version branch.
|
||||||
2. From the code-server **project root**, run `yarn install`.
|
2. From the code-server **project root**, run `yarn install`.
|
||||||
3. Test code-server locally to make sure everything works.
|
3. Apply the patches (`quilt push -a`) or restore your stashed changes. At this
|
||||||
4. Check the Node.js version that's used by Electron (which is shipped with VS
|
stage you may need to resolve conflicts. For example use `quilt push -f`,
|
||||||
|
manually apply the rejected portions, then `quilt refresh`.
|
||||||
|
4. Test code-server locally to make sure everything works.
|
||||||
|
5. Check the Node.js version that's used by Electron (which is shipped with VS
|
||||||
Code. If necessary, update your version of Node.js to match.
|
Code. If necessary, update your version of Node.js to match.
|
||||||
5. Commit the updated submodule to `code-server`.
|
6. Commit the updated submodule and patches to `code-server`.
|
||||||
6. Open a PR.
|
7. Open a PR.
|
||||||
|
|
||||||
|
### Patching Code
|
||||||
|
|
||||||
|
0. You can go through the patch stack with `quilt push` and `quilt pop`.
|
||||||
|
1. Create a new patch (`quilt new {name}.diff`) or use an existing patch.
|
||||||
|
2. Add the file(s) you are patching (`quilt add [-P patch] {file}`). A file
|
||||||
|
**must** be added before you make changes to it.
|
||||||
|
3. Make your changes. Patches do not need to be independent of each other but
|
||||||
|
each patch must result in a working code-server without any broken in-between
|
||||||
|
states otherwise they are difficult to test and modify.
|
||||||
|
4. Add your changes to the patch (`quilt refresh`)
|
||||||
|
5. Add a comment in the patch about the reason for the patch and how to
|
||||||
|
reproduce the behavior it fixes or adds. Every patch should have an e2e test
|
||||||
|
as well.
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
@ -193,99 +214,46 @@ code-server running locally. In CI, this is taken care of for you.
|
|||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
The `code-server` script serves as an HTTP API for login and starting a remote VS
|
The `code-server` script serves as an HTTP API for login and starting a remote
|
||||||
Code process.
|
Code process.
|
||||||
|
|
||||||
The CLI code is in [src/node](../src/node) and the HTTP routes are implemented
|
The CLI code is in [src/node](../src/node) and the HTTP routes are implemented
|
||||||
in [src/node/routes](../src/node/routes).
|
in [src/node/routes](../src/node/routes).
|
||||||
|
|
||||||
Most of the meaty parts are in the VS Code portion of the codebase under
|
Most of the meaty parts are in the Code portion of the codebase under
|
||||||
[lib/vscode](../lib/vscode), which we describe next.
|
[lib/vscode](../lib/vscode), which we describe next.
|
||||||
|
|
||||||
### Modifications to VS Code
|
### Modifications to Code
|
||||||
|
|
||||||
In v1 of code-server, we had a patch of VS Code that split the codebase into a
|
Our modifications to Code can be found in the [patches](../patches) directory.
|
||||||
front-end and a server. The front-end consisted of the UI code, while the server
|
We pull in Code as a submodule pointing to an upstream release branch.
|
||||||
ran the extensions and exposed an API to the front-end for file access and all
|
|
||||||
UI needs.
|
|
||||||
|
|
||||||
Over time, Microsoft added support to VS Code to run it on the web. They have
|
In v1 of code-server, we had Code as a submodule and used a single massive patch
|
||||||
made the front-end open source, but not the server. As such, code-server v2 (and
|
that split the codebase into a front-end and a server. The front-end consisted
|
||||||
later) uses the VS Code front-end and implements the server. We do this by using
|
of the UI code, while the server ran the extensions and exposed an API to the
|
||||||
a Git subtree to fork and modify VS Code. This code lives under
|
front-end for file access and all UI needs.
|
||||||
[lib/vscode](../lib/vscode).
|
|
||||||
|
|
||||||
Some noteworthy changes in our version of VS Code include:
|
Over time, Microsoft added support to Code to run it on the web. They had made
|
||||||
|
the front-end open source, but not the server. As such, code-server v2 (and
|
||||||
|
later) uses the Code front-end and implements the server. We did this by using a
|
||||||
|
Git subtree to fork and modify Code.
|
||||||
|
|
||||||
- Adding our build file, [`lib/vscode/coder.js`](../lib/vscode/coder.js), which includes build steps specific to code-server
|
Microsoft eventually made the server open source and we were able to reduce our
|
||||||
- Node.js version detection changes in [`build/lib/node.ts`](../lib/vscode/build/lib/node.ts) and [`build/lib/util.ts`](../lib/vscode/build/lib/util.ts)
|
changes significantly. Some time later we moved back to a submodule and patches
|
||||||
- Allowing extra extension directories
|
(managed by `quilt` this time instead of the mega-patch).
|
||||||
- Added extra arguments to [`src/vs/platform/environment/common/argv.ts`](../lib/vscode/src/vs/platform/environment/common/argv.ts) and to [`src/vs/platform/environment/node/argv.ts`](../lib/vscode/src/vs/platform/environment/node/argv.ts)
|
|
||||||
- Added extra environment state to [`src/vs/platform/environment/common/environment.ts`](../lib/vscode/src/vs/platform/environment/common/environment.ts);
|
|
||||||
- Added extra getters to [`src/vs/platform/environment/common/environmentService.ts`](../lib/vscode/src/vs/platform/environment/common/environmentService.ts)
|
|
||||||
- Added extra scanning paths to [`src/vs/platform/extensionManagement/node/extensionsScanner.ts`](../lib/vscode/src/vs/platform/extensionManagement/node/extensionsScanner.ts)
|
|
||||||
- Additions/removals from [`package.json`](../lib/vscode/package.json):
|
|
||||||
- Removing `electron`, `keytar` and `native-keymap` to avoid pulling in desktop dependencies during build on Linux
|
|
||||||
- Removing `gulp-azure-storage` and `gulp-tar` (unsued in our build process, may pull in outdated dependencies)
|
|
||||||
- Adding `proxy-agent`, `proxy-from-env` (for proxying) and `rimraf` (used during build/install steps)
|
|
||||||
- Adding our branding/custom URLs/version:
|
|
||||||
- [`product.json`](../lib/vscode/product.json)
|
|
||||||
- [`src/vs/base/common/product.ts`](../lib/vscode/src/vs/base/common/product.ts)
|
|
||||||
- [`src/vs/workbench/browser/parts/dialogs/dialogHandler.ts`](../lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts)
|
|
||||||
- [`src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts`](../lib/vscode/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts)
|
|
||||||
- [`src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts`](../lib/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts)
|
|
||||||
- Removing azure/macOS signing related dependencies from [`build/package.json`](../lib/vscode/build/package.json)
|
|
||||||
- Modifying `.gitignore` to allow us to add files to `src/vs/server` and modifying `.eslintignore` to ignore lint on the shared files below (we use different formatter settings than VS Code).
|
|
||||||
- Sharing some files with our codebase via symlinks:
|
|
||||||
- [`src/vs/base/common/ipc.d.ts`](../lib/vscode/src/vs/base/common/ipc.d.ts) points to [`typings/ipc.d.ts`](../typings/ipc.d.ts)
|
|
||||||
- [`src/vs/base/common/util.ts`](../lib/vscode/src/vs/base/common/util.ts) points to [`src/common/util.ts`](../src/common/util.ts)
|
|
||||||
- [`src/vs/base/node/proxy_agent.ts`](../lib/vscode/src/vs/base/node/proxy_agent.ts) points to [`src/node/proxy_agent.ts`](../src/node/proxy_agent.ts)
|
|
||||||
- Allowing socket changes by adding `setSocket` in [`src/vs/base/parts/ipc/common/ipc.net.ts`](../lib/vscode/src/vs/base/parts/ipc/common/ipc.net.ts)
|
|
||||||
- We use this for connection persistence in our server-side code.
|
|
||||||
- Added our server-side Node.JS code to `src/vs/server`.
|
|
||||||
- This code includes the logic to spawn the various services (extension host, terminal, etc.) and some glue
|
|
||||||
- Added [`src/vs/workbench/browser/client.ts`](../lib/vscode/src/vs/workbench/browser/client.ts) to hold some server customizations.
|
|
||||||
- Includes the functionality for the Log Out command and menu item
|
|
||||||
- Also, imported and called `initialize` from the main web file, [`src/vs/workbench/browser/web.main.ts`](../lib/vscode/src/vs/workbench/browser/web.main.ts)
|
|
||||||
- Added a (hopefully temporary) hotfix to [`src/vs/workbench/common/resources.ts`](../lib/vscode/src/vs/workbench/common/resources.ts) to get context menu actions working for the Git integration.
|
|
||||||
- Added connection type to WebSocket query parameters in [`src/vs/platform/remote/common/remoteAgentConnection.ts`](../lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts)
|
|
||||||
- Added `CODE_SERVER*` variables to the sanitization list in [`src/vs/base/common/processes.ts`](../lib/vscode/src/vs/base/common/processes.ts)
|
|
||||||
- Fix localization support:
|
|
||||||
- Added file [`src/vs/workbench/services/localizations/browser/localizationsService.ts`](../lib/vscode/src/vs/workbench/services/localizations/browser/localizationsService.ts).
|
|
||||||
- Modified file [`src/vs/base/common/platform.ts`](../lib/vscode/src/vs/base/common/platform.ts)
|
|
||||||
- Modified file [`src/vs/base/node/languagePacks.js`](../lib/vscode/src/vs/base/node/languagePacks.js)
|
|
||||||
- Added code to allow server to inject settings to [`src/vs/platform/product/common/product.ts`](../lib/vscode/src/vs/platform/product/common/product.ts)
|
|
||||||
- Extension fixes:
|
|
||||||
- Avoid disabling extensions by extensionKind in [`src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts`](../lib/vscode/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts) (Needed for vscode-icons)
|
|
||||||
- Remove broken symlinks in [`extensions/postinstall.js`](../lib/vscode/extensions/postinstall.js)
|
|
||||||
- Add tip about extension gallery in [`src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts`](../lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts)
|
|
||||||
- Use our own server for GitHub authentication in [`extensions/github-authentication/src/githubServer.ts`](../lib/vscode/extensions/github-authentication/src/githubServer.ts)
|
|
||||||
- Settings persistence on the server in [`src/vs/workbench/services/environment/browser/environmentService.ts`](../lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts)
|
|
||||||
- Add extension install fallback in [`src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts`](../lib/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts)
|
|
||||||
- Add proxy-agent monkeypatch and keep extension host indefinitely running in [`src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts`](../lib/vscode/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts)
|
|
||||||
- Patch build system to avoid removing extension dependencies for `yarn global add` users in [`build/lib/extensions.ts`](../lib/vscode/build/lib/extensions.ts)
|
|
||||||
- Allow all extensions to use proposed APIs in [`src/vs/workbench/services/environment/browser/environmentService.ts`](../lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts)
|
|
||||||
- Make storage writes async to allow extensions to wait for them to complete in [`src/vs/platform/storage/common/storage.ts`](../lib/vscode/src/vs/platform/storage/common/storage.ts)
|
|
||||||
- Specify webview path in [`src/vs/code/browser/workbench/workbench.ts`](../lib/vscode/src/vs/code/browser/workbench/workbench.ts)
|
|
||||||
- URL readability improvements for folder/workspace in [`src/vs/code/browser/workbench/workbench.ts`](../lib/vscode/src/vs/code/browser/workbench/workbench.ts)
|
|
||||||
- Socket/Authority-related fixes (for remote proxying etc.):
|
|
||||||
- [`src/vs/code/browser/workbench/workbench.ts`](../lib/vscode/src/vs/code/browser/workbench/workbench.ts)
|
|
||||||
- [`src/vs/platform/remote/browser/browserSocketFactory.ts`](../lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts)
|
|
||||||
- [`src/vs/base/common/network.ts`](../lib/vscode/src/vs/base/common/network.ts)
|
|
||||||
- Added code to write out IPC path in [`src/vs/workbench/api/node/extHostCLIServer.ts`](../lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts)
|
|
||||||
|
|
||||||
As the web portion of VS Code matures, we'll be able to shrink and possibly
|
As the web portion of Code continues to mature, we'll be able to shrink and
|
||||||
eliminate our modifications. In the meantime, upgrading the VS Code version requires
|
possibly eliminate our patches. In the meantime, upgrading the Code version
|
||||||
us to ensure that our changes are still applied and work as intended. In the future,
|
requires us to ensure that our changes are still applied correctly and work as
|
||||||
we'd like to run VS Code unit tests against our builds to ensure that features
|
intended. In the future, we'd like to run Code unit tests against our builds to
|
||||||
work as expected.
|
ensure that features work as expected.
|
||||||
|
|
||||||
> We have [extension docs](../ci/README.md) on the CI and build system.
|
> We have [extension docs](../ci/README.md) on the CI and build system.
|
||||||
|
|
||||||
If the functionality you're working on does NOT depend on code from VS Code, please
|
If the functionality you're working on does NOT depend on code from Code, please
|
||||||
move it out and into code-server.
|
move it out and into code-server.
|
||||||
|
|
||||||
### Currently Known Issues
|
### Currently Known Issues
|
||||||
|
|
||||||
- Creating custom VS Code extensions and debugging them doesn't work
|
- Creating custom Code extensions and debugging them doesn't work
|
||||||
- Extension profiling and tips are currently disabled
|
- Extension profiling and tips are currently disabled
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a13f6e1434ad6ab820eef0ecca5b923b3e275667
|
Subproject commit f80445acd5a3dadef24aa209168452a3d97cc326
|
305
patches/base-path.diff
Normal file
305
patches/base-path.diff
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
Add base path support
|
||||||
|
|
||||||
|
Some users will host code-server behind a path-rewriting reverse proxy, for
|
||||||
|
example domain.tld/my/base/path. This patch adds support for that since Code
|
||||||
|
assumes everything is on / by default.
|
||||||
|
|
||||||
|
To test this serve code-server behind a reverse proxy with a path like /code.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/network.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/network.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/network.ts
|
||||||
|
@@ -151,8 +151,10 @@ class RemoteAuthoritiesImpl {
|
||||||
|
}
|
||||||
|
return URI.from({
|
||||||
|
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
|
||||||
|
- authority: `${host}:${port}`,
|
||||||
|
- path: `/vscode-remote-resource`,
|
||||||
|
+ authority: platform.isWeb ? window.location.host : `${host}:${port}`,
|
||||||
|
+ path: platform.isWeb
|
||||||
|
+ ? URI.joinPath(URI.parse(window.location.href), `/vscode-remote-resource`).path
|
||||||
|
+ : `/vscode-remote-resource`,
|
||||||
|
query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
@@ -11,8 +11,8 @@
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Code">
|
||||||
|
- <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
- <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="192x192" href="{{BASE}}/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="512x512" href="{{BASE}}/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
|
||||||
|
<!-- Disable pinch zooming -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
@@ -27,23 +27,26 @@
|
||||||
|
<meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}">
|
||||||
|
|
||||||
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
|
- <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
- <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" />
|
||||||
|
- <link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||||
|
+ <link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
+ <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" />
|
||||||
|
+ <link rel="manifest" href="{{VS_BASE}}/manifest.json" crossorigin="use-credentials" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body aria-label="">
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<!-- Startup (do not modify order of script tags!) -->
|
||||||
|
- <script src="./static/out/vs/loader.js"></script>
|
||||||
|
- <script src="./static/out/vs/webPackagePaths.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/loader.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/webPackagePaths.js"></script>
|
||||||
|
<script>
|
||||||
|
Object.keys(self.webPackagePaths).map(function (key, index) {
|
||||||
|
- self.webPackagePaths[key] = `${window.location.origin}/static/remote/web/node_modules/${key}/${self.webPackagePaths[key]}`;
|
||||||
|
+ self.webPackagePaths[key] = new URL(
|
||||||
|
+ `{{VS_BASE}}/static/remote/web/node_modules/${key}/${self.webPackagePaths[key]}`,
|
||||||
|
+ window.location,
|
||||||
|
+ ).toString();
|
||||||
|
});
|
||||||
|
require.config({
|
||||||
|
- baseUrl: `${window.location.origin}/static/out`,
|
||||||
|
+ baseUrl: new URL(`{{VS_BASE}}/static/out`, window.location).toString(),
|
||||||
|
recordStats: true,
|
||||||
|
trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', {
|
||||||
|
createScriptURL(value) {
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
@@ -11,8 +11,8 @@
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Code">
|
||||||
|
- <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
- <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="192x192" href="{{BASE}}/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="512x512" href="{{BASE}}/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
|
||||||
|
<!-- Disable pinch zooming -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
@@ -24,10 +24,10 @@
|
||||||
|
<meta id="vscode-workbench-auth-session" data-settings="{{WORKBENCH_AUTH_SESSION}}">
|
||||||
|
|
||||||
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
|
- <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
- <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" />
|
||||||
|
- <link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||||
|
- <link data-name="vs/workbench/workbench.web.main" rel="stylesheet" href="./static/out/vs/workbench/workbench.web.main.css">
|
||||||
|
+ <link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
+ <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" />
|
||||||
|
+ <link rel="manifest" href="{{VS_BASE}}/manifest.json" crossorigin="use-credentials" />
|
||||||
|
+ <link data-name="vs/workbench/workbench.web.main" rel="stylesheet" href="{{VS_BASE}}/static/out/vs/workbench/workbench.web.main.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
@@ -35,14 +35,17 @@
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<!-- Startup (do not modify order of script tags!) -->
|
||||||
|
- <script src="./static/out/vs/loader.js"></script>
|
||||||
|
- <script src="./static/out/vs/webPackagePaths.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/loader.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/webPackagePaths.js"></script>
|
||||||
|
<script>
|
||||||
|
Object.keys(self.webPackagePaths).map(function (key, index) {
|
||||||
|
- self.webPackagePaths[key] = `${window.location.origin}/static/node_modules/${key}/${self.webPackagePaths[key]}`;
|
||||||
|
+ self.webPackagePaths[key] = new URL(
|
||||||
|
+ `{{VS_BASE}}/static/node_modules/${key}/${self.webPackagePaths[key]}`,
|
||||||
|
+ window.location,
|
||||||
|
+ ).toString();
|
||||||
|
});
|
||||||
|
require.config({
|
||||||
|
- baseUrl: `${window.location.origin}/static/out`,
|
||||||
|
+ baseUrl: new URL(`{{VS_BASE}}/static/out`, window.location).toString(),
|
||||||
|
recordStats: true,
|
||||||
|
trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', {
|
||||||
|
createScriptURL(value) {
|
||||||
|
@@ -55,7 +58,7 @@
|
||||||
|
<script>
|
||||||
|
performance.mark('code/willLoadWorkbenchMain');
|
||||||
|
</script>
|
||||||
|
- <script src="./static/out/vs/workbench/workbench.web.main.nls.js"></script>
|
||||||
|
- <script src="./static/out/vs/workbench/workbench.web.main.js"></script>
|
||||||
|
- <script src="./static/out/vs/code/browser/workbench/workbench.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/workbench/workbench.web.main.nls.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/workbench/workbench.web.main.js"></script>
|
||||||
|
+ <script src="{{VS_BASE}}/static/out/vs/code/browser/workbench/workbench.js"></script>
|
||||||
|
</html>
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
|
||||||
|
@@ -274,7 +274,7 @@ export class BrowserSocketFactory implem
|
||||||
|
|
||||||
|
connect(host: string, port: number, query: string, debugLabel: string, callback: IConnectCallback): void {
|
||||||
|
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
|
||||||
|
- const socket = this._webSocketFactory.create(`${webSocketSchema}://${/:/.test(host) ? `[${host}]` : host}:${port}/?${query}&skipWebSocketFrames=false`, debugLabel);
|
||||||
|
+ const socket = this._webSocketFactory.create(`${webSocketSchema}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`, debugLabel);
|
||||||
|
const errorListener = socket.onError((err) => callback(err, undefined));
|
||||||
|
socket.onOpen(() => {
|
||||||
|
errorListener.dispose();
|
||||||
|
@@ -282,6 +282,3 @@ export class BrowserSocketFactory implem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -252,7 +252,10 @@ export class WebClientServer {
|
||||||
|
return res.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
- const remoteAuthority = req.headers.host;
|
||||||
|
+ // It is not possible to reliably detect the remote authority on the server
|
||||||
|
+ // in all cases. Set this to something invalid to make sure we catch code
|
||||||
|
+ // that is using this when it should not.
|
||||||
|
+ const remoteAuthority = 'remote';
|
||||||
|
|
||||||
|
function escapeAttribute(value: string): string {
|
||||||
|
return value.replace(/"/g, '"');
|
||||||
|
@@ -272,6 +275,8 @@ export class WebClientServer {
|
||||||
|
accessToken: this._environmentService.args['github-auth'],
|
||||||
|
scopes: [['user:email'], ['repo']]
|
||||||
|
} : undefined;
|
||||||
|
+ const base = relativeRoot(getOriginalUrl(req))
|
||||||
|
+ const vscodeBase = relativePath(getOriginalUrl(req))
|
||||||
|
const data = (await util.promisify(fs.readFile)(filePath)).toString()
|
||||||
|
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({
|
||||||
|
remoteAuthority,
|
||||||
|
@@ -279,6 +284,7 @@ export class WebClientServer {
|
||||||
|
developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined },
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
+ rootEndpoint: base,
|
||||||
|
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
|
||||||
|
@@ -289,7 +295,9 @@ export class WebClientServer {
|
||||||
|
} : undefined
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
- .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '');
|
||||||
|
+ .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '')
|
||||||
|
+ .replace(/{{BASE}}/g, base)
|
||||||
|
+ .replace(/{{VS_BASE}}/g, vscodeBase);
|
||||||
|
|
||||||
|
const cspDirectives = [
|
||||||
|
'default-src \'self\';',
|
||||||
|
@@ -368,3 +376,70 @@ export class WebClientServer {
|
||||||
|
return res.end(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Remove extra slashes in a URL.
|
||||||
|
+ *
|
||||||
|
+ * This is meant to fill the job of `path.join` so you can concatenate paths and
|
||||||
|
+ * then normalize out any extra slashes.
|
||||||
|
+ *
|
||||||
|
+ * If you are using `path.join` you do not need this but note that `path` is for
|
||||||
|
+ * file system paths, not URLs.
|
||||||
|
+ */
|
||||||
|
+export const normalizeUrlPath = (url: string, keepTrailing = false): string => {
|
||||||
|
+ return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "")
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Get the relative path that will get us to the root of the page. For each
|
||||||
|
+ * slash we need to go up a directory. Will not have a trailing slash.
|
||||||
|
+ *
|
||||||
|
+ * For example:
|
||||||
|
+ *
|
||||||
|
+ * / => .
|
||||||
|
+ * /foo => .
|
||||||
|
+ * /foo/ => ./..
|
||||||
|
+ * /foo/bar => ./..
|
||||||
|
+ * /foo/bar/ => ./../..
|
||||||
|
+ *
|
||||||
|
+ * All paths must be relative in order to work behind a reverse proxy since we
|
||||||
|
+ * we do not know the base path. Anything that needs to be absolute (for
|
||||||
|
+ * example cookies) must get the base path from the frontend.
|
||||||
|
+ *
|
||||||
|
+ * All relative paths must be prefixed with the relative root to ensure they
|
||||||
|
+ * work no matter the depth at which they happen to appear.
|
||||||
|
+ *
|
||||||
|
+ * For Express `req.originalUrl` should be used as they remove the base from the
|
||||||
|
+ * standard `url` property making it impossible to get the true depth.
|
||||||
|
+ */
|
||||||
|
+export const relativeRoot = (originalUrl: string): string => {
|
||||||
|
+ const depth = (originalUrl.split("?", 1)[0].match(/\//g) || []).length
|
||||||
|
+ return normalizeUrlPath("./" + (depth > 1 ? "../".repeat(depth - 1) : ""))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Get the relative path to the current resource.
|
||||||
|
+ *
|
||||||
|
+ * For example:
|
||||||
|
+ *
|
||||||
|
+ * / => .
|
||||||
|
+ * /foo => ./foo
|
||||||
|
+ * /foo/ => .
|
||||||
|
+ * /foo/bar => ./bar
|
||||||
|
+ * /foo/bar/ => .
|
||||||
|
+ */
|
||||||
|
+export const relativePath = (originalUrl: string): string => {
|
||||||
|
+ const parts = originalUrl.split("?", 1)[0].split("/")
|
||||||
|
+ return normalizeUrlPath("./" + parts[parts.length - 1])
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * code-server serves Code using Express. Express removes the base from the url
|
||||||
|
+ * and puts the original in `originalUrl` so we must use this to get the correct
|
||||||
|
+ * depth. Code is not aware it is behind Express so the types do not match. We
|
||||||
|
+ * may want to continue moving code into Code and eventually remove the Express
|
||||||
|
+ * wrapper or move the web server back into code-server.
|
||||||
|
+ */
|
||||||
|
+export const getOriginalUrl = (req: http.IncomingMessage): string => {
|
||||||
|
+ return (req as any).originalUrl || req.url
|
||||||
|
+}
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -32,6 +32,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||||
|
|
||||||
|
export interface IProductConfiguration {
|
||||||
|
readonly codeServerVersion?: string
|
||||||
|
+ readonly rootEndpoint?: string
|
||||||
|
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
@@ -504,6 +504,7 @@ function doCreateUri(path: string, query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
|
||||||
|
return URI.parse(window.location.href).with({ path, query });
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -515,7 +516,7 @@ function doCreateUri(path: string, query
|
||||||
|
if (!configElement || !configElementAttribute) {
|
||||||
|
throw new Error('Missing web configuration element');
|
||||||
|
}
|
||||||
|
- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute);
|
||||||
|
+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host }
|
||||||
|
|
||||||
|
// Create workbench
|
||||||
|
create(document.body, {
|
19
patches/connection-type.diff
Normal file
19
patches/connection-type.diff
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Add connection type to web sockets
|
||||||
|
|
||||||
|
This allows the backend to distinguish them. In our case we use them to count a
|
||||||
|
single "open" of Code so we need to be able to distinguish between web sockets
|
||||||
|
from two instances and two web sockets used in a single instance.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts
|
||||||
|
@@ -231,7 +231,7 @@ async function connectToRemoteExtensionH
|
||||||
|
|
||||||
|
let socket: ISocket;
|
||||||
|
try {
|
||||||
|
- socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken);
|
||||||
|
+ socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `type=${connectionTypeToString(connectionType)}&reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken);
|
||||||
|
} catch (error) {
|
||||||
|
options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`);
|
||||||
|
options.logService.error(error);
|
265
patches/display-language.diff
Normal file
265
patches/display-language.diff
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
Add display language support
|
||||||
|
|
||||||
|
This likely needs tweaking if we want to upstream.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
||||||
|
@@ -198,6 +198,9 @@ export async function setupServerService
|
||||||
|
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
|
||||||
|
socketServer.registerChannel('extensions', channel);
|
||||||
|
|
||||||
|
+ const localizationsChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ILocalizationsService));
|
||||||
|
+ socketServer.registerChannel('localizations', localizationsChannel);
|
||||||
|
+
|
||||||
|
const encryptionChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(IEncryptionMainService));
|
||||||
|
socketServer.registerChannel('encryption', encryptionChannel);
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/platform.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/platform.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/platform.ts
|
||||||
|
@@ -83,6 +83,17 @@ if (typeof navigator === 'object' && !is
|
||||||
|
_isWeb = true;
|
||||||
|
_locale = navigator.language;
|
||||||
|
_language = _locale;
|
||||||
|
+
|
||||||
|
+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
|
||||||
|
+ const rawNlsConfig = el && el.getAttribute('data-settings');
|
||||||
|
+ if (rawNlsConfig) {
|
||||||
|
+ try {
|
||||||
|
+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
|
||||||
|
+ _locale = nlsConfig.locale;
|
||||||
|
+ _translationsConfigFile = nlsConfig._translationsConfigFile;
|
||||||
|
+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT;
|
||||||
|
+ } catch (error) { /* Oh well. */ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native environment
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
@@ -23,6 +23,9 @@
|
||||||
|
<!-- Workbench Auth Session -->
|
||||||
|
<meta id="vscode-workbench-auth-session" data-settings="{{WORKBENCH_AUTH_SESSION}}">
|
||||||
|
|
||||||
|
+ <!-- NLS Configuration -->
|
||||||
|
+ <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
|
||||||
|
+
|
||||||
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
|
<link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
<link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" />
|
||||||
|
@@ -38,6 +41,27 @@
|
||||||
|
<script src="{{VS_BASE}}/static/out/vs/loader.js"></script>
|
||||||
|
<script src="{{VS_BASE}}/static/out/vs/webPackagePaths.js"></script>
|
||||||
|
<script>
|
||||||
|
+ let nlsConfig
|
||||||
|
+ try {
|
||||||
|
+ nlsConfig = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
|
||||||
|
+ if (nlsConfig._resolvedLanguagePackCoreLocation) {
|
||||||
|
+ const bundles = Object.create(null)
|
||||||
|
+ nlsConfig.loadBundle = (bundle, _language, cb) => {
|
||||||
|
+ const result = bundles[bundle]
|
||||||
|
+ if (result) {
|
||||||
|
+ return cb(undefined, result)
|
||||||
|
+ }
|
||||||
|
+ const path = nlsConfig._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
|
||||||
|
+ fetch(`{{VS_BASE}}/vscode-remote-resource?path=${encodeURIComponent(path)}`)
|
||||||
|
+ .then((response) => response.json())
|
||||||
|
+ .then((json) => {
|
||||||
|
+ bundles[bundle] = json
|
||||||
|
+ cb(undefined, json)
|
||||||
|
+ })
|
||||||
|
+ .catch(cb)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ } catch (error) { /* Probably fine. */ }
|
||||||
|
Object.keys(self.webPackagePaths).map(function (key, index) {
|
||||||
|
self.webPackagePaths[key] = new URL(
|
||||||
|
`{{VS_BASE}}/static/remote/web/node_modules/${key}/${self.webPackagePaths[key]}`,
|
||||||
|
@@ -52,7 +76,8 @@
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
- paths: self.webPackagePaths
|
||||||
|
+ paths: self.webPackagePaths,
|
||||||
|
+ 'vs/nls': nlsConfig,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
||||||
|
@@ -105,7 +105,7 @@ export abstract class AbstractNativeEnvi
|
||||||
|
return URI.file(join(vscodePortable, 'argv.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
- return joinPath(this.userHome, this.productService.dataFolderName, 'argv.json');
|
||||||
|
+ return joinPath(this.appSettingsHome, 'argv.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
||||||
|
@@ -30,6 +30,12 @@ export function getNLSConfiguration(lang
|
||||||
|
if (InternalNLSConfiguration.is(value)) {
|
||||||
|
value._languagePackSupport = true;
|
||||||
|
}
|
||||||
|
+ // If the configuration has no results keep trying since code-server
|
||||||
|
+ // doesn't restart when a language is installed so this result would
|
||||||
|
+ // persist (the plugin might not be installed yet for example).
|
||||||
|
+ if (value.locale !== 'en' && value.locale !== 'en-us' && Object.keys(value.availableLanguages).length === 0) {
|
||||||
|
+ _cache.delete(key);
|
||||||
|
+ }
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
_cache.set(key, result);
|
||||||
|
@@ -44,3 +50,43 @@ export namespace InternalNLSConfiguratio
|
||||||
|
return candidate && typeof candidate._languagePackId === 'string';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * The code below is copied from from src/main.js.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+export const getLocaleFromConfig = async (argvResource: string): Promise<string> => {
|
||||||
|
+ try {
|
||||||
|
+ const content = stripComments(await fs.promises.readFile(argvResource, 'utf8'));
|
||||||
|
+ return JSON.parse(content).locale;
|
||||||
|
+ } catch (error) {
|
||||||
|
+ if (error.code !== "ENOENT") {
|
||||||
|
+ console.warn(error)
|
||||||
|
+ }
|
||||||
|
+ return 'en';
|
||||||
|
+ }
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+const stripComments = (content: string): string => {
|
||||||
|
+ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
|
||||||
|
+
|
||||||
|
+ return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
|
||||||
|
+ // Only one of m1, m2, m3, m4 matches
|
||||||
|
+ if (m3) {
|
||||||
|
+ // A block comment. Replace with nothing
|
||||||
|
+ return '';
|
||||||
|
+ } else if (m4) {
|
||||||
|
+ // A line comment. If it ends in \r?\n then keep it.
|
||||||
|
+ const length_1 = m4.length;
|
||||||
|
+ if (length_1 > 2 && m4[length_1 - 1] === '\n') {
|
||||||
|
+ return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
|
||||||
|
+ }
|
||||||
|
+ else {
|
||||||
|
+ return '';
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ // We match a string
|
||||||
|
+ return match;
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+};
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -26,6 +26,7 @@ import { URI } from 'vs/base/common/uri'
|
||||||
|
import { streamToBuffer } from 'vs/base/common/buffer';
|
||||||
|
import { IProductConfiguration } from 'vs/base/common/product';
|
||||||
|
import { isString } from 'vs/base/common/types';
|
||||||
|
+import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
|
||||||
|
|
||||||
|
const textMimeType = {
|
||||||
|
'.html': 'text/html',
|
||||||
|
@@ -277,6 +278,8 @@ export class WebClientServer {
|
||||||
|
} : undefined;
|
||||||
|
const base = relativeRoot(getOriginalUrl(req))
|
||||||
|
const vscodeBase = relativePath(getOriginalUrl(req))
|
||||||
|
+ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
|
||||||
|
+ const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
|
||||||
|
const data = (await util.promisify(fs.readFile)(filePath)).toString()
|
||||||
|
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({
|
||||||
|
remoteAuthority,
|
||||||
|
@@ -301,7 +304,8 @@ export class WebClientServer {
|
||||||
|
})))
|
||||||
|
.replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '')
|
||||||
|
.replace(/{{BASE}}/g, base)
|
||||||
|
- .replace(/{{VS_BASE}}/g, vscodeBase);
|
||||||
|
+ .replace(/{{VS_BASE}}/g, vscodeBase)
|
||||||
|
+ .replace(/{{NLS_CONFIGURATION}}/g, () => escapeAttribute(JSON.stringify(nlsConfiguration)));
|
||||||
|
|
||||||
|
const cspDirectives = [
|
||||||
|
'default-src \'self\';',
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
@@ -14,6 +14,7 @@ export const serverOptions: OptionDescri
|
||||||
|
/* ----- code-server ----- */
|
||||||
|
'disable-update-check': { type: 'boolean' },
|
||||||
|
'auth': { type: 'string' },
|
||||||
|
+ 'locale': { type: 'string' },
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
||||||
|
@@ -90,6 +91,7 @@ export interface ServerParsedArgs {
|
||||||
|
/* ----- code-server ----- */
|
||||||
|
'disable-update-check'?: boolean;
|
||||||
|
'auth'?: string
|
||||||
|
+ 'locale'?: string
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/services/localizations/browser/localizationsService.ts
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/services/localizations/browser/localizationsService.ts
|
||||||
|
@@ -0,0 +1,28 @@
|
||||||
|
+/*---------------------------------------------------------------------------------------------
|
||||||
|
+ * Copyright (c) Coder Technologies. All rights reserved.
|
||||||
|
+ * Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
+ *--------------------------------------------------------------------------------------------*/
|
||||||
|
+
|
||||||
|
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||||
|
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||||
|
+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||||
|
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Add localizations service for the browser.
|
||||||
|
+ * @author coder
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+// @ts-ignore: interface is implemented via proxy
|
||||||
|
+export class LocalizationsService implements ILocalizationsService {
|
||||||
|
+
|
||||||
|
+ declare readonly _serviceBrand: undefined;
|
||||||
|
+
|
||||||
|
+ constructor(
|
||||||
|
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||||
|
+ ) {
|
||||||
|
+ return ProxyChannel.toService<ILocalizationsService>(remoteAgentService.getConnection()!.getChannel('localizations'));
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+registerSingleton(ILocalizationsService, LocalizationsService, true);
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
||||||
|
@@ -109,6 +109,10 @@ registerSingleton(ICustomEndpointTelemet
|
||||||
|
|
||||||
|
//#region --- workbench contributions
|
||||||
|
|
||||||
|
+// Localizations
|
||||||
|
+import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
|
||||||
|
+import 'vs/workbench/services/localizations/browser/localizationsService';
|
||||||
|
+
|
||||||
|
// Output
|
||||||
|
import 'vs/workbench/contrib/output/common/outputChannelModelService';
|
||||||
|
|
118
patches/github-auth.diff
Normal file
118
patches/github-auth.diff
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
Use our own GitHub auth relay server
|
||||||
|
|
||||||
|
Microsoft's does not work with self-hosted instances so we run our own.
|
||||||
|
|
||||||
|
Also add an extra set of scopes so that tokens provided via --github-auth will
|
||||||
|
work for the PR extension.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/extensions/github-authentication/src/githubServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/extensions/github-authentication/src/githubServer.ts
|
||||||
|
+++ code-server/lib/vscode/extensions/github-authentication/src/githubServer.ts
|
||||||
|
@@ -17,7 +17,7 @@ const localize = nls.loadMessageBundle()
|
||||||
|
const CLIENT_ID = '01ab8ac9400c4e429b23';
|
||||||
|
|
||||||
|
const NETWORK_ERROR = 'network error';
|
||||||
|
-const AUTH_RELAY_SERVER = 'vscode-auth.github.com';
|
||||||
|
+const AUTH_RELAY_SERVER = 'auth.code-server.dev';
|
||||||
|
// const AUTH_RELAY_STAGING_SERVER = 'client-auth-staging-14a768b.herokuapp.com';
|
||||||
|
|
||||||
|
class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -274,7 +274,7 @@ export class WebClientServer {
|
||||||
|
id: generateUuid(),
|
||||||
|
providerId: 'github',
|
||||||
|
accessToken: this._environmentService.args['github-auth'],
|
||||||
|
- scopes: [['user:email'], ['repo']]
|
||||||
|
+ scopes: [['read:user', 'user:email', 'repo'], ['user:email'], ['repo']]
|
||||||
|
} : undefined;
|
||||||
|
const base = relativeRoot(getOriginalUrl(req))
|
||||||
|
const vscodeBase = relativePath(getOriginalUrl(req))
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
@@ -17,6 +17,7 @@ import { isFolderToOpen, isWorkspaceToOp
|
||||||
|
import { create, ICredentialsProvider, IURLCallbackProvider, IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/workbench.web.main';
|
||||||
|
import { posix } from 'vs/base/common/path';
|
||||||
|
import { ltrim } from 'vs/base/common/strings';
|
||||||
|
+import { equals as arrayEquals } from 'vs/base/common/arrays';
|
||||||
|
|
||||||
|
interface ICredential {
|
||||||
|
service: string;
|
||||||
|
@@ -24,6 +25,13 @@ interface ICredential {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
+interface IToken {
|
||||||
|
+ accessToken: string
|
||||||
|
+ account?: { label: string }
|
||||||
|
+ id: string
|
||||||
|
+ scopes: string[]
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
class LocalStorageCredentialsProvider implements ICredentialsProvider {
|
||||||
|
|
||||||
|
private static readonly CREDENTIALS_STORAGE_KEY = 'credentials.provider';
|
||||||
|
@@ -51,6 +59,58 @@ class LocalStorageCredentialsProvider im
|
||||||
|
scopes,
|
||||||
|
accessToken: authSessionInfo!.accessToken
|
||||||
|
}))));
|
||||||
|
+
|
||||||
|
+ // Add tokens for extensions to use. This works for extensions like the
|
||||||
|
+ // pull requests one or GitLens.
|
||||||
|
+ const extensionId = `vscode.${authSessionInfo.providerId}-authentication`;
|
||||||
|
+ const service = `${product.urlProtocol}${extensionId}`;
|
||||||
|
+ const account = `${authSessionInfo.providerId}.auth`;
|
||||||
|
+ // Oddly the scopes need to match exactly so we cannot just have one token
|
||||||
|
+ // with all the scopes, instead we have to duplicate the token for each
|
||||||
|
+ // expected set of scopes.
|
||||||
|
+ const tokens: IToken[] = authSessionInfo.scopes.map((scopes) => ({
|
||||||
|
+ id: authSessionInfo!.id,
|
||||||
|
+ scopes: scopes.sort(), // Sort for comparing later.
|
||||||
|
+ accessToken: authSessionInfo!.accessToken,
|
||||||
|
+ }));
|
||||||
|
+ this.getPassword(service, account).then((raw) => {
|
||||||
|
+ let existing: {
|
||||||
|
+ content: IToken[]
|
||||||
|
+ } | undefined;
|
||||||
|
+
|
||||||
|
+ if (raw) {
|
||||||
|
+ try {
|
||||||
|
+ const json = JSON.parse(raw);
|
||||||
|
+ json.content = JSON.parse(json.content);
|
||||||
|
+ existing = json;
|
||||||
|
+ } catch (error) {
|
||||||
|
+ console.log(error);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Keep tokens for account and scope combinations we do not have in case
|
||||||
|
+ // there is an extension that uses scopes we have not accounted for (in
|
||||||
|
+ // these cases the user will need to manually authenticate the extension
|
||||||
|
+ // through the UI) or the user has tokens for other accounts.
|
||||||
|
+ if (existing?.content) {
|
||||||
|
+ existing.content = existing.content.filter((existingToken) => {
|
||||||
|
+ const scopes = existingToken.scopes.sort();
|
||||||
|
+ return !(tokens.find((token) => {
|
||||||
|
+ return arrayEquals(scopes, token.scopes)
|
||||||
|
+ && token.account?.label === existingToken.account?.label;
|
||||||
|
+ }))
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return this.setPassword(service, account, JSON.stringify({
|
||||||
|
+ extensionId,
|
||||||
|
+ ...(existing || {}),
|
||||||
|
+ content: JSON.stringify([
|
||||||
|
+ ...tokens,
|
||||||
|
+ ...(existing?.content || []),
|
||||||
|
+ ])
|
||||||
|
+ }));
|
||||||
|
+ })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
57
patches/insecure-notification.diff
Normal file
57
patches/insecure-notification.diff
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
Add a notification when accessing code-server in an insecure context
|
||||||
|
|
||||||
|
This is done because otherwise when things like the clipboard do not work users
|
||||||
|
may think code-server is broken. Ideally there would be a notification at the
|
||||||
|
point where these things are used instead of this though.
|
||||||
|
|
||||||
|
To test access over something like an HTTP domain or an IP address (not
|
||||||
|
localhost).
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
@@ -1,7 +1,10 @@
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
+import { localize } from 'vs/nls';
|
||||||
|
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||||
|
|
||||||
|
export class CodeServerClient extends Disposable {
|
||||||
|
constructor (
|
||||||
|
+ @INotificationService private notificationService: INotificationService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
@@ -42,5 +45,32 @@ export class CodeServerClient extends Di
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (!window.isSecureContext) {
|
||||||
|
+ this.notificationService.notify({
|
||||||
|
+ severity: Severity.Warning,
|
||||||
|
+ message: localize(
|
||||||
|
+ 'insecureContext',
|
||||||
|
+ "{0} is being accessed in an insecure context. Web views, the clipboard, and other functionality may not work as expected.",
|
||||||
|
+ 'code-server',
|
||||||
|
+ ),
|
||||||
|
+ actions: {
|
||||||
|
+ primary: [
|
||||||
|
+ {
|
||||||
|
+ id: 'understand',
|
||||||
|
+ label: localize('confirmInsecure', "I understand"),
|
||||||
|
+ tooltip: '',
|
||||||
|
+ class: undefined,
|
||||||
|
+ enabled: true,
|
||||||
|
+ checked: true,
|
||||||
|
+ dispose: () => undefined,
|
||||||
|
+ run: () => {
|
||||||
|
+ return Promise.resolve();
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ ],
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
269
patches/integration.diff
Normal file
269
patches/integration.diff
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
Prepare Code for integration with code-server
|
||||||
|
|
||||||
|
1. We already have the arguments so allow passing them in. There is also a
|
||||||
|
slight change in a few directories to preserve the directory structure we
|
||||||
|
have been using and to not override passed-in arguments.
|
||||||
|
2. Modify the terminal environment to filter out code-server environment variables.
|
||||||
|
3. Add the code-server version to the help dialog.
|
||||||
|
4. Add ready events for use in an iframe.
|
||||||
|
5. Add our icons.
|
||||||
|
6. Remove sourcemap host since we cannot upload ours there.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/server.main.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/server.main.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/server.main.ts
|
||||||
|
@@ -12,7 +12,7 @@ import { createServer as doCreateServer,
|
||||||
|
import { parseArgs, ErrorReporter } from 'vs/platform/environment/node/argv';
|
||||||
|
import { join, dirname } from 'vs/base/common/path';
|
||||||
|
import { performance } from 'perf_hooks';
|
||||||
|
-import { serverOptions } from 'vs/server/node/serverEnvironmentService';
|
||||||
|
+import { serverOptions, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService';
|
||||||
|
import product from 'vs/platform/product/common/product';
|
||||||
|
import * as perf from 'vs/base/common/performance';
|
||||||
|
|
||||||
|
@@ -33,37 +33,42 @@ const errorReporter: ErrorReporter = {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
-const args = parseArgs(process.argv.slice(2), serverOptions, errorReporter);
|
||||||
|
+function parse(): ServerParsedArgs {
|
||||||
|
+ return parseArgs(process.argv.slice(2), serverOptions, errorReporter);
|
||||||
|
+}
|
||||||
|
|
||||||
|
-const REMOTE_DATA_FOLDER = args['server-data-dir'] || process.env['VSCODE_AGENT_FOLDER'] || join(os.homedir(), product.serverDataFolderName || '.vscode-remote');
|
||||||
|
-const USER_DATA_PATH = join(REMOTE_DATA_FOLDER, 'data');
|
||||||
|
-const APP_SETTINGS_HOME = join(USER_DATA_PATH, 'User');
|
||||||
|
-const GLOBAL_STORAGE_HOME = join(APP_SETTINGS_HOME, 'globalStorage');
|
||||||
|
-const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
|
||||||
|
-args['user-data-dir'] = USER_DATA_PATH;
|
||||||
|
-const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath);
|
||||||
|
-const BUILTIN_EXTENSIONS_FOLDER_PATH = join(APP_ROOT, 'extensions');
|
||||||
|
-args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
|
||||||
|
-args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
|
||||||
|
-
|
||||||
|
-[REMOTE_DATA_FOLDER, args['extensions-dir'], USER_DATA_PATH, APP_SETTINGS_HOME, MACHINE_SETTINGS_HOME, GLOBAL_STORAGE_HOME].forEach(f => {
|
||||||
|
- try {
|
||||||
|
- if (!fs.existsSync(f)) {
|
||||||
|
- fs.mkdirSync(f, { mode: 0o700 });
|
||||||
|
- }
|
||||||
|
- } catch (err) { console.error(err); }
|
||||||
|
-});
|
||||||
|
+function createDirs(args: ServerParsedArgs): string {
|
||||||
|
+ const REMOTE_DATA_FOLDER = args['server-data-dir'] || args['user-data-dir'] || process.env['VSCODE_AGENT_FOLDER'] || join(os.homedir(), product.serverDataFolderName || '.vscode-remote');
|
||||||
|
+ const USER_DATA_PATH = args['user-data-dir'] || join(REMOTE_DATA_FOLDER, 'data');
|
||||||
|
+ const APP_SETTINGS_HOME = join(USER_DATA_PATH, 'User');
|
||||||
|
+ const GLOBAL_STORAGE_HOME = join(APP_SETTINGS_HOME, 'globalStorage');
|
||||||
|
+ const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
|
||||||
|
+ args['user-data-dir'] = USER_DATA_PATH;
|
||||||
|
+ const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath);
|
||||||
|
+ const BUILTIN_EXTENSIONS_FOLDER_PATH = args['builtin-extensions-dir'] || join(APP_ROOT, 'extensions');
|
||||||
|
+ args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
|
||||||
|
+ args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
|
||||||
|
+
|
||||||
|
+ [REMOTE_DATA_FOLDER, args['extensions-dir'], USER_DATA_PATH, APP_SETTINGS_HOME, MACHINE_SETTINGS_HOME, GLOBAL_STORAGE_HOME].forEach(f => {
|
||||||
|
+ try {
|
||||||
|
+ if (!fs.existsSync(f)) {
|
||||||
|
+ fs.mkdirSync(f, { mode: 0o700 });
|
||||||
|
+ }
|
||||||
|
+ } catch (err) { console.error(err); }
|
||||||
|
+ });
|
||||||
|
+ return REMOTE_DATA_FOLDER
|
||||||
|
+}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoked by server-main.js
|
||||||
|
*/
|
||||||
|
-export function spawnCli() {
|
||||||
|
- runCli(args, REMOTE_DATA_FOLDER, serverOptions);
|
||||||
|
+export function spawnCli(args = parse()): Promise<void> {
|
||||||
|
+ return runCli(args, createDirs(args), serverOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoked by server-main.js
|
||||||
|
*/
|
||||||
|
-export function createServer(address: string | net.AddressInfo | null): Promise<IServerAPI> {
|
||||||
|
- return doCreateServer(address, args, REMOTE_DATA_FOLDER);
|
||||||
|
+export function createServer(address: string | net.AddressInfo | null, args = parse()): Promise<IServerAPI> {
|
||||||
|
+ return doCreateServer(address, args, createDirs(args));
|
||||||
|
}
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/processes.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/processes.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/processes.ts
|
||||||
|
@@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme
|
||||||
|
/^VSCODE_(?!SHELL_LOGIN).+$/,
|
||||||
|
/^SNAP(|_.*)$/,
|
||||||
|
/^GDK_PIXBUF_.+$/,
|
||||||
|
+ /^CODE_SERVER_.+$/,
|
||||||
|
+ /^CS_.+$/,
|
||||||
|
];
|
||||||
|
const envKeys = Object.keys(env);
|
||||||
|
envKeys
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
|
||||||
|
@@ -144,11 +144,12 @@ export class BrowserDialogHandler implem
|
||||||
|
async about(): Promise<void> {
|
||||||
|
const detailString = (useAgo: boolean): string => {
|
||||||
|
return localize('aboutDetail',
|
||||||
|
- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
|
||||||
|
+ "code-server: v{4}\nCode: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
|
||||||
|
this.productService.version || 'Unknown',
|
||||||
|
this.productService.commit || 'Unknown',
|
||||||
|
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
|
||||||
|
- navigator.userAgent
|
||||||
|
+ navigator.userAgent,
|
||||||
|
+ this.productService.codeServerVersion || 'Unknown'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
@@ -0,0 +1,46 @@
|
||||||
|
+import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
+
|
||||||
|
+export class CodeServerClient extends Disposable {
|
||||||
|
+ constructor (
|
||||||
|
+ ) {
|
||||||
|
+ super();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ async startup(): Promise<void> {
|
||||||
|
+ // Emit ready events
|
||||||
|
+ const event = new CustomEvent('ide-ready');
|
||||||
|
+ window.dispatchEvent(event);
|
||||||
|
+
|
||||||
|
+ if (parent) {
|
||||||
|
+ // Tell the parent loading has completed.
|
||||||
|
+ parent.postMessage({ event: 'loaded' }, '*');
|
||||||
|
+
|
||||||
|
+ // Proxy or stop proxing events as requested by the parent.
|
||||||
|
+ const listeners = new Map<string, (event: Event) => void>();
|
||||||
|
+
|
||||||
|
+ window.addEventListener('message', parentEvent => {
|
||||||
|
+ const eventName = parentEvent.data.bind || parentEvent.data.unbind;
|
||||||
|
+ if (eventName) {
|
||||||
|
+ const oldListener = listeners.get(eventName);
|
||||||
|
+ if (oldListener) {
|
||||||
|
+ document.removeEventListener(eventName, oldListener);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (parentEvent.data.bind && parentEvent.data.prop) {
|
||||||
|
+ const listener = (event: Event) => {
|
||||||
|
+ parent?.postMessage(
|
||||||
|
+ {
|
||||||
|
+ event: parentEvent.data.event,
|
||||||
|
+ [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event],
|
||||||
|
+ },
|
||||||
|
+ window.location.origin,
|
||||||
|
+ );
|
||||||
|
+ };
|
||||||
|
+ listeners.set(parentEvent.data.bind, listener);
|
||||||
|
+ document.addEventListener(parentEvent.data.bind, listener);
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
@@ -69,6 +69,7 @@ import { ICredentialsService } from 'vs/
|
||||||
|
import { IndexedDB } from 'vs/base/browser/indexedDB';
|
||||||
|
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
|
||||||
|
import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
|
||||||
|
+import { CodeServerClient } from 'vs/workbench/browser/client';
|
||||||
|
|
||||||
|
export class BrowserMain extends Disposable {
|
||||||
|
|
||||||
|
@@ -103,6 +104,9 @@ export class BrowserMain extends Disposa
|
||||||
|
// Startup
|
||||||
|
const instantiationService = workbench.startup();
|
||||||
|
|
||||||
|
+ const codeServerClient = this._register(instantiationService.createInstance(CodeServerClient));
|
||||||
|
+ await codeServerClient.startup();
|
||||||
|
+
|
||||||
|
// Window
|
||||||
|
this._register(instantiationService.createInstance(BrowserWindow));
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -31,6 +31,8 @@ export type ExtensionVirtualWorkspaceSup
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IProductConfiguration {
|
||||||
|
+ readonly codeServerVersion?: string
|
||||||
|
+
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
readonly quality?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html
|
||||||
|
@@ -11,7 +11,8 @@
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Code">
|
||||||
|
- <link rel="apple-touch-icon" href="/code-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
|
||||||
|
<!-- Disable pinch zooming -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
@@ -26,7 +27,8 @@
|
||||||
|
<meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}">
|
||||||
|
|
||||||
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
|
- <link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
|
+ <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
+ <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" />
|
||||||
|
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
|
||||||
|
@@ -11,7 +11,8 @@
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Code">
|
||||||
|
- <link rel="apple-touch-icon" href="/code-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" />
|
||||||
|
+ <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" />
|
||||||
|
|
||||||
|
<!-- Disable pinch zooming -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
@@ -23,7 +24,8 @@
|
||||||
|
<meta id="vscode-workbench-auth-session" data-settings="{{WORKBENCH_AUTH_SESSION}}">
|
||||||
|
|
||||||
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
|
- <link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
|
+ <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" />
|
||||||
|
+ <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" />
|
||||||
|
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||||
|
<link data-name="vs/workbench/workbench.web.main" rel="stylesheet" href="./static/out/vs/workbench/workbench.web.main.css">
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/build/gulpfile.reh.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/gulpfile.reh.js
|
||||||
|
+++ code-server/lib/vscode/build/gulpfile.reh.js
|
||||||
|
@@ -365,7 +365,7 @@ function packageTask(type, platform, arc
|
||||||
|
const minifyTask = task.define(`minify-vscode-${type}`, task.series(
|
||||||
|
optimizeTask,
|
||||||
|
util.rimraf(`out-vscode-${type}-min`),
|
||||||
|
- common.minifyTask(`out-vscode-${type}`, `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`)
|
||||||
|
+ common.minifyTask(`out-vscode-${type}`, ``)
|
||||||
|
));
|
||||||
|
gulp.task(minifyTask);
|
||||||
|
|
32
patches/last-opened.diff
Normal file
32
patches/last-opened.diff
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Remove last opened functionality
|
||||||
|
|
||||||
|
This conflicts with our own handling of the last opened workspace. If we wanted
|
||||||
|
to switch to this we would need to pass through the disable-last-opened flag and
|
||||||
|
respect it here then remove our own redirction code that handles this.
|
||||||
|
|
||||||
|
Our version might be better anyway since it puts the workspace in the URL.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
|
@@ -350,19 +350,6 @@ class WorkspaceProvider implements IWork
|
||||||
|
workspace = { folderUri: URI.revive(config.folderUri) };
|
||||||
|
} else if (config.workspaceUri) {
|
||||||
|
workspace = { workspaceUri: URI.revive(config.workspaceUri) };
|
||||||
|
- } else {
|
||||||
|
- workspace = (() => {
|
||||||
|
- const lastWorkspaceRaw = window.localStorage.getItem(WorkspaceProvider.LAST_WORKSPACE_STORAGE_KEY);
|
||||||
|
- if (lastWorkspaceRaw) {
|
||||||
|
- try {
|
||||||
|
- return parse(lastWorkspaceRaw); // use marshalling#parse() to revive potential URIs
|
||||||
|
- } catch (error) {
|
||||||
|
- // Ignore
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return undefined;
|
||||||
|
- })();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
66
patches/local-storage.diff
Normal file
66
patches/local-storage.diff
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
Make storage local to the remote server
|
||||||
|
|
||||||
|
This solves two problems:
|
||||||
|
1. Extensions running in the browser (like Vim) might use these paths
|
||||||
|
directly instead of using the file service and most likely can't write
|
||||||
|
to `/User` on disk.
|
||||||
|
2. Settings will be stored in the file system instead of in browser
|
||||||
|
storage. Using browser storage makes sharing or seeding settings
|
||||||
|
between browsers difficult. We may want to revisit this once/if we get
|
||||||
|
settings sync.
|
||||||
|
|
||||||
|
Unfortunately this does not affect state which uses a separate method with
|
||||||
|
IndexedDB and does not appear nearly as easy to redirect to disk.
|
||||||
|
|
||||||
|
To test install the Vim extension and make sure something that uses file storage
|
||||||
|
works (history recall for example) and change settings from the UI and on disk
|
||||||
|
while making sure they appear on the other side.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -289,6 +289,7 @@ export class WebClientServer {
|
||||||
|
enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined,
|
||||||
|
logLevel: this._logService.getLevel(),
|
||||||
|
},
|
||||||
|
+ userDataPath: this._environmentService.userDataPath,
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
rootEndpoint: base,
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
|
||||||
|
@@ -205,6 +205,11 @@ export interface IWorkbenchConstructionO
|
||||||
|
*/
|
||||||
|
readonly configurationDefaults?: Record<string, any>;
|
||||||
|
|
||||||
|
+ /**
|
||||||
|
+ * Path to the user data directory.
|
||||||
|
+ */
|
||||||
|
+ readonly userDataPath?: string
|
||||||
|
+
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
@@ -126,7 +126,14 @@ export class BrowserWorkbenchEnvironment
|
||||||
|
get logFile(): URI { return joinPath(this.logsHome, 'window.log'); }
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
- get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.userData }); }
|
||||||
|
+ get userRoamingDataHome(): URI { return joinPath(URI.file(this.userDataPath).with({ scheme: Schemas.vscodeRemote }), 'User'); }
|
||||||
|
+
|
||||||
|
+ get userDataPath(): string {
|
||||||
|
+ if (!this.options.userDataPath) {
|
||||||
|
+ throw new Error('userDataPath was not provided to the browser');
|
||||||
|
+ }
|
||||||
|
+ return this.options.userDataPath;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
|
21
patches/log-level.diff
Normal file
21
patches/log-level.diff
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Propagate the log level to the client
|
||||||
|
|
||||||
|
This can be tested by using `--log trace`. You should see plenty of debug and
|
||||||
|
trace logs in the console.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -285,7 +285,10 @@ export class WebClientServer {
|
||||||
|
remoteAuthority,
|
||||||
|
webviewEndpoint: vscodeBase + '/static/out/vs/workbench/contrib/webview/browser/pre',
|
||||||
|
_wrapWebWorkerExtHostInIframe,
|
||||||
|
- developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined },
|
||||||
|
+ developmentOptions: {
|
||||||
|
+ enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined,
|
||||||
|
+ logLevel: this._logService.getLevel(),
|
||||||
|
+ },
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
rootEndpoint: base,
|
107
patches/logout.diff
Normal file
107
patches/logout.diff
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
Add a logout command and menu item
|
||||||
|
|
||||||
|
This will only show if you have authentication enabled.
|
||||||
|
|
||||||
|
This has e2e tests but are currently disabled and need to be fixed.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -34,6 +34,7 @@ export interface IProductConfiguration {
|
||||||
|
readonly codeServerVersion?: string
|
||||||
|
readonly rootEndpoint?: string
|
||||||
|
readonly updateEndpoint?: string
|
||||||
|
+ readonly logoutEndpoint?: string
|
||||||
|
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
@@ -13,6 +13,7 @@ import { IEnvironmentService, INativeEnv
|
||||||
|
export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
|
||||||
|
/* ----- code-server ----- */
|
||||||
|
'disable-update-check': { type: 'boolean' },
|
||||||
|
+ 'auth': { type: 'string' },
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
||||||
|
@@ -88,6 +89,7 @@ export const serverOptions: OptionDescri
|
||||||
|
export interface ServerParsedArgs {
|
||||||
|
/* ----- code-server ----- */
|
||||||
|
'disable-update-check'?: boolean;
|
||||||
|
+ 'auth'?: string
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -287,6 +287,7 @@ export class WebClientServer {
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
rootEndpoint: base,
|
||||||
|
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||||
|
+ logoutEndpoint: this._environmentService.args['auth'] ? base + '/logout' : undefined,
|
||||||
|
extensionsGallery: {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
'resourceUrlTemplate': this._webExtensionResourceUrlTemplate ? this._webExtensionResourceUrlTemplate.with({
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
@@ -1,11 +1,15 @@
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
+import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||||
|
+import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||||
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||||
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
|
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
|
|
||||||
|
export class CodeServerClient extends Disposable {
|
||||||
|
+ static LOGOUT_COMMAND_ID = 'code-server.logout';
|
||||||
|
+
|
||||||
|
constructor (
|
||||||
|
@ILogService private logService: ILogService,
|
||||||
|
@INotificationService private notificationService: INotificationService,
|
||||||
|
@@ -82,6 +86,10 @@ export class CodeServerClient extends Di
|
||||||
|
if (this.productService.updateEndpoint) {
|
||||||
|
this.checkUpdates(this.productService.updateEndpoint)
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (this.productService.logoutEndpoint) {
|
||||||
|
+ this.addLogoutCommand(this.productService.logoutEndpoint);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkUpdates(updateEndpoint: string) {
|
||||||
|
@@ -133,4 +141,25 @@ export class CodeServerClient extends Di
|
||||||
|
|
||||||
|
updateLoop();
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ private addLogoutCommand(logoutEndpoint: string) {
|
||||||
|
+ CommandsRegistry.registerCommand(CodeServerClient.LOGOUT_COMMAND_ID, () => {
|
||||||
|
+ const logoutUrl = new URL(logoutEndpoint, window.location.href);
|
||||||
|
+ // Cookies must be set with absolute paths and must use the same path to
|
||||||
|
+ // be unset (we set it on the root) so send the relative root and the
|
||||||
|
+ // current href so the backend can derive the absolute path to the root.
|
||||||
|
+ logoutUrl.searchParams.set('base', this.productService.rootEndpoint || ".");
|
||||||
|
+ logoutUrl.searchParams.set('href', window.location.href);
|
||||||
|
+ window.location.assign(logoutUrl);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ for (const menuId of [MenuId.CommandPalette, MenuId.MenubarHomeMenu]) {
|
||||||
|
+ MenuRegistry.appendMenuItem(menuId, {
|
||||||
|
+ command: {
|
||||||
|
+ id: CodeServerClient.LOGOUT_COMMAND_ID,
|
||||||
|
+ title: localize('logout', "Sign out of {0}", 'code-server'),
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
53
patches/marketplace.diff
Normal file
53
patches/marketplace.diff
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
Add Open VSX default and an env var for marketplace, fix old marketplace
|
||||||
|
|
||||||
|
Our old marketplace only supports `serviceUrl` but this causes the marketplace
|
||||||
|
to be disabled entirely so this moves the template var check to fix that.
|
||||||
|
|
||||||
|
This can be tested by setting EXTENSIONS_GALLERY set to:
|
||||||
|
|
||||||
|
'{"serviceUrl": "https://extensions.coder.com/api"}'
|
||||||
|
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/product/common/product.ts
|
||||||
|
@@ -45,7 +45,14 @@ else if (typeof require?.__$__nodeRequir
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(product, {
|
||||||
|
- version: pkg.version
|
||||||
|
+ version: pkg.version,
|
||||||
|
+ extensionsGallery: env.EXTENSIONS_GALLERY ? JSON.parse(env.EXTENSIONS_GALLERY) : (product.extensionsGallery || {
|
||||||
|
+ serviceUrl: "https://open-vsx.org/vscode/gallery",
|
||||||
|
+ itemUrl: "https://open-vsx.org/vscode/item",
|
||||||
|
+ resourceUrlTemplate: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}",
|
||||||
|
+ controlUrl: "",
|
||||||
|
+ recommendationsUrl: "",
|
||||||
|
+ }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -285,14 +285,14 @@ export class WebClientServer {
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
rootEndpoint: base,
|
||||||
|
- extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||||
|
+ extensionsGallery: {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
- 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
|
||||||
|
+ 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate ? this._webExtensionResourceUrlTemplate.with({
|
||||||
|
scheme: 'http',
|
||||||
|
authority: remoteAuthority,
|
||||||
|
path: `web-extension-resource/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}`
|
||||||
|
- }).toString(true)
|
||||||
|
- } : undefined
|
||||||
|
+ }).toString(true) : undefined
|
||||||
|
+ },
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
.replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '')
|
106
patches/node-version.diff
Normal file
106
patches/node-version.diff
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
Patch the Node version to use the current version of Node
|
||||||
|
|
||||||
|
Previously it would use the yarnrc which results in builds that cannot run with
|
||||||
|
the version of Node they were built with because the native modules are
|
||||||
|
targeting the wrong version.
|
||||||
|
|
||||||
|
One way test this is to build in a fresh Docker container, run the build, then
|
||||||
|
try opening the built-in terminal.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/build/gulpfile.reh.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/gulpfile.reh.js
|
||||||
|
+++ code-server/lib/vscode/build/gulpfile.reh.js
|
||||||
|
@@ -122,9 +122,7 @@ const serverWithWebEntryPoints = [
|
||||||
|
];
|
||||||
|
|
||||||
|
function getNodeVersion() {
|
||||||
|
- const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8');
|
||||||
|
- const target = /^target "(.*)"$/m.exec(yarnrc)[1];
|
||||||
|
- return target;
|
||||||
|
+ return process.versions.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeVersion = getNodeVersion();
|
||||||
|
Index: code-server/lib/vscode/build/lib/node.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/lib/node.js
|
||||||
|
+++ code-server/lib/vscode/build/lib/node.js
|
||||||
|
@@ -7,9 +7,7 @@ Object.defineProperty(exports, "__esModu
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const root = path.dirname(path.dirname(__dirname));
|
||||||
|
-const yarnrcPath = path.join(root, 'remote', '.yarnrc');
|
||||||
|
-const yarnrc = fs.readFileSync(yarnrcPath, 'utf8');
|
||||||
|
-const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)[1];
|
||||||
|
+const version = process.versions.node;
|
||||||
|
const platform = process.platform;
|
||||||
|
const arch = platform === 'darwin' ? 'x64' : process.arch;
|
||||||
|
const node = platform === 'win32' ? 'node.exe' : 'node';
|
||||||
|
Index: code-server/lib/vscode/build/lib/node.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/lib/node.ts
|
||||||
|
+++ code-server/lib/vscode/build/lib/node.ts
|
||||||
|
@@ -7,9 +7,7 @@ import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
const root = path.dirname(path.dirname(__dirname));
|
||||||
|
-const yarnrcPath = path.join(root, 'remote', '.yarnrc');
|
||||||
|
-const yarnrc = fs.readFileSync(yarnrcPath, 'utf8');
|
||||||
|
-const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)![1];
|
||||||
|
+const version = process.versions.node;
|
||||||
|
|
||||||
|
const platform = process.platform;
|
||||||
|
const arch = platform === 'darwin' ? 'x64' : process.arch;
|
||||||
|
Index: code-server/lib/vscode/build/lib/util.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/lib/util.js
|
||||||
|
+++ code-server/lib/vscode/build/lib/util.js
|
||||||
|
@@ -298,9 +298,7 @@ function streamToPromise(stream) {
|
||||||
|
}
|
||||||
|
exports.streamToPromise = streamToPromise;
|
||||||
|
function getElectronVersion() {
|
||||||
|
- const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
|
||||||
|
- const target = /^target "(.*)"$/m.exec(yarnrc)[1];
|
||||||
|
- return target;
|
||||||
|
+ return process.versions.node;
|
||||||
|
}
|
||||||
|
exports.getElectronVersion = getElectronVersion;
|
||||||
|
function acquireWebNodePaths() {
|
||||||
|
Index: code-server/lib/vscode/build/lib/util.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/build/lib/util.ts
|
||||||
|
+++ code-server/lib/vscode/build/lib/util.ts
|
||||||
|
@@ -371,9 +371,7 @@ export function streamToPromise(stream:
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronVersion(): string {
|
||||||
|
- const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
|
||||||
|
- const target = /^target "(.*)"$/m.exec(yarnrc)![1];
|
||||||
|
- return target;
|
||||||
|
+ return process.versions.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function acquireWebNodePaths() {
|
||||||
|
@@ -455,4 +453,3 @@ export function buildWebNodePaths(outDir
|
||||||
|
result.taskName = 'build-web-node-paths';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
Index: code-server/lib/vscode/remote/.yarnrc
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/remote/.yarnrc
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,3 +0,0 @@
|
||||||
|
-disturl "http://nodejs.org/dist"
|
||||||
|
-target "14.16.0"
|
||||||
|
-runtime "node"
|
||||||
|
Index: code-server/lib/vscode/.yarnrc
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/.yarnrc
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,4 +0,0 @@
|
||||||
|
-disturl "https://electronjs.org/headers"
|
||||||
|
-target "13.5.2"
|
||||||
|
-runtime "electron"
|
||||||
|
-build_from_source "true"
|
26
patches/post-install.diff
Normal file
26
patches/post-install.diff
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Replace rimraf with fs.rmSync in postinstall
|
||||||
|
|
||||||
|
The postinstall gets ran when you install with npm but rimraf is a development
|
||||||
|
dependency so it will not exist.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/extensions/postinstall.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/extensions/postinstall.js
|
||||||
|
+++ code-server/lib/vscode/extensions/postinstall.js
|
||||||
|
@@ -8,7 +8,6 @@
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
-const rimraf = require('rimraf');
|
||||||
|
|
||||||
|
const root = path.join(__dirname, 'node_modules', 'typescript');
|
||||||
|
|
||||||
|
@@ -21,7 +20,7 @@ function processRoot() {
|
||||||
|
if (!toKeep.has(name)) {
|
||||||
|
const filePath = path.join(root, name);
|
||||||
|
console.log(`Removed ${filePath}`);
|
||||||
|
- rimraf.sync(filePath);
|
||||||
|
+ fs.rmSync(filePath, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
patches/proposed-api.diff
Normal file
36
patches/proposed-api.diff
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Unconditionally enable the proposed API
|
||||||
|
|
||||||
|
To test run an extension that uses the proposed API.
|
||||||
|
|
||||||
|
We also override isProposedApiEnabled in case an extension does not declare the
|
||||||
|
APIs it needs correctly (the Jupyter extension had this issue).
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
|
||||||
|
@@ -1134,7 +1134,7 @@ class ProposedApiController {
|
||||||
|
|
||||||
|
this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id)));
|
||||||
|
|
||||||
|
- this._envEnablesProposedApiForAll =
|
||||||
|
+ this._envEnablesProposedApiForAll = true ||
|
||||||
|
!_environmentService.isBuilt || // always allow proposed API when running out of sources
|
||||||
|
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
|
||||||
|
(this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
|
||||||
|
@@ -135,10 +135,7 @@ export interface IExtensionHost {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
|
||||||
|
- if (!extension.enabledApiProposals) {
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
- return extension.enabledApiProposals.includes(proposal);
|
||||||
|
+ return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {
|
104
patches/proxy-uri.diff
Normal file
104
patches/proxy-uri.diff
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
Add VSCODE_PROXY_URI environment variable
|
||||||
|
|
||||||
|
This can be used by extensions to open a port and access it through the proxy.
|
||||||
|
|
||||||
|
It is available in the terminal as well.
|
||||||
|
|
||||||
|
This can be tested using printenv in the terminal and by using the
|
||||||
|
codeServerTest.proxyUri command through the test extension (copy it into your
|
||||||
|
extensions, use --extensions-dir, or symlink it).
|
||||||
|
|
||||||
|
This has e2e tests.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -35,6 +35,7 @@ export interface IProductConfiguration {
|
||||||
|
readonly rootEndpoint?: string
|
||||||
|
readonly updateEndpoint?: string
|
||||||
|
readonly logoutEndpoint?: string
|
||||||
|
+ readonly proxyEndpointTemplate?: string
|
||||||
|
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
||||||
|
@@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { RemoteAuthorities } from 'vs/base/common/network';
|
||||||
|
import { URI } from 'vs/base/common/uri';
|
||||||
|
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||||
|
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||||
|
|
||||||
|
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ export class RemoteAuthorityResolverServ
|
||||||
|
private readonly _connectionToken: string | undefined;
|
||||||
|
private readonly _connectionTokens: Map<string, string>;
|
||||||
|
|
||||||
|
- constructor(connectionToken: string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) {
|
||||||
|
+ constructor(connectionToken: string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, private readonly proxyEndpointTemplate?: string) {
|
||||||
|
super();
|
||||||
|
this._cache = new Map<string, ResolverResult>();
|
||||||
|
this._connectionToken = connectionToken;
|
||||||
|
@@ -59,12 +59,17 @@ export class RemoteAuthorityResolverServ
|
||||||
|
|
||||||
|
private _doResolveAuthority(authority: string): ResolverResult {
|
||||||
|
const connectionToken = this._connectionTokens.get(authority) || this._connectionToken;
|
||||||
|
+ let options: ResolvedOptions | undefined
|
||||||
|
+ if (this.proxyEndpointTemplate) {
|
||||||
|
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href);
|
||||||
|
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
|
||||||
|
+ }
|
||||||
|
if (authority.indexOf(':') >= 0) {
|
||||||
|
const pieces = authority.split(':');
|
||||||
|
- return { authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10), connectionToken } };
|
||||||
|
+ return { authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10), connectionToken }, options };
|
||||||
|
}
|
||||||
|
const port = (/^https:/.test(window.location.href) ? 443 : 80);
|
||||||
|
- return { authority: { authority, host: authority, port: port, connectionToken } };
|
||||||
|
+ return { authority: { authority, host: authority, port: port, connectionToken }, options };
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearResolvedAuthority(authority: string): void {
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -288,6 +288,7 @@ export class WebClientServer {
|
||||||
|
rootEndpoint: base,
|
||||||
|
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||||
|
logoutEndpoint: this._environmentService.args['auth'] ? base + '/logout' : undefined,
|
||||||
|
+ proxyEndpointTemplate: base + '/proxy/{{port}}',
|
||||||
|
extensionsGallery: {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
'resourceUrlTemplate': this._webExtensionResourceUrlTemplate ? this._webExtensionResourceUrlTemplate.with({
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
|
||||||
|
@@ -179,7 +179,7 @@ export class BrowserMain extends Disposa
|
||||||
|
|
||||||
|
// Remote
|
||||||
|
const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName);
|
||||||
|
- const remoteAuthorityResolverService = new RemoteAuthorityResolverService(connectionToken, this.configuration.resourceUriProvider);
|
||||||
|
+ const remoteAuthorityResolverService = new RemoteAuthorityResolverService(connectionToken, this.configuration.resourceUriProvider, this.configuration.productConfiguration?.proxyEndpointTemplate);
|
||||||
|
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
|
||||||
|
|
||||||
|
// Signing
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
||||||
|
@@ -388,7 +388,7 @@ export function createTerminalEnvironmen
|
||||||
|
|
||||||
|
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||||
|
// variables
|
||||||
|
- sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
|
||||||
|
+ sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI', 'VSCODE_PROXY_URI');
|
||||||
|
|
||||||
|
// Merge config (settings) and ShellLaunchConfig environments
|
||||||
|
mergeEnvironments(env, allowedEnvFromConfig);
|
20
patches/series
Normal file
20
patches/series
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
integration.diff
|
||||||
|
node-version.diff
|
||||||
|
base-path.diff
|
||||||
|
proposed-api.diff
|
||||||
|
marketplace.diff
|
||||||
|
webview.diff
|
||||||
|
insecure-notification.diff
|
||||||
|
update-check.diff
|
||||||
|
logout.diff
|
||||||
|
store-socket.diff
|
||||||
|
proxy-uri.diff
|
||||||
|
display-language.diff
|
||||||
|
github-auth.diff
|
||||||
|
unique-db.diff
|
||||||
|
post-install.diff
|
||||||
|
log-level.diff
|
||||||
|
local-storage.diff
|
||||||
|
service-worker.diff
|
||||||
|
last-opened.diff
|
||||||
|
connection-type.diff
|
67
patches/service-worker.diff
Normal file
67
patches/service-worker.diff
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
Add a service worker
|
||||||
|
|
||||||
|
To test try installing code-server as a PWA.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -36,6 +36,10 @@ export interface IProductConfiguration {
|
||||||
|
readonly updateEndpoint?: string
|
||||||
|
readonly logoutEndpoint?: string
|
||||||
|
readonly proxyEndpointTemplate?: string
|
||||||
|
+ readonly serviceWorker?: {
|
||||||
|
+ readonly path: string;
|
||||||
|
+ readonly scope: string;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -296,6 +296,10 @@ export class WebClientServer {
|
||||||
|
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||||
|
logoutEndpoint: this._environmentService.args['auth'] ? base + '/logout' : undefined,
|
||||||
|
proxyEndpointTemplate: base + '/proxy/{{port}}',
|
||||||
|
+ serviceWorker: {
|
||||||
|
+ scope: vscodeBase + '/',
|
||||||
|
+ path: base + '/_static/out/browser/serviceWorker.js',
|
||||||
|
+ },
|
||||||
|
extensionsGallery: {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
'resourceUrlTemplate': this._webExtensionResourceUrlTemplate ? this._webExtensionResourceUrlTemplate.with({
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
@@ -90,6 +90,10 @@ export class CodeServerClient extends Di
|
||||||
|
if (this.productService.logoutEndpoint) {
|
||||||
|
this.addLogoutCommand(this.productService.logoutEndpoint);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (this.productService.serviceWorker) {
|
||||||
|
+ await this.registerServiceWorker(this.productService.serviceWorker);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkUpdates(updateEndpoint: string) {
|
||||||
|
@@ -162,4 +166,17 @@ export class CodeServerClient extends Di
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ private async registerServiceWorker(serviceWorker: { path: string; scope: string }) {
|
||||||
|
+ if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {
|
||||||
|
+ try {
|
||||||
|
+ await navigator.serviceWorker.register(serviceWorker.path, {
|
||||||
|
+ scope: serviceWorker.scope,
|
||||||
|
+ });
|
||||||
|
+ this.logService.info('[Service Worker] registered');
|
||||||
|
+ } catch (error: any) {
|
||||||
|
+ this.logService.error('[Service Worker] registration', error as Error);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
31
patches/store-socket.diff
Normal file
31
patches/store-socket.diff
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
Store a static reference to the IPC socket
|
||||||
|
|
||||||
|
This lets us use it to open files inside code-server from outside of
|
||||||
|
code-server.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||||
|
@@ -2,7 +2,9 @@
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
-
|
||||||
|
+import { promises as fs } from 'fs';
|
||||||
|
+import * as os from 'os'
|
||||||
|
+import * as path from 'vs/base/common/path';
|
||||||
|
import * as performance from 'vs/base/common/performance';
|
||||||
|
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
|
||||||
|
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
|
||||||
|
@@ -69,6 +71,10 @@ export class ExtHostExtensionService ext
|
||||||
|
if (this._initData.remote.isRemote && this._initData.remote.authority) {
|
||||||
|
const cliServer = this._instaService.createInstance(CLIServer);
|
||||||
|
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
|
||||||
|
+
|
||||||
|
+ fs.writeFile(path.join(os.tmpdir(), 'vscode-ipc'), cliServer.ipcHandlePath).catch((error) => {
|
||||||
|
+ this._logService.error(error);
|
||||||
|
+ });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module loading tricks
|
63
patches/unique-db.diff
Normal file
63
patches/unique-db.diff
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
Prevent state collisions
|
||||||
|
|
||||||
|
Previously if you opened different workspaces that had the same filesystem path
|
||||||
|
(for example if you have /home/coder on two different machines that are both
|
||||||
|
accessed through the same host) they would conflict with each other. This
|
||||||
|
ensures that different browser paths will be unique (for example /workspace1 and
|
||||||
|
/workspace2).
|
||||||
|
|
||||||
|
The easiest way to test is to open files in the same workspace using both / and
|
||||||
|
/vscode and make sure they are not interacting with each other.
|
||||||
|
|
||||||
|
It should also migrate old databases which can be tested by opening in an old
|
||||||
|
code-server.
|
||||||
|
|
||||||
|
This has e2e tests.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/platform/storage/browser/storageService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/platform/storage/browser/storageService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/platform/storage/browser/storageService.ts
|
||||||
|
@@ -13,6 +13,7 @@ import { InMemoryStorageDatabase, isStor
|
||||||
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
|
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
|
||||||
|
+import { hash } from 'vs/base/common/hash';
|
||||||
|
|
||||||
|
export class BrowserStorageService extends AbstractStorageService {
|
||||||
|
|
||||||
|
@@ -36,7 +37,11 @@ export class BrowserStorageService exten
|
||||||
|
}
|
||||||
|
|
||||||
|
private getId(scope: StorageScope): string {
|
||||||
|
- return scope === StorageScope.GLOBAL ? 'global' : this.payload.id;
|
||||||
|
+ // Add a unique ID based on the current path for per-workspace databases.
|
||||||
|
+ // This prevents workspaces on different machines that share the same domain
|
||||||
|
+ // and file path from colliding (since it does not appear IndexedDB can be
|
||||||
|
+ // scoped to a path) as long as they are hosted on different paths.
|
||||||
|
+ return scope === StorageScope.GLOBAL ? 'global' : (this.payload.id + '-' + hash(location.pathname.toString().replace(/\/$/, "")).toString(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async doInitialize(): Promise<void> {
|
||||||
|
@@ -75,6 +80,21 @@ export class BrowserStorageService exten
|
||||||
|
const firstWorkspaceOpen = this.workspaceStorage.getBoolean(IS_NEW_KEY);
|
||||||
|
if (firstWorkspaceOpen === undefined) {
|
||||||
|
this.workspaceStorage.set(IS_NEW_KEY, true);
|
||||||
|
+ // Migrate the old database.
|
||||||
|
+ let db: IIndexedDBStorageDatabase | undefined
|
||||||
|
+ try {
|
||||||
|
+ db = await IndexedDBStorageDatabase.create({ id: this.payload.id }, this.logService)
|
||||||
|
+ const items = await db.getItems()
|
||||||
|
+ for (const [key, value] of items) {
|
||||||
|
+ this.workspaceStorage.set(key, value);
|
||||||
|
+ }
|
||||||
|
+ } catch (error) {
|
||||||
|
+ this.logService.error(`[IndexedDB Storage ${this.payload.id}] migrate error: ${toErrorMessage(error)}`);
|
||||||
|
+ } finally {
|
||||||
|
+ if (db) {
|
||||||
|
+ db.close()
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
} else if (firstWorkspaceOpen) {
|
||||||
|
this.workspaceStorage.set(IS_NEW_KEY, false);
|
||||||
|
}
|
132
patches/update-check.diff
Normal file
132
patches/update-check.diff
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
Add a notification that lets you know when an update is out
|
||||||
|
|
||||||
|
The easiest way to test this is probably to change the version in your
|
||||||
|
package.json and delete the last notification storage item.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||||
|
@@ -1,10 +1,16 @@
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
+import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||||
|
+import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
|
+import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
|
|
||||||
|
export class CodeServerClient extends Disposable {
|
||||||
|
constructor (
|
||||||
|
+ @ILogService private logService: ILogService,
|
||||||
|
@INotificationService private notificationService: INotificationService,
|
||||||
|
+ @IProductService private productService: IProductService,
|
||||||
|
+ @IStorageService private storageService: IStorageService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
@@ -72,5 +78,59 @@ export class CodeServerClient extends Di
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (this.productService.updateEndpoint) {
|
||||||
|
+ this.checkUpdates(this.productService.updateEndpoint)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private checkUpdates(updateEndpoint: string) {
|
||||||
|
+ const getUpdate = async (updateCheckEndpoint: string): Promise<void> => {
|
||||||
|
+ this.logService.debug('Checking for update...');
|
||||||
|
+
|
||||||
|
+ const response = await fetch(updateCheckEndpoint, {
|
||||||
|
+ headers: { Accept: 'application/json' },
|
||||||
|
+ });
|
||||||
|
+ if (!response.ok) {
|
||||||
|
+ throw new Error(response.statusText);
|
||||||
|
+ }
|
||||||
|
+ const json = await response.json();
|
||||||
|
+ if (json.error) {
|
||||||
|
+ throw new Error(json.error);
|
||||||
|
+ }
|
||||||
|
+ if (json.isLatest) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ const lastNoti = this.storageService.getNumber('csLastUpdateNotification', StorageScope.GLOBAL);
|
||||||
|
+ if (lastNoti) {
|
||||||
|
+ // Only remind them again after 1 week.
|
||||||
|
+ const timeout = 1000 * 60 * 60 * 24 * 7;
|
||||||
|
+ const threshold = lastNoti + timeout;
|
||||||
|
+ if (Date.now() < threshold) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ this.storageService.store('csLastUpdateNotification', Date.now(), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||||
|
+
|
||||||
|
+ this.notificationService.notify({
|
||||||
|
+ severity: Severity.Info,
|
||||||
|
+ message: `[code-server v${json.latest}](https://github.com/cdr/code-server/releases/tag/v${json.latest}) has been released!`,
|
||||||
|
+ });
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ const updateLoop = (): void => {
|
||||||
|
+ getUpdate(updateEndpoint)
|
||||||
|
+ .catch(error => {
|
||||||
|
+ this.logService.debug(`failed to check for update: ${error}`);
|
||||||
|
+ })
|
||||||
|
+ .finally(() => {
|
||||||
|
+ // Check again every 6 hours.
|
||||||
|
+ setTimeout(updateLoop, 1000 * 60 * 60 * 6);
|
||||||
|
+ });
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ updateLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
||||||
|
@@ -33,6 +33,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||||
|
export interface IProductConfiguration {
|
||||||
|
readonly codeServerVersion?: string
|
||||||
|
readonly rootEndpoint?: string
|
||||||
|
+ readonly updateEndpoint?: string
|
||||||
|
|
||||||
|
readonly version: string;
|
||||||
|
readonly date?: string;
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -286,6 +286,7 @@ export class WebClientServer {
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
productConfiguration: <Partial<IProductConfiguration>>{
|
||||||
|
rootEndpoint: base,
|
||||||
|
+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||||
|
extensionsGallery: {
|
||||||
|
...this._productService.extensionsGallery,
|
||||||
|
'resourceUrlTemplate': this._webExtensionResourceUrlTemplate ? this._webExtensionResourceUrlTemplate.with({
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||||
|
@@ -11,6 +11,8 @@ import { refineServiceDecorator } from '
|
||||||
|
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
|
|
||||||
|
export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
|
||||||
|
+ /* ----- code-server ----- */
|
||||||
|
+ 'disable-update-check': { type: 'boolean' },
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
||||||
|
@@ -84,6 +86,8 @@ export const serverOptions: OptionDescri
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ServerParsedArgs {
|
||||||
|
+ /* ----- code-server ----- */
|
||||||
|
+ 'disable-update-check'?: boolean;
|
||||||
|
|
||||||
|
/* ----- server setup ----- */
|
||||||
|
|
46
patches/webview.diff
Normal file
46
patches/webview.diff
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
Serve webviews from the same origin
|
||||||
|
|
||||||
|
Normally webviews are served from vscode-webview.net but we would rather them be
|
||||||
|
self-hosted.
|
||||||
|
|
||||||
|
When doing this CSP will block resources (for example when viewing images) so
|
||||||
|
add 'self' to the CSP to fix that.
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
|
||||||
|
@@ -240,7 +240,7 @@ export class BrowserWorkbenchEnvironment
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
get webviewExternalEndpoint(): string {
|
||||||
|
- const endpoint = this.options.webviewEndpoint
|
||||||
|
+ const endpoint = (this.options.webviewEndpoint && new URL(this.options.webviewEndpoint, window.location.toString()).toString())
|
||||||
|
|| this.productService.webviewContentExternalBaseUrlTemplate
|
||||||
|
|| 'https://{{uuid}}.vscode-webview.net/{{quality}}/{{commit}}/out/vs/workbench/contrib/webview/browser/pre/';
|
||||||
|
|
||||||
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
|
@@ -280,6 +280,7 @@ export class WebClientServer {
|
||||||
|
const data = (await util.promisify(fs.readFile)(filePath)).toString()
|
||||||
|
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({
|
||||||
|
remoteAuthority,
|
||||||
|
+ webviewEndpoint: vscodeBase + '/static/out/vs/workbench/contrib/webview/browser/pre',
|
||||||
|
_wrapWebWorkerExtHostInIframe,
|
||||||
|
developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined },
|
||||||
|
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
|
||||||
|
Index: code-server/lib/vscode/src/vs/workbench/api/common/shared/webview.ts
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/vs/workbench/api/common/shared/webview.ts
|
||||||
|
+++ code-server/lib/vscode/src/vs/workbench/api/common/shared/webview.ts
|
||||||
|
@@ -25,7 +25,7 @@ export const webviewResourceBaseHost = '
|
||||||
|
|
||||||
|
export const webviewRootResourceAuthority = `vscode-resource.${webviewResourceBaseHost}`;
|
||||||
|
|
||||||
|
-export const webviewGenericCspSource = `https://*.${webviewResourceBaseHost}`;
|
||||||
|
+export const webviewGenericCspSource = `'self' https://*.${webviewResourceBaseHost}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a uri that can load resources inside a webview
|
14
src/browser/serviceWorker.ts
Normal file
14
src/browser/serviceWorker.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
self.addEventListener("install", () => {
|
||||||
|
console.debug("[Service Worker] installed")
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener("activate", (event: any) => {
|
||||||
|
event.waitUntil((self as any).clients.claim())
|
||||||
|
console.debug("[Service Worker] activated")
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener("fetch", () => {
|
||||||
|
// Without this event handler we won't be recognized as a PWA.
|
||||||
|
})
|
@ -31,13 +31,33 @@ export enum LogLevel {
|
|||||||
|
|
||||||
export class OptionalString extends Optional<string> {}
|
export class OptionalString extends Optional<string> {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code flags provided by the user.
|
||||||
|
*/
|
||||||
|
export interface UserProvidedCodeArgs {
|
||||||
|
"disable-telemetry"?: boolean
|
||||||
|
force?: boolean
|
||||||
|
"user-data-dir"?: string
|
||||||
|
"enable-proposed-api"?: string[]
|
||||||
|
"extensions-dir"?: string
|
||||||
|
"builtin-extensions-dir"?: string
|
||||||
|
"install-extension"?: string[]
|
||||||
|
"uninstall-extension"?: string[]
|
||||||
|
"list-extensions"?: boolean
|
||||||
|
"locate-extension"?: string[]
|
||||||
|
"show-versions"?: boolean
|
||||||
|
category?: string
|
||||||
|
"github-auth"?: string
|
||||||
|
"disable-update-check"?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments that the user explicitly provided on the command line. All
|
* Arguments that the user explicitly provided on the command line. All
|
||||||
* arguments must be optional.
|
* arguments must be optional.
|
||||||
*
|
*
|
||||||
* For arguments with defaults see DefaultedArgs.
|
* For arguments with defaults see DefaultedArgs.
|
||||||
*/
|
*/
|
||||||
export interface UserProvidedArgs {
|
export interface UserProvidedArgs extends UserProvidedCodeArgs {
|
||||||
config?: string
|
config?: string
|
||||||
auth?: AuthType
|
auth?: AuthType
|
||||||
password?: string
|
password?: string
|
||||||
@ -45,7 +65,6 @@ export interface UserProvidedArgs {
|
|||||||
cert?: OptionalString
|
cert?: OptionalString
|
||||||
"cert-host"?: string
|
"cert-host"?: string
|
||||||
"cert-key"?: string
|
"cert-key"?: string
|
||||||
"disable-update-check"?: boolean
|
|
||||||
enable?: string[]
|
enable?: string[]
|
||||||
help?: boolean
|
help?: boolean
|
||||||
host?: string
|
host?: string
|
||||||
@ -66,21 +85,6 @@ export interface UserProvidedArgs {
|
|||||||
verbose?: boolean
|
verbose?: boolean
|
||||||
/* Positional arguments. */
|
/* Positional arguments. */
|
||||||
_?: string[]
|
_?: string[]
|
||||||
|
|
||||||
// VS Code flags.
|
|
||||||
"disable-telemetry"?: boolean
|
|
||||||
force?: boolean
|
|
||||||
"user-data-dir"?: string
|
|
||||||
"enable-proposed-api"?: string[]
|
|
||||||
"extensions-dir"?: string
|
|
||||||
"builtin-extensions-dir"?: string
|
|
||||||
"install-extension"?: string[]
|
|
||||||
"uninstall-extension"?: string[]
|
|
||||||
"list-extensions"?: boolean
|
|
||||||
"locate-extension"?: string[]
|
|
||||||
"show-versions"?: boolean
|
|
||||||
category?: string
|
|
||||||
"github-auth"?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Option<T> {
|
interface Option<T> {
|
||||||
@ -761,14 +765,37 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arguments for running Code's server.
|
||||||
|
*
|
||||||
|
* A subset of ../../lib/vscode/src/vs/server/node/serverEnvironmentService.ts:90
|
||||||
|
*/
|
||||||
|
export interface CodeArgs extends UserProvidedCodeArgs {
|
||||||
|
"accept-server-license-terms"?: boolean
|
||||||
|
"connection-token"?: string
|
||||||
|
help: boolean
|
||||||
|
port?: string
|
||||||
|
version: boolean
|
||||||
|
"without-connection-token"?: boolean
|
||||||
|
"without-browser-env-var"?: boolean
|
||||||
|
compatibility: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types for ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
||||||
|
*/
|
||||||
|
export type SpawnCodeCli = (args: CodeArgs) => Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert our arguments to VS Code server arguments.
|
* Convert our arguments to VS Code server arguments.
|
||||||
*/
|
*/
|
||||||
export const toVsCodeArgs = async (args: DefaultedArgs): Promise<CodeServerLib.ServerParsedArgs> => {
|
export const toCodeArgs = async (args: DefaultedArgs): Promise<CodeArgs> => {
|
||||||
return {
|
return {
|
||||||
"connection-token": "0000",
|
|
||||||
...args,
|
...args,
|
||||||
"accept-server-license-terms": true,
|
"accept-server-license-terms": true,
|
||||||
|
// This seems to be used to make the connection token flags optional (when
|
||||||
|
// set to 1.63) but we have always included them.
|
||||||
|
compatibility: "1.64",
|
||||||
/** Type casting. */
|
/** Type casting. */
|
||||||
help: !!args.help,
|
help: !!args.help,
|
||||||
version: !!args.version,
|
version: !!args.version,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { logger } from "@coder/logger"
|
import { logger } from "@coder/logger"
|
||||||
import { optionDescriptions, parse, readConfigFile, setDefaults, shouldOpenInExistingInstance } from "./cli"
|
import { optionDescriptions, parse, readConfigFile, setDefaults, shouldOpenInExistingInstance } from "./cli"
|
||||||
import { getVersionString, getVersionJsonString } from "./constants"
|
import { getVersionString, getVersionJsonString } from "./constants"
|
||||||
import { openInExistingInstance, runCodeServer, runVsCodeCli, shouldSpawnCliProcess } from "./main"
|
import { openInExistingInstance, runCodeServer, runCodeCli, shouldSpawnCliProcess } from "./main"
|
||||||
import { isChild, wrapper } from "./wrapper"
|
import { isChild, wrapper } from "./wrapper"
|
||||||
|
|
||||||
async function entry(): Promise<void> {
|
async function entry(): Promise<void> {
|
||||||
@ -48,7 +48,7 @@ async function entry(): Promise<void> {
|
|||||||
|
|
||||||
if (shouldSpawnCliProcess(args)) {
|
if (shouldSpawnCliProcess(args)) {
|
||||||
logger.debug("Found VS Code arguments; spawning VS Code CLI")
|
logger.debug("Found VS Code arguments; spawning VS Code CLI")
|
||||||
return runVsCodeCli(args)
|
return runCodeCli(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketPath = await shouldOpenInExistingInstance(cliArgs)
|
const socketPath = await shouldOpenInExistingInstance(cliArgs)
|
||||||
|
@ -5,7 +5,7 @@ import path from "path"
|
|||||||
import { Disposable } from "../common/emitter"
|
import { Disposable } from "../common/emitter"
|
||||||
import { plural } from "../common/util"
|
import { plural } from "../common/util"
|
||||||
import { createApp, ensureAddress } from "./app"
|
import { createApp, ensureAddress } from "./app"
|
||||||
import { AuthType, DefaultedArgs, Feature, toVsCodeArgs, UserProvidedArgs } from "./cli"
|
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
|
||||||
import { coderCloudBind } from "./coder_cloud"
|
import { coderCloudBind } from "./coder_cloud"
|
||||||
import { commit, version } from "./constants"
|
import { commit, version } from "./constants"
|
||||||
import { register } from "./routes"
|
import { register } from "./routes"
|
||||||
@ -24,27 +24,46 @@ export const shouldSpawnCliProcess = (args: UserProvidedArgs): boolean => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is useful when an CLI arg should be passed to VS Code directly,
|
* This is copy of OpenCommandPipeArgs from
|
||||||
* such as when managing extensions.
|
* ../../lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts:15
|
||||||
* @deprecated This should be removed when code-server merges with lib/vscode.
|
*
|
||||||
|
* Arguments supported by Code's socket. It can be used to perform actions from
|
||||||
|
* the CLI in a running instance of Code (for example to open a file).
|
||||||
|
*
|
||||||
|
* TODO: Can we import this (and other types) directly?
|
||||||
*/
|
*/
|
||||||
export const runVsCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
export interface OpenCommandPipeArgs {
|
||||||
logger.debug("Running VS Code CLI")
|
type: "open"
|
||||||
|
fileURIs?: string[]
|
||||||
|
folderURIs: string[]
|
||||||
|
forceNewWindow?: boolean
|
||||||
|
diffMode?: boolean
|
||||||
|
addMode?: boolean
|
||||||
|
gotoLineMode?: boolean
|
||||||
|
forceReuseWindow?: boolean
|
||||||
|
waitMarkerFilePath?: string
|
||||||
|
}
|
||||||
|
|
||||||
// See ../../lib/vscode/src/vs/server/node/server.main.js.
|
/**
|
||||||
const spawnCli = await loadAMDModule<CodeServerLib.SpawnCli>("vs/server/node/server.main", "spawnCli")
|
* Run Code's CLI for things like managing extensions.
|
||||||
|
*/
|
||||||
|
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
||||||
|
logger.debug("Running Code CLI")
|
||||||
|
|
||||||
|
// See ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
||||||
|
const spawnCli = await loadAMDModule<SpawnCodeCli>("vs/server/node/server.main", "spawnCli")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await spawnCli(await toVsCodeArgs(args))
|
await spawnCli(await toCodeArgs(args))
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error("Got error from VS Code", error)
|
logger.error("Got error from Code", error)
|
||||||
}
|
}
|
||||||
|
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
|
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
|
||||||
const pipeArgs: CodeServerLib.OpenCommandPipeArgs & { fileURIs: string[] } = {
|
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
|
||||||
type: "open",
|
type: "open",
|
||||||
folderURIs: [],
|
folderURIs: [],
|
||||||
fileURIs: [],
|
fileURIs: [],
|
||||||
@ -76,12 +95,12 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
|
|||||||
},
|
},
|
||||||
(response) => {
|
(response) => {
|
||||||
response.on("data", (message) => {
|
response.on("data", (message) => {
|
||||||
logger.debug("got message from VS Code", field("message", message.toString()))
|
logger.debug("got message from Code", field("message", message.toString()))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
vscode.on("error", (error: unknown) => {
|
vscode.on("error", (error: unknown) => {
|
||||||
logger.error("got error from VS Code", field("error", error))
|
logger.error("got error from Code", field("error", error))
|
||||||
})
|
})
|
||||||
vscode.write(JSON.stringify(pipeArgs))
|
vscode.write(JSON.stringify(pipeArgs))
|
||||||
vscode.end()
|
vscode.end()
|
||||||
|
@ -129,6 +129,14 @@ export const register = async (app: App, args: DefaultedArgs): Promise<Disposabl
|
|||||||
express.static(rootPath, {
|
express.static(rootPath, {
|
||||||
cacheControl: commit !== "development",
|
cacheControl: commit !== "development",
|
||||||
fallthrough: false,
|
fallthrough: false,
|
||||||
|
setHeaders: (res, path, stat) => {
|
||||||
|
// The service worker is served from a sub-path on the static route so
|
||||||
|
// this is required to allow it to register a higher scope (by default
|
||||||
|
// the browser only allows it to register from its own path or lower).
|
||||||
|
if (path.endsWith("/serviceWorker.js")) {
|
||||||
|
res.setHeader("Service-Worker-Allowed", "/")
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,19 +1,34 @@
|
|||||||
import { logger } from "@coder/logger"
|
import { logger } from "@coder/logger"
|
||||||
import * as express from "express"
|
import * as express from "express"
|
||||||
|
import * as http from "http"
|
||||||
|
import * as net from "net"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import { WebsocketRequest } from "../../../typings/pluginapi"
|
import { WebsocketRequest } from "../../../typings/pluginapi"
|
||||||
import { logError } from "../../common/util"
|
import { logError } from "../../common/util"
|
||||||
import { toVsCodeArgs } from "../cli"
|
import { CodeArgs, toCodeArgs } from "../cli"
|
||||||
import { isDevMode } from "../constants"
|
import { isDevMode } from "../constants"
|
||||||
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
|
import { authenticated, ensureAuthenticated, redirect, replaceTemplates, self } from "../http"
|
||||||
import { SocketProxyProvider } from "../socket"
|
import { SocketProxyProvider } from "../socket"
|
||||||
import { isFile, loadAMDModule } from "../util"
|
import { isFile, loadAMDModule } from "../util"
|
||||||
import { Router as WsRouter } from "../wsRouter"
|
import { Router as WsRouter } from "../wsRouter"
|
||||||
import { errorHandler } from "./errors"
|
|
||||||
|
/**
|
||||||
|
* This is the API of Code's web client server. code-server delegates requests
|
||||||
|
* to Code here.
|
||||||
|
*/
|
||||||
|
export interface IServerAPI {
|
||||||
|
handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>
|
||||||
|
handleUpgrade(req: http.IncomingMessage, socket: net.Socket): void
|
||||||
|
handleServerError(err: Error): void
|
||||||
|
dispose(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types for ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
||||||
|
export type CreateServer = (address: string | net.AddressInfo | null, args: CodeArgs) => Promise<IServerAPI>
|
||||||
|
|
||||||
export class CodeServerRouteWrapper {
|
export class CodeServerRouteWrapper {
|
||||||
/** Assigned in `ensureCodeServerLoaded` */
|
/** Assigned in `ensureCodeServerLoaded` */
|
||||||
private _codeServerMain!: CodeServerLib.IServerAPI
|
private _codeServerMain!: IServerAPI
|
||||||
private _wsRouterWrapper = WsRouter()
|
private _wsRouterWrapper = WsRouter()
|
||||||
private _socketProxyProvider = new SocketProxyProvider()
|
private _socketProxyProvider = new SocketProxyProvider()
|
||||||
public router = express.Router()
|
public router = express.Router()
|
||||||
@ -24,6 +39,32 @@ export class CodeServerRouteWrapper {
|
|||||||
|
|
||||||
//#region Route Handlers
|
//#region Route Handlers
|
||||||
|
|
||||||
|
private manifest: express.Handler = async (req, res, next) => {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/manifest+json" })
|
||||||
|
|
||||||
|
return res.end(
|
||||||
|
replaceTemplates(
|
||||||
|
req,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
name: "code-server",
|
||||||
|
short_name: "code-server",
|
||||||
|
start_url: ".",
|
||||||
|
display: "fullscreen",
|
||||||
|
description: "Run Code on a remote server.",
|
||||||
|
icons: [192, 512].map((size) => ({
|
||||||
|
src: `{{BASE}}/_static/src/browser/media/pwa-icon-${size}.png`,
|
||||||
|
type: "image/png",
|
||||||
|
sizes: `${size}x${size}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private $root: express.Handler = async (req, res, next) => {
|
private $root: express.Handler = async (req, res, next) => {
|
||||||
const isAuthenticated = await authenticated(req)
|
const isAuthenticated = await authenticated(req)
|
||||||
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
|
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
|
||||||
@ -81,17 +122,6 @@ export class CodeServerRouteWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private $proxyRequest: express.Handler = async (req, res, next) => {
|
private $proxyRequest: express.Handler = async (req, res, next) => {
|
||||||
// We allow certain errors to propagate so that other routers may handle requests
|
|
||||||
// outside VS Code
|
|
||||||
const requestErrorHandler = (error: any) => {
|
|
||||||
if (error instanceof Error && ["EntryNotFound", "FileNotFound", "HttpError"].includes(error.message)) {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
errorHandler(error, req, res, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.once("error", requestErrorHandler)
|
|
||||||
|
|
||||||
this._codeServerMain.handleRequest(req, res)
|
this._codeServerMain.handleRequest(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,15 +147,14 @@ export class CodeServerRouteWrapper {
|
|||||||
|
|
||||||
const { args } = req
|
const { args } = req
|
||||||
|
|
||||||
/**
|
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
||||||
* @file ../../../lib/vscode/src/vs/server/node/server.main.js
|
const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")
|
||||||
*/
|
|
||||||
const createVSServer = await loadAMDModule<CodeServerLib.CreateServer>("vs/server/node/server.main", "createServer")
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._codeServerMain = await createVSServer(null, {
|
this._codeServerMain = await createVSServer(null, {
|
||||||
...(await toVsCodeArgs(args)),
|
...(await toCodeArgs(args)),
|
||||||
// TODO: Make the browser helper script work.
|
// TODO: Make the browser helper script work.
|
||||||
|
"without-connection-token": true,
|
||||||
"without-browser-env-var": true,
|
"without-browser-env-var": true,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -141,6 +170,7 @@ export class CodeServerRouteWrapper {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
||||||
|
this.router.get(/manifest.json$/, this.manifest)
|
||||||
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
||||||
this._wsRouterWrapper.ws("/", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
this._wsRouterWrapper.ws("/", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
setDefaults,
|
setDefaults,
|
||||||
shouldOpenInExistingInstance,
|
shouldOpenInExistingInstance,
|
||||||
splitOnFirstEquals,
|
splitOnFirstEquals,
|
||||||
toVsCodeArgs,
|
toCodeArgs,
|
||||||
optionDescriptions,
|
optionDescriptions,
|
||||||
options,
|
options,
|
||||||
Options,
|
Options,
|
||||||
@ -727,11 +727,11 @@ describe("readSocketPath", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("toVsCodeArgs", () => {
|
describe("toCodeArgs", () => {
|
||||||
const vscodeDefaults = {
|
const vscodeDefaults = {
|
||||||
...defaults,
|
...defaults,
|
||||||
"connection-token": "0000",
|
|
||||||
"accept-server-license-terms": true,
|
"accept-server-license-terms": true,
|
||||||
|
compatibility: "1.64",
|
||||||
help: false,
|
help: false,
|
||||||
port: "8080",
|
port: "8080",
|
||||||
version: false,
|
version: false,
|
||||||
@ -744,7 +744,7 @@ describe("toVsCodeArgs", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should convert empty args", async () => {
|
it("should convert empty args", async () => {
|
||||||
expect(await toVsCodeArgs(await setDefaults(parse([])))).toStrictEqual({
|
expect(await toCodeArgs(await setDefaults(parse([])))).toStrictEqual({
|
||||||
...vscodeDefaults,
|
...vscodeDefaults,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -752,7 +752,7 @@ describe("toVsCodeArgs", () => {
|
|||||||
it("should ignore regular file", async () => {
|
it("should ignore regular file", async () => {
|
||||||
const file = path.join(await tmpdir(testName), "file")
|
const file = path.join(await tmpdir(testName), "file")
|
||||||
await fs.writeFile(file, "foobar")
|
await fs.writeFile(file, "foobar")
|
||||||
expect(await toVsCodeArgs(await setDefaults(parse([file])))).toStrictEqual({
|
expect(await toCodeArgs(await setDefaults(parse([file])))).toStrictEqual({
|
||||||
...vscodeDefaults,
|
...vscodeDefaults,
|
||||||
_: [file],
|
_: [file],
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user