mirror of https://github.com/coder/code-server.git
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
|
@ -110,6 +110,12 @@ jobs:
|
|||
fetch-depth: 0
|
||||
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
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
|
@ -132,25 +138,22 @@ jobs:
|
|||
run: yarn build
|
||||
|
||||
# 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
|
||||
# rebuild.
|
||||
# different and we need to rebuild.
|
||||
- name: Get latest lib/vscode rev
|
||||
id: vscode-rev
|
||||
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)"
|
||||
|
||||
- name: Fetch Code build from cache
|
||||
id: cache-vscode-2
|
||||
# We need to rebuild when we have a new version of Code or when any of
|
||||
# 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
|
||||
with:
|
||||
path: |
|
||||
lib/vscode/.build
|
||||
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 }}
|
||||
path: lib/vscode-reh-web-*
|
||||
key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ hashFiles('patches/*.diff') }}
|
||||
|
||||
- name: Build vscode
|
||||
if: steps.cache-vscode-2.outputs.cache-hit != 'true'
|
||||
if: steps.cache-vscode.outputs.cache-hit != 'true'
|
||||
run: yarn build:vscode
|
||||
|
||||
# Our code imports code from VS Code's `out` directory meaning VS Code
|
||||
|
|
|
@ -8,12 +8,17 @@ release-packages/
|
|||
release-gcp/
|
||||
release-images/
|
||||
node_modules
|
||||
vendor/modules
|
||||
node-*
|
||||
/plugins
|
||||
/lib/coder-cloud-agent
|
||||
.home
|
||||
coverage
|
||||
**/.DS_Store
|
||||
|
||||
# Code packages itself here.
|
||||
/lib/vscode-reh-web-*
|
||||
|
||||
# Failed e2e test videos are saved here
|
||||
test/test-results
|
||||
|
||||
# Quilt's internal data.
|
||||
/.pc
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "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
|
||||
|
||||
# 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"
|
||||
NFPM_ARCH="$(get_nfpm_arch $PKG_FORMAT "$ARCH")"
|
||||
nfpm_config="$(envsubst < ./ci/build/nfpm.yaml)"
|
||||
|
|
|
@ -66,31 +66,10 @@ EOF
|
|||
|
||||
bundle_vscode() {
|
||||
mkdir -p "$VSCODE_OUT_PATH"
|
||||
rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH"
|
||||
rsync "$VSCODE_SRC_PATH/out-vscode-reh-web${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out"
|
||||
rsync ./lib/vscode-reh-web-*/ "$VSCODE_OUT_PATH"
|
||||
|
||||
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
|
||||
if [ "$KEEP_MODULES" = 0 ]; then
|
||||
rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules"
|
||||
else
|
||||
rsync "$VSCODE_SRC_PATH/node_modules/" "$VSCODE_OUT_PATH/node_modules"
|
||||
fi
|
||||
rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions"
|
||||
rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions"
|
||||
rsync "$VSCODE_SRC_PATH/extensions/postinstall.js" "$VSCODE_OUT_PATH/extensions"
|
||||
|
||||
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.
|
||||
# Add the commit, date, our name, links, 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" <(
|
||||
cat << EOF
|
||||
{
|
||||
|
@ -98,15 +77,48 @@ bundle_vscode() {
|
|||
"commit": "$(cd "$VSCODE_SRC_PATH" && git rev-parse HEAD)",
|
||||
"quality": "stable",
|
||||
"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
|
||||
) > "$VSCODE_OUT_PATH/product.json"
|
||||
|
||||
# We remove the scripts field so that later on we can run
|
||||
# yarn to fetch node_modules if necessary without build scripts running.
|
||||
# We cannot use --no-scripts because we still want dependent package scripts to run.
|
||||
jq 'del(.scripts)' < "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json"
|
||||
# Use the package.json for the web/remote server. It does not have the right
|
||||
# version though so pull that from the main package.json. Also remove keytar
|
||||
# since the web does not rely on it and that removes the dependency on
|
||||
# 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"
|
||||
symlink_asar
|
||||
|
|
|
@ -29,11 +29,6 @@ main() {
|
|||
|
||||
cd "$RELEASE_PATH"
|
||||
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 "$@"
|
||||
|
|
|
@ -14,11 +14,17 @@ main() {
|
|||
# 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
|
||||
# that case symlink `out` to a production build directory.
|
||||
local vscode="lib/vscode"
|
||||
local link="$vscode/out"
|
||||
local target="out-build"
|
||||
if [[ ! -e $link ]] && [[ -d $vscode/$target ]]; then
|
||||
ln -s "$target" "$link"
|
||||
if [[ ! -e lib/vscode/out ]]; then
|
||||
pushd lib
|
||||
local out=(vscode-reh-web-*)
|
||||
if [[ -d "${out[0]}" ]]; then
|
||||
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
|
||||
|
||||
# We must keep jest in a sub-directory. See ../../test/package.json for more
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
- [Creating pull requests](#creating-pull-requests)
|
||||
- [Commits and commit history](#commits-and-commit-history)
|
||||
- [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)
|
||||
- [Help](#help)
|
||||
- [Test](#test)
|
||||
|
@ -16,7 +17,7 @@
|
|||
- [Integration tests](#integration-tests)
|
||||
- [End-to-end tests](#end-to-end-tests)
|
||||
- [Structure](#structure)
|
||||
- [Modifications to VS Code](#modifications-to-vs-code)
|
||||
- [Modifications to Code](#modifications-to-code)
|
||||
- [Currently Known Issues](#currently-known-issues)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
@ -44,6 +45,8 @@ Here is what is needed:
|
|||
signature
|
||||
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)
|
||||
- `quilt`
|
||||
- Used to manage patches to Code
|
||||
- `rsync` and `unzip`
|
||||
- Used for code-server releases
|
||||
- `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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
@ -78,26 +81,44 @@ we'll guide you.
|
|||
|
||||
## 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`
|
||||
2. `git submodule update --init` - Clone `vscode` submodule
|
||||
3. `yarn` - Install dependencies
|
||||
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.
|
||||
3. `quilt push -a` - Apply patches to the `vscode` submodule.
|
||||
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`.
|
||||
3. Test code-server locally to make sure everything works.
|
||||
4. Check the Node.js version that's used by Electron (which is shipped with VS
|
||||
3. Apply the patches (`quilt push -a`) or restore your stashed changes. At this
|
||||
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.
|
||||
5. Commit the updated submodule to `code-server`.
|
||||
6. Open a PR.
|
||||
6. Commit the updated submodule and patches to `code-server`.
|
||||
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
|
||||
|
||||
|
@ -193,99 +214,46 @@ code-server running locally. In CI, this is taken care of for you.
|
|||
|
||||
## 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.
|
||||
|
||||
The CLI code is in [src/node](../src/node) and the HTTP routes are implemented
|
||||
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.
|
||||
|
||||
### 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
|
||||
front-end and a server. The front-end consisted of the UI code, while the server
|
||||
ran the extensions and exposed an API to the front-end for file access and all
|
||||
UI needs.
|
||||
Our modifications to Code can be found in the [patches](../patches) directory.
|
||||
We pull in Code as a submodule pointing to an upstream release branch.
|
||||
|
||||
Over time, Microsoft added support to VS Code to run it on the web. They have
|
||||
made the front-end open source, but not the server. As such, code-server v2 (and
|
||||
later) uses the VS Code front-end and implements the server. We do this by using
|
||||
a Git subtree to fork and modify VS Code. This code lives under
|
||||
[lib/vscode](../lib/vscode).
|
||||
In v1 of code-server, we had Code as a submodule and used a single massive patch
|
||||
that split the codebase into a front-end and a server. The front-end consisted
|
||||
of the UI code, while the server ran the extensions and exposed an API to the
|
||||
front-end for file access and all UI needs.
|
||||
|
||||
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
|
||||
- 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)
|
||||
- Allowing extra extension directories
|
||||
- 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)
|
||||
Microsoft eventually made the server open source and we were able to reduce our
|
||||
changes significantly. Some time later we moved back to a submodule and patches
|
||||
(managed by `quilt` this time instead of the mega-patch).
|
||||
|
||||
As the web portion of VS Code matures, we'll be able to shrink and possibly
|
||||
eliminate our modifications. In the meantime, upgrading the VS Code version requires
|
||||
us to ensure that our changes are still applied and work as intended. In the future,
|
||||
we'd like to run VS Code unit tests against our builds to ensure that features
|
||||
work as expected.
|
||||
As the web portion of Code continues to mature, we'll be able to shrink and
|
||||
possibly eliminate our patches. In the meantime, upgrading the Code version
|
||||
requires us to ensure that our changes are still applied correctly and work as
|
||||
intended. In the future, we'd like to run Code unit tests against our builds to
|
||||
ensure that features work as expected.
|
||||
|
||||
> 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.
|
||||
|
||||
### 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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a13f6e1434ad6ab820eef0ecca5b923b3e275667
|
||||
Subproject commit f80445acd5a3dadef24aa209168452a3d97cc326
|
|
@ -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, {
|
|
@ -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);
|
|
@ -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';
|
||||
|
|
@ -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 || []),
|
||||
+ ])
|
||||
+ }));
|
||||
+ })
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
+ },
|
||||
+ },
|
||||
+ ],
|
||||
+ },
|
||||
+ });
|
||||
+ }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
- })();
|
||||
}
|
||||
}
|
||||
|
|
@ -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'); }
|
|
@ -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,
|
|
@ -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'),
|
||||
+ },
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
}
|
|
@ -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)) : '')
|
|
@ -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"
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 ----- */
|
||||
|
|
@ -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
|
|
@ -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> {}
|
||||
|
||||
/**
|
||||
* Code flags provided by the user.
|
||||
*/
|
||||
export interface UserProvidedCodeArgs {
|
||||
"disable-telemetry"?: boolean
|
||||
force?: boolean
|
||||
"user-data-dir"?: string
|
||||
"enable-proposed-api"?: string[]
|
||||
"extensions-dir"?: string
|
||||
"builtin-extensions-dir"?: string
|
||||
"install-extension"?: string[]
|
||||
"uninstall-extension"?: string[]
|
||||
"list-extensions"?: boolean
|
||||
"locate-extension"?: string[]
|
||||
"show-versions"?: boolean
|
||||
category?: string
|
||||
"github-auth"?: string
|
||||
"disable-update-check"?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments that the user explicitly provided on the command line. All
|
||||
* arguments must be optional.
|
||||
*
|
||||
* For arguments with defaults see DefaultedArgs.
|
||||
*/
|
||||
export interface UserProvidedArgs {
|
||||
export interface UserProvidedArgs extends UserProvidedCodeArgs {
|
||||
config?: string
|
||||
auth?: AuthType
|
||||
password?: string
|
||||
|
@ -45,7 +65,6 @@ export interface UserProvidedArgs {
|
|||
cert?: OptionalString
|
||||
"cert-host"?: string
|
||||
"cert-key"?: string
|
||||
"disable-update-check"?: boolean
|
||||
enable?: string[]
|
||||
help?: boolean
|
||||
host?: string
|
||||
|
@ -66,21 +85,6 @@ export interface UserProvidedArgs {
|
|||
verbose?: boolean
|
||||
/* Positional arguments. */
|
||||
_?: string[]
|
||||
|
||||
// VS Code flags.
|
||||
"disable-telemetry"?: boolean
|
||||
force?: boolean
|
||||
"user-data-dir"?: string
|
||||
"enable-proposed-api"?: string[]
|
||||
"extensions-dir"?: string
|
||||
"builtin-extensions-dir"?: string
|
||||
"install-extension"?: string[]
|
||||
"uninstall-extension"?: string[]
|
||||
"list-extensions"?: boolean
|
||||
"locate-extension"?: string[]
|
||||
"show-versions"?: boolean
|
||||
category?: string
|
||||
"github-auth"?: string
|
||||
}
|
||||
|
||||
interface Option<T> {
|
||||
|
@ -761,14 +765,37 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
|
|||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for running Code's server.
|
||||
*
|
||||
* A subset of ../../lib/vscode/src/vs/server/node/serverEnvironmentService.ts:90
|
||||
*/
|
||||
export interface CodeArgs extends UserProvidedCodeArgs {
|
||||
"accept-server-license-terms"?: boolean
|
||||
"connection-token"?: string
|
||||
help: boolean
|
||||
port?: string
|
||||
version: boolean
|
||||
"without-connection-token"?: boolean
|
||||
"without-browser-env-var"?: boolean
|
||||
compatibility: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Types for ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
||||
*/
|
||||
export type SpawnCodeCli = (args: CodeArgs) => Promise<void>
|
||||
|
||||
/**
|
||||
* Convert our arguments to VS Code server arguments.
|
||||
*/
|
||||
export const toVsCodeArgs = async (args: DefaultedArgs): Promise<CodeServerLib.ServerParsedArgs> => {
|
||||
export const toCodeArgs = async (args: DefaultedArgs): Promise<CodeArgs> => {
|
||||
return {
|
||||
"connection-token": "0000",
|
||||
...args,
|
||||
"accept-server-license-terms": true,
|
||||
// This seems to be used to make the connection token flags optional (when
|
||||
// set to 1.63) but we have always included them.
|
||||
compatibility: "1.64",
|
||||
/** Type casting. */
|
||||
help: !!args.help,
|
||||
version: !!args.version,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { logger } from "@coder/logger"
|
||||
import { optionDescriptions, parse, readConfigFile, setDefaults, shouldOpenInExistingInstance } from "./cli"
|
||||
import { getVersionString, getVersionJsonString } from "./constants"
|
||||
import { openInExistingInstance, runCodeServer, runVsCodeCli, shouldSpawnCliProcess } from "./main"
|
||||
import { openInExistingInstance, runCodeServer, runCodeCli, shouldSpawnCliProcess } from "./main"
|
||||
import { isChild, wrapper } from "./wrapper"
|
||||
|
||||
async function entry(): Promise<void> {
|
||||
|
@ -48,7 +48,7 @@ async function entry(): Promise<void> {
|
|||
|
||||
if (shouldSpawnCliProcess(args)) {
|
||||
logger.debug("Found VS Code arguments; spawning VS Code CLI")
|
||||
return runVsCodeCli(args)
|
||||
return runCodeCli(args)
|
||||
}
|
||||
|
||||
const socketPath = await shouldOpenInExistingInstance(cliArgs)
|
||||
|
|
|
@ -5,7 +5,7 @@ import path from "path"
|
|||
import { Disposable } from "../common/emitter"
|
||||
import { plural } from "../common/util"
|
||||
import { createApp, ensureAddress } from "./app"
|
||||
import { AuthType, DefaultedArgs, Feature, toVsCodeArgs, UserProvidedArgs } from "./cli"
|
||||
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
|
||||
import { coderCloudBind } from "./coder_cloud"
|
||||
import { commit, version } from "./constants"
|
||||
import { register } from "./routes"
|
||||
|
@ -24,27 +24,46 @@ export const shouldSpawnCliProcess = (args: UserProvidedArgs): boolean => {
|
|||
}
|
||||
|
||||
/**
|
||||
* This is useful when an CLI arg should be passed to VS Code directly,
|
||||
* such as when managing extensions.
|
||||
* @deprecated This should be removed when code-server merges with lib/vscode.
|
||||
* This is copy of OpenCommandPipeArgs from
|
||||
* ../../lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts:15
|
||||
*
|
||||
* Arguments supported by Code's socket. It can be used to perform actions from
|
||||
* the CLI in a running instance of Code (for example to open a file).
|
||||
*
|
||||
* TODO: Can we import this (and other types) directly?
|
||||
*/
|
||||
export const runVsCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
||||
logger.debug("Running VS Code CLI")
|
||||
export interface OpenCommandPipeArgs {
|
||||
type: "open"
|
||||
fileURIs?: string[]
|
||||
folderURIs: string[]
|
||||
forceNewWindow?: boolean
|
||||
diffMode?: boolean
|
||||
addMode?: boolean
|
||||
gotoLineMode?: boolean
|
||||
forceReuseWindow?: boolean
|
||||
waitMarkerFilePath?: string
|
||||
}
|
||||
|
||||
// See ../../lib/vscode/src/vs/server/node/server.main.js.
|
||||
const spawnCli = await loadAMDModule<CodeServerLib.SpawnCli>("vs/server/node/server.main", "spawnCli")
|
||||
/**
|
||||
* Run Code's CLI for things like managing extensions.
|
||||
*/
|
||||
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
||||
logger.debug("Running Code CLI")
|
||||
|
||||
// See ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
||||
const spawnCli = await loadAMDModule<SpawnCodeCli>("vs/server/node/server.main", "spawnCli")
|
||||
|
||||
try {
|
||||
await spawnCli(await toVsCodeArgs(args))
|
||||
await spawnCli(await toCodeArgs(args))
|
||||
} catch (error: any) {
|
||||
logger.error("Got error from VS Code", error)
|
||||
logger.error("Got error from Code", error)
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
|
||||
const pipeArgs: CodeServerLib.OpenCommandPipeArgs & { fileURIs: string[] } = {
|
||||
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
|
||||
type: "open",
|
||||
folderURIs: [],
|
||||
fileURIs: [],
|
||||
|
@ -76,12 +95,12 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
|
|||
},
|
||||
(response) => {
|
||||
response.on("data", (message) => {
|
||||
logger.debug("got message from VS Code", field("message", message.toString()))
|
||||
logger.debug("got message from Code", field("message", message.toString()))
|
||||
})
|
||||
},
|
||||
)
|
||||
vscode.on("error", (error: unknown) => {
|
||||
logger.error("got error from VS Code", field("error", error))
|
||||
logger.error("got error from Code", field("error", error))
|
||||
})
|
||||
vscode.write(JSON.stringify(pipeArgs))
|
||||
vscode.end()
|
||||
|
|
|
@ -129,6 +129,14 @@ export const register = async (app: App, args: DefaultedArgs): Promise<Disposabl
|
|||
express.static(rootPath, {
|
||||
cacheControl: commit !== "development",
|
||||
fallthrough: false,
|
||||
setHeaders: (res, path, stat) => {
|
||||
// The service worker is served from a sub-path on the static route so
|
||||
// this is required to allow it to register a higher scope (by default
|
||||
// the browser only allows it to register from its own path or lower).
|
||||
if (path.endsWith("/serviceWorker.js")) {
|
||||
res.setHeader("Service-Worker-Allowed", "/")
|
||||
}
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
import { logger } from "@coder/logger"
|
||||
import * as express from "express"
|
||||
import * as http from "http"
|
||||
import * as net from "net"
|
||||
import * as path from "path"
|
||||
import { WebsocketRequest } from "../../../typings/pluginapi"
|
||||
import { logError } from "../../common/util"
|
||||
import { toVsCodeArgs } from "../cli"
|
||||
import { CodeArgs, toCodeArgs } from "../cli"
|
||||
import { isDevMode } from "../constants"
|
||||
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
|
||||
import { authenticated, ensureAuthenticated, redirect, replaceTemplates, self } from "../http"
|
||||
import { SocketProxyProvider } from "../socket"
|
||||
import { isFile, loadAMDModule } from "../util"
|
||||
import { Router as WsRouter } from "../wsRouter"
|
||||
import { errorHandler } from "./errors"
|
||||
|
||||
/**
|
||||
* This is the API of Code's web client server. code-server delegates requests
|
||||
* to Code here.
|
||||
*/
|
||||
export interface IServerAPI {
|
||||
handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>
|
||||
handleUpgrade(req: http.IncomingMessage, socket: net.Socket): void
|
||||
handleServerError(err: Error): void
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
// Types for ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
||||
export type CreateServer = (address: string | net.AddressInfo | null, args: CodeArgs) => Promise<IServerAPI>
|
||||
|
||||
export class CodeServerRouteWrapper {
|
||||
/** Assigned in `ensureCodeServerLoaded` */
|
||||
private _codeServerMain!: CodeServerLib.IServerAPI
|
||||
private _codeServerMain!: IServerAPI
|
||||
private _wsRouterWrapper = WsRouter()
|
||||
private _socketProxyProvider = new SocketProxyProvider()
|
||||
public router = express.Router()
|
||||
|
@ -24,6 +39,32 @@ export class CodeServerRouteWrapper {
|
|||
|
||||
//#region Route Handlers
|
||||
|
||||
private manifest: express.Handler = async (req, res, next) => {
|
||||
res.writeHead(200, { "Content-Type": "application/manifest+json" })
|
||||
|
||||
return res.end(
|
||||
replaceTemplates(
|
||||
req,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "code-server",
|
||||
short_name: "code-server",
|
||||
start_url: ".",
|
||||
display: "fullscreen",
|
||||
description: "Run Code on a remote server.",
|
||||
icons: [192, 512].map((size) => ({
|
||||
src: `{{BASE}}/_static/src/browser/media/pwa-icon-${size}.png`,
|
||||
type: "image/png",
|
||||
sizes: `${size}x${size}`,
|
||||
})),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private $root: express.Handler = async (req, res, next) => {
|
||||
const isAuthenticated = await authenticated(req)
|
||||
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
|
||||
|
@ -81,17 +122,6 @@ export class CodeServerRouteWrapper {
|
|||
}
|
||||
|
||||
private $proxyRequest: express.Handler = async (req, res, next) => {
|
||||
// We allow certain errors to propagate so that other routers may handle requests
|
||||
// outside VS Code
|
||||
const requestErrorHandler = (error: any) => {
|
||||
if (error instanceof Error && ["EntryNotFound", "FileNotFound", "HttpError"].includes(error.message)) {
|
||||
next()
|
||||
}
|
||||
errorHandler(error, req, res, next)
|
||||
}
|
||||
|
||||
req.once("error", requestErrorHandler)
|
||||
|
||||
this._codeServerMain.handleRequest(req, res)
|
||||
}
|
||||
|
||||
|
@ -117,15 +147,14 @@ export class CodeServerRouteWrapper {
|
|||
|
||||
const { args } = req
|
||||
|
||||
/**
|
||||
* @file ../../../lib/vscode/src/vs/server/node/server.main.js
|
||||
*/
|
||||
const createVSServer = await loadAMDModule<CodeServerLib.CreateServer>("vs/server/node/server.main", "createServer")
|
||||
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
||||
const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")
|
||||
|
||||
try {
|
||||
this._codeServerMain = await createVSServer(null, {
|
||||
...(await toVsCodeArgs(args)),
|
||||
...(await toCodeArgs(args)),
|
||||
// TODO: Make the browser helper script work.
|
||||
"without-connection-token": true,
|
||||
"without-browser-env-var": true,
|
||||
})
|
||||
} catch (error) {
|
||||
|
@ -141,6 +170,7 @@ export class CodeServerRouteWrapper {
|
|||
|
||||
constructor() {
|
||||
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
||||
this.router.get(/manifest.json$/, this.manifest)
|
||||
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
||||
this._wsRouterWrapper.ws("/", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
setDefaults,
|
||||
shouldOpenInExistingInstance,
|
||||
splitOnFirstEquals,
|
||||
toVsCodeArgs,
|
||||
toCodeArgs,
|
||||
optionDescriptions,
|
||||
options,
|
||||
Options,
|
||||
|
@ -727,11 +727,11 @@ describe("readSocketPath", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("toVsCodeArgs", () => {
|
||||
describe("toCodeArgs", () => {
|
||||
const vscodeDefaults = {
|
||||
...defaults,
|
||||
"connection-token": "0000",
|
||||
"accept-server-license-terms": true,
|
||||
compatibility: "1.64",
|
||||
help: false,
|
||||
port: "8080",
|
||||
version: false,
|
||||
|
@ -744,7 +744,7 @@ describe("toVsCodeArgs", () => {
|
|||
})
|
||||
|
||||
it("should convert empty args", async () => {
|
||||
expect(await toVsCodeArgs(await setDefaults(parse([])))).toStrictEqual({
|
||||
expect(await toCodeArgs(await setDefaults(parse([])))).toStrictEqual({
|
||||
...vscodeDefaults,
|
||||
})
|
||||
})
|
||||
|
@ -752,7 +752,7 @@ describe("toVsCodeArgs", () => {
|
|||
it("should ignore regular file", async () => {
|
||||
const file = path.join(await tmpdir(testName), "file")
|
||||
await fs.writeFile(file, "foobar")
|
||||
expect(await toVsCodeArgs(await setDefaults(parse([file])))).toStrictEqual({
|
||||
expect(await toCodeArgs(await setDefaults(parse([file])))).toStrictEqual({
|
||||
...vscodeDefaults,
|
||||
_: [file],
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue