Compare commits

..

66 Commits

Author SHA1 Message Date
DevMirza
5428442628 feat(lang): Add Urdu Locale (#6444) 2023-09-22 17:16:51 -08:00
Dov Benyomin Sohacheski
020f280458 Add .local mount to example Docker command (#6419) 2023-09-22 17:07:07 -08:00
Asher
58f6e24a07 Fix installation of kerberos module on armv7l (#6442)
Also build with the same version of Node we will release with.
2023-09-22 00:54:14 -08:00
Asher
7868f4db23 Fix cross-compilation (#6441)
* Avoid packaging yarn.lock

Since the shrinkwrap is what we want everything to use.

* Build with npm

It seems we stuck with yarn because npm was giving us errors but I will try
sorting it out now so we can build with npm as originally intended.

* Remove build from source

Not using CentOS 7 anymore so based on the comment we no longer need
this.  Keytar seems to install fine now.

* Update missed Node version

These numbers are all over the place.

* npm_config_arch must be lowercase

* Patch out Kerberos

I am not sure exactly how it is used but I think it is not a path code-server
worries about, at least not right now.  Just going to patch it out rather than
figure out how to build it on armv7l but we can revisit later.
2023-09-21 23:21:01 -08:00
Asher
acc50a5d36 Update dependencies and force-update qs (#6440)
* Update dependencies and force-update qs

This is mainly an attempt to get rid of as many resolutions as possible
since it seems they are unnecessary except for qs (according to yarn/npm
audit).

For qs use 6.9.7 since Express is using 6.9.6 and that matches the most
closely.

Also add overrides since this is npm's version of yarn's resolutions and
we need it for the shrinkwrap to generate with the right dependencies.

Decided to keep pinning @types/node as well although I am not sure it is
necessary.  Express is pulling in v20 types.  Since this is
development-only we only need it in resolutions.

* Run formatter

Some rules seem to have changed with the dependency updates.

* Replace deprecated bodyParser.json() usage

* Audit npm shrinkwrap as well

* Skip installing dependencies in audit

It seems the tools only require the lock files.

* Fix tests when using ipv6

* Add missing openssl dependency to flake
2023-09-21 16:13:34 -08:00
Asher
47ee7ae670 Bump minimum glibc to 2.28 (#6439)
At least, for the standalone and for anyone running on default Node 18.

If support for 2.17 is needed then one would need to build Node 18 with 2.17 and then build code-server with that version (specifically, the native npm modules).
2023-09-21 13:42:17 -08:00
Asher
70d0c603cc Update to VS Code 1.82.2 (#6436)
* Update VS Code to 1.82.2

* Add new libkrb5 dependency

* Update patches

The only changes were to context except:

- The URL callback provider uses a new _callbackRoute argument and moved
  locations.
- The telemetry provider gets passed the request service as the first
  argument now.
- CSP hash changed, as usual.

* Update Node to v18

* Revert back to es2020

es2022 is breaking Safari.
2023-09-20 15:33:28 -08:00
dependabot[bot]
2e29c233ea chore: bump tj-actions/changed-files from 37 to 38 (#6417)
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 37 to 38.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v37...v38)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:54:56 -08:00
dependabot[bot]
331a017309 chore: bump argon2 from 0.30.3 to 0.31.0 (#6415)
Bumps [argon2](https://github.com/ranisalt/node-argon2) from 0.30.3 to 0.31.0.
- [Release notes](https://github.com/ranisalt/node-argon2/releases)
- [Commits](https://github.com/ranisalt/node-argon2/compare/v0.30.3...v0.31.0)

---
updated-dependencies:
- dependency-name: argon2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:53:31 -08:00
dependabot[bot]
f915d1e2c8 chore: bump eslint from 8.44.0 to 8.48.0 (#6414)
Bumps [eslint](https://github.com/eslint/eslint) from 8.44.0 to 8.48.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.44.0...v8.48.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 11:52:54 -08:00
Kaoru Yamamoto
95bcf101d7 Add Japanese locale (#6433) 2023-09-18 17:23:03 +00:00
James MacDonald
d80568df20 Add pod annotations (#6432) 2023-09-16 00:31:05 +00:00
Asher
03dc8cd808 Propagate post-installation failures
pipefail might be ideal here but not sure how wide the support is yet
considering this may run on plain sh.
2023-09-11 09:27:29 -08:00
Asher
3e1c00e017 Run formatter
Not sure why CI missed these, will have to look into it.
2023-09-11 09:27:28 -08:00
Cyanoure
a76e5241b6 Add "CODE_SERVER_HOST" environment variable (#6423) 2023-09-08 22:50:11 +00:00
Asher
913fc30866 Upgrade @coder/logger (#6409)
This gets rid of the unused peer dependency that was pulling in
protobufjs.
2023-08-30 12:10:34 -08:00
Asher
eb498b0d6d Remove humanPath (#6404)
The tilde is ambiguous and it can be helpful to know exactly what paths
code-server is trying to use, especially if it is running as a different
user than you expected.
2023-08-29 14:25:24 -08:00
Antoine
eb8099ff89 feat(helm): Don't create Secret if existingSecret is provided (#6397) 2023-08-28 11:01:11 -08:00
Efren
56d3548fa2 Add braces to Caddyfile example (#6382) 2023-08-10 00:44:23 +00:00
jothi prasath
2d335bd1ea Update screenshots (#6372) 2023-08-07 19:02:54 +00:00
dependabot[bot]
291682e1c0 chore: bump i18next from 23.2.11 to 23.4.1 (#6362)
Bumps [i18next](https://github.com/i18next/i18next) from 23.2.11 to 23.4.1.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v23.2.11...v23.4.1)

---
updated-dependencies:
- dependency-name: i18next
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 10:17:13 -08:00
dependabot[bot]
97aeb77abe chore: bump eslint-plugin-import from 2.27.5 to 2.28.0 (#6363)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.27.5 to 2.28.0.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.27.5...v2.28.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 10:16:13 -08:00
dependabot[bot]
76fd2389e4 chore: bump proxy-agent from 6.2.1 to 6.3.0 (#6361)
Bumps [proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/proxy-agent) from 6.2.1 to 6.3.0.
- [Release notes](https://github.com/TooTallNate/proxy-agents/releases)
- [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/proxy-agent/CHANGELOG.md)
- [Commits](https://github.com/TooTallNate/proxy-agents/commits/proxy-agent@6.3.0/packages/proxy-agent)

---
updated-dependencies:
- dependency-name: proxy-agent
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 09:51:46 -08:00
Asher
3cb92edc76 Release v4.16.1 2023-07-31 11:21:45 -08:00
Olivier Benz
94ef3776ad Update Code to 1.80.2 (#6357) 2023-07-31 09:14:29 -08:00
Asher
a82f1ceaa5 Add missing FAQ table of contents entry 2023-07-28 17:46:36 -08:00
Asher
25254214fb Release v4.16.0 2023-07-28 17:32:57 -08:00
Asher
feb5e6770c Add entry for location of configuration
Closes #6324.
2023-07-28 17:32:48 -08:00
Asher
e37b35278d Account for disabled proxy in startup logs 2023-07-27 09:26:11 -08:00
Asher
2ffbcfdfcb Release v4.15.0 2023-07-21 15:30:03 -08:00
Ryan Brainard
74da5167a2 Add --disable-proxy option (#6349) 2023-07-21 15:23:21 -08:00
dependabot[bot]
daac46b3cf chore: bump i18next from 22.5.1 to 23.2.11 (#6326)
Bumps [i18next](https://github.com/i18next/i18next) from 22.5.1 to 23.2.11.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v22.5.1...v23.2.11)

---
updated-dependencies:
- dependency-name: i18next
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-20 14:03:21 -05:00
dependabot[bot]
ea3c79ffd1 chore: bump @typescript-eslint/eslint-plugin from 5.59.1 to 5.62.0 (#6327)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.59.1 to 5.62.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.62.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-20 13:31:37 -05:00
Alex Thillen
93e60f7b0e add trusted-origins cli argument (#6319) 2023-07-19 14:04:03 -08:00
dependabot[bot]
7926647058 chore: bump tough-cookie from 4.0.0 to 4.1.3 in /test (#6318)
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.0.0 to 4.1.3.
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.0.0...v4.1.3)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 14:02:19 -08:00
dependabot[bot]
d27cd43dce chore: bump @typescript-eslint/parser from 5.61.0 to 5.62.0 (#6328)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.61.0 to 5.62.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.62.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 14:01:32 -08:00
dependabot[bot]
1befd37310 chore: bump word-wrap from 1.2.3 to 1.2.4 (#6342)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 14:01:14 -08:00
dependabot[bot]
2b3b5e7051 chore: bump word-wrap from 1.2.3 to 1.2.4 in /test (#6343)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 14:00:56 -08:00
Olivier Benz
788da5d495 Update Code to 1.80.1 (#6340)
* Update Code to 1.80.1 (#6340)
* Give build workflow more time to run
2023-07-19 14:00:31 -08:00
dependabot[bot]
070a5a3179 chore: bump semver from 6.3.0 to 6.3.1 in /test (#6320)
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-14 13:52:42 -08:00
Michael Thomas
7cbb6a8b09 Add Svelte to guide (#6269)
Added a guide on proxying to a Svelte app since there wasn't one already. Used the vue and angular guides as a template and included a link to an issue post on sveltekits website which adds some context.
2023-07-13 16:17:25 -05:00
dependabot[bot]
7fb54073db chore: bump @typescript-eslint/parser from 5.59.11 to 5.61.0 (#6312)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.59.11 to 5.61.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.61.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 16:04:32 -05:00
dependabot[bot]
e72e8bb6b5 chore: bump aquasecurity/trivy-action from 0.10.0 to 0.11.2 (#6303)
Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.10.0 to 0.11.2.
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](e5f43133f6...41f05d9ecf)

---
updated-dependencies:
- dependency-name: aquasecurity/trivy-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 16:04:18 -05:00
dependabot[bot]
411846528b chore: bump eslint from 8.39.0 to 8.44.0 (#6299)
Bumps [eslint](https://github.com/eslint/eslint) from 8.39.0 to 8.44.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.39.0...v8.44.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 16:03:55 -05:00
dependabot[bot]
7f024eda6e chore: bump typescript from 5.0.4 to 5.1.6 (#6297)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.0.4 to 5.1.6.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 13:59:24 -05:00
dependabot[bot]
2617623ed6 chore: bump tj-actions/changed-files from 36 to 37 (#6302)
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 36 to 37.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v36...v37)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 13:59:01 -05:00
Asher
70aa1b7722 Release v4.14.1 2023-06-26 10:21:35 -08:00
dependabot[bot]
90a4147381 chore: bump semver from 7.3.8 to 7.5.2 (#6287)
Bumps [semver](https://github.com/npm/node-semver) from 7.3.8 to 7.5.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.8...v7.5.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-26 12:12:45 -05:00
Asher
5c19962930 Set session socket into environment variable (#6282)
* Avoid spawning code-server with --reuse-window and --new-window

These flags mean the user explicitly wants to open in an existing
instance so if the socket is down it should error rather than try to
spawn code-server normally.

* Set session socket into environment variable

While I was at it I added a CLI flag to override the default.  I also
swapped the default to --user-data-dir.

The value is set on an environment variable so it can be used by the
extension host similar to VSCODE_IPC_HOOK_CLI.

* Add e2e test for opening files externally
2023-06-21 22:47:01 -08:00
Asher
56d10d82bf Enforce Node binary permissions
In some cases it may have extra write permissions.
2023-06-21 11:23:40 -08:00
Asher
cef2aa22dc Move session server log after HTTP server logs
The indented logs technically apply to the HTTP server so move the
session server log afterward to avoid making them look like they apply
to the session server.
2023-06-21 10:59:02 -08:00
Sean Lee
b5a9ef80e7 Use unique socket per user for managing editor sessions (#6278)
Also warn if editor session manager socket cannot be created rather than failing.
2023-06-21 13:39:25 -05:00
Asher
5d3c9edce4 Release v4.14.0 2023-06-16 13:38:21 -08:00
Asher
9955cd91a4 Update Code to 1.79.1 (#6264)
Mostly just the usual shifting or changing of the surrounding context
but I did refactor the getting started block we insert because it keeps
getting mangled on each update.  Instead of shifting things around the
columns I just prepend it to the right column.

Getting 404s on some vsda module but everything seems to work 
without it and I do not see it referenced in the package.json nor a
nywhere on npmjs.com so it seems to be optional.
2023-06-15 08:00:03 -08:00
dependabot[bot]
fdeaba9581 chore: bump proxy-agent from 5.0.0 to 6.2.1 (#6247)
* chore: bump proxy-agent from 5.0.0 to 6.2.1

Bumps [proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/proxy-agent) from 5.0.0 to 6.2.1.
- [Release notes](https://github.com/TooTallNate/proxy-agents/releases)
- [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/proxy-agent/CHANGELOG.md)
- [Commits](https://github.com/TooTallNate/proxy-agents/commits/proxy-agent@6.2.1/packages/proxy-agent)

---
updated-dependencies:
- dependency-name: proxy-agent
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update proxy-agent usage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Asher <ash@coder.com>
2023-06-14 14:27:15 -08:00
dependabot[bot]
40ff2e6049 chore: bump @typescript-eslint/parser from 5.57.1 to 5.59.11 (#6262)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.57.1 to 5.59.11.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.59.11/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 14:08:39 -08:00
dependabot[bot]
73d42f7ea0 chore: bump i18next from 22.4.6 to 22.5.1 (#6261)
Bumps [i18next](https://github.com/i18next/i18next) from 22.4.6 to 22.5.1.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v22.4.6...v22.5.1)

---
updated-dependencies:
- dependency-name: i18next
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 13:33:51 -08:00
Sean Lee
fb73742b2b Prefer matching editor sessions when opening files. (#6191)
Signed-off-by: Sean Lee <freshdried@gmail.com>
2023-06-14 13:32:07 -08:00
Asher
ccb0d3a34f Remove unused dependency split2
This was used with now-removed `--link`.
2023-06-13 10:38:13 -08:00
dependabot[bot]
4a121edd16 chore: bump tj-actions/changed-files from 35 to 36 (#6246)
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 35 to 36.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v35...v36)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 18:28:24 +00:00
dependabot[bot]
f6db985712 chore: bump robinraju/release-downloader from 1.7 to 1.8 (#6245)
Bumps [robinraju/release-downloader](https://github.com/robinraju/release-downloader) from 1.7 to 1.8.
- [Release notes](https://github.com/robinraju/release-downloader/releases)
- [Commits](https://github.com/robinraju/release-downloader/compare/v1.7...v1.8)

---
updated-dependencies:
- dependency-name: robinraju/release-downloader
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 12:47:40 -05:00
dependabot[bot]
6d00fc7f46 chore: bump typescript from 4.6.4 to 5.0.4 (#6182)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.6.4 to 5.0.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.6.4...v5.0.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 12:45:49 -05:00
Jonas
ee024f3f2d docs: mention Termux extension workarounds (#6227) 2023-05-31 15:00:58 -08:00
Simon Merschjohann
0703ef008c Allow {{host}} and {{port}} in domain proxy (#6225) 2023-05-31 16:31:30 -05:00
Asher
2109d1cf6a Add new unreleased section to changelog 2023-05-22 12:21:30 -08:00
Asher
74af05dfbe Release v4.13.0 2023-05-19 13:14:29 -08:00
88 changed files with 3140 additions and 2089 deletions

View File

@@ -31,7 +31,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Run prettier with actionsx/prettier - name: Run prettier with actionsx/prettier
uses: actionsx/prettier@v2 uses: actionsx/prettier@v3
with: with:
args: --check --loglevel=warn . args: --check --loglevel=warn .
@@ -45,16 +45,16 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v35 uses: tj-actions/changed-files@v38
with: with:
files: | files: |
docs/** docs/**
- name: Install Node.js v16 - name: Install Node.js v18
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
cache: "yarn" cache: "yarn"
- name: Install doctoc - name: Install doctoc
@@ -76,7 +76,7 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v35 uses: tj-actions/changed-files@v38
with: with:
files: | files: |
ci/helm-chart/** ci/helm-chart/**
@@ -107,7 +107,7 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v35 uses: tj-actions/changed-files@v38
with: with:
files: | files: |
**/*.ts **/*.ts
@@ -115,11 +115,11 @@ jobs:
files_ignore: | files_ignore: |
lib/vscode/** lib/vscode/**
- name: Install Node.js v16 - name: Install Node.js v18
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Fetch dependencies from cache
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
@@ -163,18 +163,18 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v35 uses: tj-actions/changed-files@v38
with: with:
files: | files: |
**/*.ts **/*.ts
files_ignore: | files_ignore: |
lib/vscode/** lib/vscode/**
- name: Install Node.js v16 - name: Install Node.js v18
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Fetch dependencies from cache
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
@@ -203,7 +203,7 @@ jobs:
build: build:
name: Build code-server name: Build code-server
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 30 timeout-minutes: 60
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps: steps:
@@ -212,6 +212,9 @@ jobs:
with: with:
submodules: true submodules: true
- name: Install system dependencies
run: sudo apt update && sudo apt install -y libkrb5-dev
- name: Install quilt - name: Install quilt
uses: awalsh128/cache-apt-pkgs-action@latest uses: awalsh128/cache-apt-pkgs-action@latest
with: with:
@@ -221,10 +224,10 @@ jobs:
- name: Patch Code - name: Patch Code
run: quilt push -a run: quilt push -a
- name: Install Node.js v16 - name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Fetch dependencies from cache
id: cache-node-modules id: cache-node-modules
@@ -292,10 +295,13 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16 - name: Install system dependencies
run: sudo apt update && sudo apt install -y libkrb5-dev
- name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Fetch dependencies from cache
id: cache-node-modules id: cache-node-modules
@@ -348,10 +354,13 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16 - name: Install system dependencies
run: sudo apt update && sudo apt install -y libkrb5-dev
- name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Fetch dependencies from cache
id: cache-node-modules id: cache-node-modules

View File

@@ -36,7 +36,7 @@ jobs:
cache: "yarn" cache: "yarn"
- name: Download npm package from release artifacts - name: Download npm package from release artifacts
uses: robinraju/release-downloader@v1.7 uses: robinraju/release-downloader@v1.8
with: with:
repository: "coder/code-server" repository: "coder/code-server"
tag: ${{ github.event.inputs.version || github.ref_name }} tag: ${{ github.event.inputs.version || github.ref_name }}
@@ -184,7 +184,7 @@ jobs:
echo "VERSION=${TAG#v}" >> $GITHUB_ENV echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Download release artifacts - name: Download release artifacts
uses: robinraju/release-downloader@v1.7 uses: robinraju/release-downloader@v1.8
with: with:
repository: "coder/code-server" repository: "coder/code-server"
tag: v${{ env.VERSION }} tag: v${{ env.VERSION }}

View File

@@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
needs: npm-version needs: npm-version
container: "centos:7" container: "centos:8"
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -35,17 +35,17 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16 - name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Install development tools - name: Install development tools
run: | run: |
yum install -y epel-release centos-release-scl make cd /etc/yum.repos.d/
yum install -y devtoolset-9-{make,gcc,gcc-c++} jq rsync python3 sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
# for keytar sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
yum install -y libsecret-devel yum install -y gcc-c++ make jq rsync python3 libsecret-devel krb5-devel
- name: Install nfpm and envsubst - name: Install nfpm and envsubst
run: | run: |
@@ -67,10 +67,8 @@ jobs:
- name: Decompress npm package - name: Decompress npm package
run: tar -xzf package.tar.gz run: tar -xzf package.tar.gz
# NOTE: && here is deliberate - GitHub puts each line in its own `.sh`
# file when running inside a docker container.
- name: Build standalone release - name: Build standalone release
run: source scl_source enable devtoolset-9 && npm run release:standalone run: npm run release:standalone
- name: Install test dependencies - name: Install test dependencies
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
@@ -102,25 +100,7 @@ jobs:
discussion_category_name: "📣 Announcements" discussion_category_name: "📣 Announcements"
files: ./release-packages/* files: ./release-packages/*
# NOTE@oxy: # TODO: We should use the same CentOS image to cross-compile if possible?
# We use Ubuntu 16.04 here, so that our build is more compatible
# with older libc versions. We used to (Q1'20) use CentOS 7 here,
# but it has a full update EOL of Q4'20 and a 'critical security'
# update EOL of 2024. We're dropping full support a few years before
# the final EOL, but I don't believe CentOS 7 has a large arm64 userbase.
# It is not feasible to cross-compile with CentOS.
# Cross-compile notes: To compile native dependencies for arm64,
# we install the aarch64/armv7l cross toolchain and then set it as the default
# compiler/linker/etc. with the AR/CC/CXX/LINK environment variables.
# qemu-user-static on ubuntu-16.04 currently doesn't run Node correctly,
# so we just build with "native"/x86_64 node, then download arm64/armv7l node
# and then put it in our release. We can't smoke test the cross build this way,
# but this means we don't need to maintain a self-hosted runner!
# NOTE@jsjoeio:
# We used to use 18.04 until GitHub browned it out on December 15, 2022
# See here: https://github.com/actions/runner-images/issues/6002
package-linux-cross: package-linux-cross:
name: Linux cross-compile builds name: Linux cross-compile builds
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -139,17 +119,17 @@ jobs:
CC: ${{ format('{0}-gcc', matrix.prefix) }} CC: ${{ format('{0}-gcc', matrix.prefix) }}
CXX: ${{ format('{0}-g++', matrix.prefix) }} CXX: ${{ format('{0}-g++', matrix.prefix) }}
LINK: ${{ format('{0}-g++', matrix.prefix) }} LINK: ${{ format('{0}-g++', matrix.prefix) }}
NPM_CONFIG_ARCH: ${{ matrix.arch }} npm_config_arch: ${{ matrix.arch }}
NODE_VERSION: v16.13.0 NODE_VERSION: v18.15.0
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16 - name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18.15.0"
- name: Install nfpm - name: Install nfpm
run: | run: |
@@ -157,8 +137,21 @@ jobs:
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
echo "$HOME/.local/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install cross-compiler - name: Install cross-compiler and system dependencies (arm64)
run: sudo apt update && sudo apt install $PACKAGE if: ${{ matrix.arch != 'armv7l' }}
run: sudo apt update && sudo apt install -y $PACKAGE libkrb5-dev
env:
PACKAGE: ${{ format('g++-{0}', matrix.prefix) }}
- name: Install cross-compiler and system dependencies (armv7l)
if: ${{ matrix.arch == 'armv7l' }}
run: |
sudo sed -i "s/^deb/deb [arch=amd64,i386]/g" /etc/apt/sources.list
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ $(lsb_release -s -c) main universe multiverse restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ $(lsb_release -s -c)-updates main universe multiverse restricted" | sudo tee -a /etc/apt/sources.list
sudo dpkg --add-architecture armhf
sudo apt update
sudo apt install -y $PACKAGE libkrb5-dev:armhf
env: env:
PACKAGE: ${{ format('g++-{0}', matrix.prefix) }} PACKAGE: ${{ format('g++-{0}', matrix.prefix) }}
@@ -170,15 +163,13 @@ jobs:
- name: Decompress npm package - name: Decompress npm package
run: tar -xzf package.tar.gz run: tar -xzf package.tar.gz
# NOTE@jsjoeio - npm fails here
# so use yarn
- name: Build standalone release - name: Build standalone release
run: yarn release:standalone run: npm run release:standalone
- name: Replace node with cross-compile equivalent - name: Replace node with cross-compile equivalent
run: | run: |
wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-${NPM_CONFIG_ARCH}.tar.xz wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-${npm_config_arch}.tar.xz
tar -xf node-${NODE_VERSION}-linux-${NPM_CONFIG_ARCH}.tar.xz node-${NODE_VERSION}-linux-${NPM_CONFIG_ARCH}/bin/node --strip-components=2 tar -xf node-${NODE_VERSION}-linux-${npm_config_arch}.tar.xz node-${NODE_VERSION}-linux-${npm_config_arch}/bin/node --strip-components=2
mv ./node ./release-standalone/lib/node mv ./node ./release-standalone/lib/node
# NOTE@jsjoeio - we do this so we can strip out the v # NOTE@jsjoeio - we do this so we can strip out the v
@@ -191,7 +182,7 @@ jobs:
- name: Build packages with nfpm - name: Build packages with nfpm
env: env:
VERSION: ${{ env.VERSION }} VERSION: ${{ env.VERSION }}
run: yarn package ${NPM_CONFIG_ARCH} run: yarn package ${npm_config_arch}
- uses: softprops/action-gh-release@v1 - uses: softprops/action-gh-release@v1
with: with:
@@ -208,10 +199,10 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16 - name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Install nfpm - name: Install nfpm
run: | run: |
@@ -305,7 +296,7 @@ jobs:
npm version --prefix release "$VERSION" npm version --prefix release "$VERSION"
echo "Updating version in lib/vscode/product.json" echo "Updating version in lib/vscode/product.json"
tmp=$(mktemp) tmp=$(mktemp)
jq ".codeServerVersion = \"$VERSION\"" release/lib/vscode/product.json > "$tmp" && mv "$tmp" release/lib/vscode/product.json jq ".codeServerVersion = \"$VERSION\"" release/lib/vscode/product.json > "$tmp" && mv "$tmp" release/lib/vscode/product.json
# Ensure it has the same permissions as before # Ensure it has the same permissions as before
chmod 644 release/lib/vscode/product.json chmod 644 release/lib/vscode/product.json

View File

@@ -29,26 +29,17 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Node.js v16 - name: Install Node.js v18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "18"
- name: Fetch dependencies from cache - name: Audit yarn for vulnerabilities
id: cache-yarn run: yarn audit
uses: actions/cache@v3 if: success()
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Install dependencies - name: Audit npm for vulnerabilities
if: steps.cache-yarn.outputs.cache-hit != 'true' run: npm shrinkwrap && npm audit
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Audit for vulnerabilities
run: yarn _audit
if: success() if: success()
trivy-scan-repo: trivy-scan-repo:
@@ -64,7 +55,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Run Trivy vulnerability scanner in repo mode - name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54
with: with:
scan-type: "fs" scan-type: "fs"
scan-ref: "." scan-ref: "."

View File

@@ -51,7 +51,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Run Trivy vulnerability scanner in image mode - name: Run Trivy vulnerability scanner in image mode
uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54
with: with:
image-ref: "docker.io/codercom/code-server:latest" image-ref: "docker.io/codercom/code-server:latest"
ignore-unfixed: true ignore-unfixed: true

View File

@@ -1 +1 @@
16 18

View File

@@ -20,6 +20,87 @@ Code v99.99.999
--> -->
## Unreleased
## [4.16.1](https://github.com/coder/code-server/releases/tag/v4.16.1) - 2023-07-31
Code v1.80.2
### Changed
- Updated to Code 1.80.2.
## [4.16.0](https://github.com/coder/code-server/releases/tag/v4.16.0) - 2023-07-28
Code v1.80.1
### Added
- `--disable-proxy` flag. This disables the domain and path proxies but it does
not disable the ports panel in Code. That can be disabled by using
`remote.autoForwardPorts=false` in your settings.
## [4.15.0](https://github.com/coder/code-server/releases/tag/v4.15.0) - 2023-07-21
Code v1.80.1
### Changed
- Updated to Code 1.80.1.
### Added
- `--trusted-origin` flag for specifying origins that you trust but do not
control (for example a reverse proxy).
Code v1.79.2
## [4.14.1](https://github.com/coder/code-server/releases/tag/v4.14.1) - 2023-06-26
Code v1.79.2
### Security
- Remove extra write permissions on the Node binary bundled with the linux-amd64
tarball. If you extract the tar without a umask this could mean the Node
binary would be unexpectedly writable.
### Fixed
- Inability to launch multiple instances of code-server for different users.
### Added
- `--session-socket` CLI flag to configure the location of the session socket.
By default it will be placed in `<user data dir>/code-server-ipc.sock`.
## [4.14.0](https://github.com/coder/code-server/releases/tag/v4.14.0) - 2023-06-16
Code v1.79.2
### Added
- `--domain-proxy` now supports `{{port}}` and `{{host}}` template variables.
### Changed
- Updated to Code 1.79.2
- Files opened from an external terminal will now open in the most closely
related window rather than in the last opened window.
## [4.13.0](https://github.com/coder/code-server/releases/tag/v4.13.0) - 2023-05-19
Code v1.78.2
### Changed
- Updated to Code 1.78.2.
### Fixed
- Proxying files that contain non-ASCII characters.
- Origin check when X-Forwarded-Host contains comma-separated hosts.
## [4.12.0](https://github.com/coder/code-server/releases/tag/v4.12.0) - 2023-04-21 ## [4.12.0](https://github.com/coder/code-server/releases/tag/v4.12.0) - 2023-04-21
Code v1.77.3 Code v1.77.3

View File

@@ -56,7 +56,6 @@ bundle_code_server() {
} }
EOF EOF
) > "$RELEASE_PATH/package.json" ) > "$RELEASE_PATH/package.json"
rsync yarn.lock "$RELEASE_PATH"
mv npm-shrinkwrap.json "$RELEASE_PATH" mv npm-shrinkwrap.json "$RELEASE_PATH"
rsync ci/build/npm-postinstall.sh "$RELEASE_PATH/postinstall.sh" rsync ci/build/npm-postinstall.sh "$RELEASE_PATH/postinstall.sh"
@@ -95,12 +94,10 @@ bundle_vscode() {
"$VSCODE_SRC_PATH/remote/package.json" \ "$VSCODE_SRC_PATH/remote/package.json" \
"$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json" "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json"
rsync "$VSCODE_SRC_PATH/remote/yarn.lock" "$VSCODE_OUT_PATH/yarn.lock"
mv "$VSCODE_SRC_PATH/remote/npm-shrinkwrap.json" "$VSCODE_OUT_PATH/npm-shrinkwrap.json" mv "$VSCODE_SRC_PATH/remote/npm-shrinkwrap.json" "$VSCODE_OUT_PATH/npm-shrinkwrap.json"
# Include global extension dependencies as well. # Include global extension dependencies as well.
rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions/package.json" rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions/package.json"
rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions/yarn.lock"
mv "$VSCODE_SRC_PATH/extensions/npm-shrinkwrap.json" "$VSCODE_OUT_PATH/extensions/npm-shrinkwrap.json" mv "$VSCODE_SRC_PATH/extensions/npm-shrinkwrap.json" "$VSCODE_OUT_PATH/extensions/npm-shrinkwrap.json"
rsync "$VSCODE_SRC_PATH/extensions/postinstall.mjs" "$VSCODE_OUT_PATH/extensions/postinstall.mjs" rsync "$VSCODE_SRC_PATH/extensions/postinstall.mjs" "$VSCODE_OUT_PATH/extensions/postinstall.mjs"
} }

View File

@@ -1,10 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2
# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057
export npm_config_build_from_source=true
main() { main() {
cd "$(dirname "${0}")/../.." cd "$(dirname "${0}")/../.."
@@ -24,6 +20,8 @@ main() {
rsync ./ci/build/code-server.sh "$RELEASE_PATH/bin/code-server" rsync ./ci/build/code-server.sh "$RELEASE_PATH/bin/code-server"
rsync "$node_path" "$RELEASE_PATH/lib/node" rsync "$node_path" "$RELEASE_PATH/lib/node"
chmod 755 "$RELEASE_PATH/lib/node"
pushd "$RELEASE_PATH" pushd "$RELEASE_PATH"
npm install --unsafe-perm --omit=dev npm install --unsafe-perm --omit=dev
popd popd

View File

@@ -112,7 +112,7 @@ EOF
pushd lib/vscode-reh-web-linux-x64 pushd lib/vscode-reh-web-linux-x64
# Make sure Code took the version we set in the environment variable. Not # Make sure Code took the version we set in the environment variable. Not
# having a version will break display languages. # having a version will break display languages.
if ! jq -e .commit product.json ; then if ! jq -e .commit product.json; then
echo "'commit' is missing from product.json" echo "'commit' is missing from product.json"
exit 1 exit 1
fi fi

View File

@@ -53,10 +53,6 @@ symlink_bin_script() {
OS="$(os)" OS="$(os)"
# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2
# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057
export npm_config_build_from_source=true
main() { main() {
# Grabs the major version of node from $npm_config_user_agent which looks like # Grabs the major version of node from $npm_config_user_agent which looks like
# yarn/1.21.1 npm/? node/v14.2.0 darwin x64 # yarn/1.21.1 npm/? node/v14.2.0 darwin x64
@@ -68,8 +64,8 @@ main() {
echo "USE AT YOUR OWN RISK!" echo "USE AT YOUR OWN RISK!"
fi fi
if [ "$major_node_version" -ne "${FORCE_NODE_VERSION:-16}" ]; then if [ "$major_node_version" -ne "${FORCE_NODE_VERSION:-18}" ]; then
echo "ERROR: code-server currently requires node v16." echo "ERROR: code-server currently requires node v18."
if [ -n "$FORCE_NODE_VERSION" ]; then if [ -n "$FORCE_NODE_VERSION" ]; then
echo "However, you have overrided the version check to use v$FORCE_NODE_VERSION." echo "However, you have overrided the version check to use v$FORCE_NODE_VERSION."
fi fi
@@ -113,29 +109,40 @@ install_with_yarn_or_npm() {
# HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases. # HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases.
# The legacy behavior doesn't complain about pre-releases being used, falling back to that for now. # The legacy behavior doesn't complain about pre-releases being used, falling back to that for now.
# See https://github.com//pull/5071 # See https://github.com//pull/5071
npm install --unsafe-perm --legacy-peer-deps --omit=dev if ! npm install --unsafe-perm --legacy-peer-deps --omit=dev; then
return 1
fi
;; ;;
yarn*) yarn*)
yarn --production --frozen-lockfile --no-default-rc if ! yarn --production --frozen-lockfile --no-default-rc; then
return 1
fi
;; ;;
*) *)
echo "Could not determine which package manager is being used to install code-server" echo "Could not determine which package manager is being used to install code-server"
exit 1 exit 1
;; ;;
esac esac
return 0
} }
vscode_install() { vscode_install() {
echo 'Installing Code dependencies...' echo 'Installing Code dependencies...'
cd lib/vscode cd lib/vscode
install_with_yarn_or_npm if ! install_with_yarn_or_npm; then
return 1
fi
symlink_asar symlink_asar
symlink_bin_script remote-cli code code-server symlink_bin_script remote-cli code code-server
symlink_bin_script helpers browser browser .sh symlink_bin_script helpers browser browser .sh
cd extensions cd extensions
install_with_yarn_or_npm if ! install_with_yarn_or_npm; then
return 1
fi
return 0
} }
main "$@" main "$@"

View File

@@ -15,9 +15,9 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 3.8.0 version: 3.12.1
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 4.12.0 appVersion: 4.16.1

View File

@@ -20,6 +20,9 @@ spec:
labels: labels:
app.kubernetes.io/name: {{ include "code-server.name" . }} app.kubernetes.io/name: {{ include "code-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Values.podAnnotations }}
annotations: {{- toYaml .Values.podAnnotations | nindent 8 }}
{{- end }}
spec: spec:
imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }} imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }}
{{- if .Values.hostnameOverride }} {{- if .Values.hostnameOverride }}

View File

@@ -1,3 +1,4 @@
{{- if not .Values.existingSecret }}
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
@@ -11,8 +12,9 @@ metadata:
app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/managed-by: {{ .Release.Service }}
type: Opaque type: Opaque
data: data:
{{ if .Values.password }} {{- if .Values.password }}
password: "{{ .Values.password | b64enc }}" password: "{{ .Values.password | b64enc }}"
{{ else }} {{- else }}
password: "{{ randAlphaNum 24 | b64enc }}" password: "{{ randAlphaNum 24 | b64enc }}"
{{ end }} {{- end }}
{{- end }}

View File

@@ -6,7 +6,7 @@ replicaCount: 1
image: image:
repository: codercom/code-server repository: codercom/code-server
tag: '4.12.0' tag: '4.16.1'
pullPolicy: Always pullPolicy: Always
# Specifies one or more secrets to be used when pulling images from a # Specifies one or more secrets to be used when pulling images from a

View File

@@ -37,7 +37,7 @@ for [VS
Code](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites). Code](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites).
Here is what is needed: Here is what is needed:
- `node` v16.x - `node` v18.x
- `git` v2.x or greater - `git` v2.x or greater
- [`git-lfs`](https://git-lfs.github.com) - [`git-lfs`](https://git-lfs.github.com)
- [`yarn`](https://classic.yarnpkg.com/en/) - [`yarn`](https://classic.yarnpkg.com/en/)
@@ -63,7 +63,7 @@ Here is what is needed:
If you're developing code-server on Linux, make sure you have installed or install the following dependencies: If you're developing code-server on Linux, make sure you have installed or install the following dependencies:
```shell ```shell
sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev python-is-python3 sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev libkrb5-dev python-is-python3
``` ```
These are required by 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.

View File

@@ -14,6 +14,7 @@
- [How do I install an extension manually?](#how-do-i-install-an-extension-manually) - [How do I install an extension manually?](#how-do-i-install-an-extension-manually)
- [How do I use my own extensions marketplace?](#how-do-i-use-my-own-extensions-marketplace) - [How do I use my own extensions marketplace?](#how-do-i-use-my-own-extensions-marketplace)
- [Where are extensions stored?](#where-are-extensions-stored) - [Where are extensions stored?](#where-are-extensions-stored)
- [Where is VS Code configuration stored?](#where-is-vs-code-configuration-stored)
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) - [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open) - [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open)
- [How do I access my Documents/Downloads/Desktop folders in code-server on macOS?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-macos) - [How do I access my Documents/Downloads/Desktop folders in code-server on macOS?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-macos)
@@ -34,6 +35,7 @@
- [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server) - [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server)
- [How do I change the port?](#how-do-i-change-the-port) - [How do I change the port?](#how-do-i-change-the-port)
- [How do I hide the coder/coder promotion in Help: Getting Started?](#how-do-i-hide-the-codercoder-promotion-in-help-getting-started) - [How do I hide the coder/coder promotion in Help: Getting Started?](#how-do-i-hide-the-codercoder-promotion-in-help-getting-started)
- [How do I disable the proxy?](#how-do-i-disable-the-proxy)
- [How do I disable file download?](#how-do-i-disable-file-download) - [How do I disable file download?](#how-do-i-disable-file-download)
<!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -191,10 +193,20 @@ docs](https://github.com/VSCodium/vscodium/blob/master/DOCS.md#extensions--marke
## Where are extensions stored? ## Where are extensions stored?
Extensions are store, by default, to `~/.local/share/code-server/extensions`. Extensions are stored in `~/.local/share/code-server/extensions` by default.
If you set the `XDG_DATA_HOME` environment variable, the data directory will be On Linux and macOS if you set the `XDG_DATA_HOME` environment variable, the
`$XDG_DATA_HOME/code-server/extensions`. In general, we try to follow the XDG directory spec. extensions directory will be `$XDG_DATA_HOME/code-server/extensions`. In
general, we try to follow the XDG directory spec.
## Where is VS Code configuration stored?
VS Code configuration such as settings and keybindings are stored in
`~/.local/share/code-server` by default.
On Linux and macOS if you set the `XDG_DATA_HOME` environment variable, the data
directory will be `$XDG_DATA_HOME/code-server`. In general, we try to follow the
XDG directory spec.
## How can I reuse my VS Code configuration? ## How can I reuse my VS Code configuration?
@@ -453,6 +465,19 @@ You can pass the flag `--disable-getting-started-override` to `code-server` or
you can set the environment variable `CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or you can set the environment variable `CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or
`CS_DISABLE_GETTING_STARTED_OVERRIDE=true`. `CS_DISABLE_GETTING_STARTED_OVERRIDE=true`.
## How do I disable the proxy?
You can pass the flag `--disable-proxy` to `code-server` or
you can set the environment variable `CS_DISABLE_PROXY=1` or
`CS_DISABLE_PROXY=true`.
Note, this option currently only disables the proxy routes to forwarded ports, including
the domain and path proxy routes over HTTP and WebSocket; however, it does not
disable the automatic port forwarding in the VS Code workbench itself. In other words,
user will still see the Ports tab and notifications, but will not be able to actually
use access the ports. It is recommended to set `remote.autoForwardPorts` to `false`
when using the option.
## How do I disable file download? ## How do I disable file download?
You can pass the flag `--disable-file-downloads` to `code-server` You can pass the flag `--disable-file-downloads` to `code-server`

View File

@@ -5,7 +5,8 @@
Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and
access it in the browser. access it in the browser.
![Screenshot](./assets/screenshot.png) ![Screenshot](./assets/screenshot-1.png)
![Screenshot](./assets/screenshot-2.png)
## Highlights ## Highlights

View File

@@ -11,11 +11,11 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
``` ```
6. Exit the terminal using `exit` and then reopen the terminal 6. Exit the terminal using `exit` and then reopen the terminal
7. Install and use Node.js 16: 7. Install and use Node.js 18:
```shell ```shell
nvm install 16 nvm install 18
nvm use 16 nvm use 18
``` ```
8. Install code-server globally on device with: `npm install --global code-server --unsafe-perm` 8. Install code-server globally on device with: `npm install --global code-server --unsafe-perm`

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -19,6 +19,7 @@
- [Proxying to create a React app](#proxying-to-create-a-react-app) - [Proxying to create a React app](#proxying-to-create-a-react-app)
- [Proxying to a Vue app](#proxying-to-a-vue-app) - [Proxying to a Vue app](#proxying-to-a-vue-app)
- [Proxying to an Angular app](#proxying-to-an-angular-app) - [Proxying to an Angular app](#proxying-to-an-angular-app)
- [Proxying to a Svelte app](#proxying-to-a-svelte-app)
- [SSH into code-server on VS Code](#ssh-into-code-server-on-vs-code) - [SSH into code-server on VS Code](#ssh-into-code-server-on-vs-code)
- [Option 1: cloudflared tunnel](#option-1-cloudflared-tunnel) - [Option 1: cloudflared tunnel](#option-1-cloudflared-tunnel)
- [Option 2: ngrok tunnel](#option-2-ngrok-tunnel) - [Option 2: ngrok tunnel](#option-2-ngrok-tunnel)
@@ -138,9 +139,9 @@ sudo apt install caddy
1. Replace `/etc/caddy/Caddyfile` using `sudo` so that the file looks like this: 1. Replace `/etc/caddy/Caddyfile` using `sudo` so that the file looks like this:
```text ```text
mydomain.com mydomain.com {
reverse_proxy 127.0.0.1:8080
reverse_proxy 127.0.0.1:8080 }
``` ```
If you want to serve code-server from a sub-path, you can do so as follows: If you want to serve code-server from a sub-path, you can do so as follows:
@@ -414,6 +415,27 @@ In order to use code-server's built-in proxy with Angular, you need to make the
For additional context, see [this GitHub Discussion](https://github.com/coder/code-server/discussions/5439#discussioncomment-3371983). For additional context, see [this GitHub Discussion](https://github.com/coder/code-server/discussions/5439#discussioncomment-3371983).
### Proxying to a Svelte app
In order to use code-server's built-in proxy with Svelte, you need to make the following changes in your app:
1. Add `svelte.config.js` if you don't already have one
2. Update the values to match this (you can use any free port):
```js
const config = {
kit: {
paths: {
base: "/absproxy/5173",
},
},
}
```
3. Access app at `<code-server-root>/absproxy/5173/` e.g. `http://localhost:8080/absproxy/5173/
For additional context, see [this Github Issue](https://github.com/sveltejs/kit/issues/2958)
## SSH into code-server on VS Code ## SSH into code-server on VS Code
[![SSH](https://img.shields.io/badge/SSH-363636?style=for-the-badge&logo=GNU+Bash&logoColor=ffffff)](https://ohmyz.sh/) [![Terminal](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff)](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff) [![Visual Studio Code](https://img.shields.io/badge/Visual_Studio_Code-007ACC?style=for-the-badge&logo=Visual+Studio+Code&logoColor=ffffff)](vscode:extension/ms-vscode-remote.remote-ssh) [![SSH](https://img.shields.io/badge/SSH-363636?style=for-the-badge&logo=GNU+Bash&logoColor=ffffff)](https://ohmyz.sh/) [![Terminal](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff)](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff) [![Visual Studio Code](https://img.shields.io/badge/Visual_Studio_Code-007ACC?style=for-the-badge&logo=Visual+Studio+Code&logoColor=ffffff)](vscode:extension/ms-vscode-remote.remote-ssh)

View File

@@ -103,10 +103,9 @@ _exact_ same commands presented in the rest of this document.
We recommend installing with `npm` when: We recommend installing with `npm` when:
1. You aren't using a machine with `amd64` or `arm64`. 1. You aren't using a machine with `amd64` or `arm64`.
1. You are installing code-server on Windows 2. You are installing code-server on Windows.
1. You're on Linux with `glibc` < v2.17, `glibcxx` < v3.4.18 on `amd64`, `glibc` 3. You're on Linux with `glibc` < v2.28 or `glibcxx` < v3.4.21.
< v2.23, or `glibcxx` < v3.4.21 on `arm64`. 4. You're running Alpine Linux or are using a non-glibc libc. See
1. You're running Alpine Linux or are using a non-glibc libc. See
[#1430](https://github.com/coder/code-server/issues/1430#issuecomment-629883198) [#1430](https://github.com/coder/code-server/issues/1430#issuecomment-629883198)
for more information. for more information.
@@ -123,8 +122,8 @@ node binary and node modules.
We create the standalone releases using the [npm package](#npm), and we We create the standalone releases using the [npm package](#npm), and we
then create the remaining releases using the standalone version. then create the remaining releases using the standalone version.
The only requirement to use the standalone release is `glibc` >= 2.17 and The only requirement to use the standalone release is `glibc` >= 2.28 and
`glibcxx` >= v3.4.18 on Linux (for macOS, there is no minimum system `glibcxx` >= v3.4.21 on Linux (for macOS, there is no minimum system
requirement). requirement).
To use a standalone release: To use a standalone release:
@@ -280,6 +279,7 @@ brew services start code-server
# outside the container. # outside the container.
mkdir -p ~/.config mkdir -p ~/.config
docker run -it --name code-server -p 127.0.0.1:8080:8080 \ docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$HOME/.local:/home/coder/.local" \
-v "$HOME/.config:/home/coder/.config" \ -v "$HOME/.config:/home/coder/.config" \
-v "$PWD:/home/coder/project" \ -v "$PWD:/home/coder/project" \
-u "$(id -u):$(id -g)" \ -u "$(id -u):$(id -g)" \

View File

@@ -30,7 +30,7 @@ includes installing instructions based on your operating system.
## Node.js version ## Node.js version
We use the same major version of Node.js shipped with Code's remote, which is We use the same major version of Node.js shipped with Code's remote, which is
currently `16.x`. VS Code also [lists Node.js currently `18.x`. VS Code also [lists Node.js
requirements](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites). requirements](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites).
Using other versions of Node.js [may lead to unexpected Using other versions of Node.js [may lead to unexpected
@@ -79,7 +79,7 @@ Proceed to [installing](#installing)
## FreeBSD ## FreeBSD
```sh ```sh
pkg install -y git python npm-node16 pkgconf pkg install -y git python npm-node18 pkgconf
pkg install -y libinotify pkg install -y libinotify
``` ```

View File

@@ -8,6 +8,7 @@
- [Upgrade](#upgrade) - [Upgrade](#upgrade)
- [Known Issues](#known-issues) - [Known Issues](#known-issues)
- [Git won't work in `/sdcard`](#git-wont-work-in-sdcard) - [Git won't work in `/sdcard`](#git-wont-work-in-sdcard)
- [Many extensions including language packs fail to install](#many-extensions-including-language-packs-fail-to-install)
- [Extra](#extra) - [Extra](#extra)
- [Create a new user](#create-a-new-user) - [Create a new user](#create-a-new-user)
- [Install Go](#install-go) - [Install Go](#install-go)
@@ -55,7 +56,7 @@ npm config set python python3
node -v node -v
``` ```
you will get node version `v16.15.0` you will get Node version `v18`
5. Now install code-server following our guide on [installing with npm](./npm.md) 5. Now install code-server following our guide on [installing with npm](./npm.md)
@@ -87,6 +88,37 @@ Potential Workaround :
1. Create a soft-link from the debian-fs to your folder in `/sdcard` 1. Create a soft-link from the debian-fs to your folder in `/sdcard`
2. Use git from termux (preferred) 2. Use git from termux (preferred)
### Many extensions including language packs fail to install
Issue: Android is not seen as a Linux environment but as a separate, unsupported platform, so code-server only allows [Web Extensions](https://code.visualstudio.com/api/extension-guides/web-extensions), refusing to download extensions that run on the server.\
Fix: None\
Potential workarounds :
Either
- Manually download extensions as `.vsix` file and install them via `Extensions: Install from VSIX...` in the Command Palette.
- Use an override to pretend the platform is Linux:
Create a JS script that patches `process.platform`:
```js
// android-as-linux.js
Object.defineProperty(process, "platform", {
get() {
return "linux"
},
})
```
Then use Node's `--require` option to make sure it is loaded before `code-server` starts:
```sh
NODE_OPTIONS="--require /path/to/android-as-linux.js" code-server
```
⚠️ Note that Android and Linux are not 100% compatible, so use these workarounds at your own risk. Extensions that have native dependencies other than Node or that directly interact with the OS might cause issues.
## Extra ## Extra
### Create a new user ### Create a new user

View File

@@ -7,14 +7,14 @@
flake-utils.lib.eachDefaultSystem flake-utils.lib.eachDefaultSystem
(system: (system:
let pkgs = nixpkgs.legacyPackages.${system}; let pkgs = nixpkgs.legacyPackages.${system};
nodejs = pkgs.nodejs-16_x; nodejs = pkgs.nodejs-18_x;
yarn' = pkgs.yarn.override { inherit nodejs; }; yarn' = pkgs.yarn.override { inherit nodejs; };
in { in {
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
nodejs yarn' python3 pkg-config git rsync jq moreutils quilt bats nodejs yarn' python3 pkg-config git rsync jq moreutils quilt bats openssl
]; ];
buildInputs = with pkgs; (lib.optionals (!stdenv.isDarwin) [ libsecret ] buildInputs = with pkgs; (lib.optionals (!stdenv.isDarwin) [ libsecret libkrb5 ]
++ (with xorg; [ libX11 libxkbfile ]) ++ (with xorg; [ libX11 libxkbfile ])
++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ ++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
AppKit Cocoa CoreServices Security xcbuild AppKit Cocoa CoreServices Security xcbuild

View File

@@ -441,7 +441,7 @@ install_npm() {
return return
fi fi
echoerr "Please install npm to install code-server!" echoerr "Please install npm to install code-server!"
echoerr "You will need at least node v12 and a few C dependencies." echoerr "You will need at least node v18 and a few C dependencies."
echoerr "See the docs https://coder.com/docs/code-server/latest/install#npm" echoerr "See the docs https://coder.com/docs/code-server/latest/install#npm"
exit 1 exit 1

View File

@@ -38,77 +38,62 @@
}, },
"main": "out/node/entry.js", "main": "out/node/entry.js",
"devDependencies": { "devDependencies": {
"@schemastore/package": "^0.0.6", "@schemastore/package": "^0.0.10",
"@types/compression": "^1.7.0", "@types/compression": "^1.7.3",
"@types/cookie-parser": "^1.4.2", "@types/cookie-parser": "^1.4.4",
"@types/express": "^4.17.8", "@types/express": "^4.17.17",
"@types/http-proxy": "^1.17.4", "@types/http-proxy": "1.17.7",
"@types/js-yaml": "^4.0.0", "@types/js-yaml": "^4.0.6",
"@types/node": "^16.0.0", "@types/node": "^18.0.0",
"@types/pem": "^1.9.5", "@types/pem": "^1.14.1",
"@types/proxy-from-env": "^1.0.1", "@types/proxy-from-env": "^1.0.1",
"@types/safe-compare": "^1.1.0", "@types/safe-compare": "^1.1.0",
"@types/semver": "^7.1.0", "@types/semver": "^7.5.2",
"@types/split2": "^3.2.0", "@types/trusted-types": "^2.0.4",
"@types/trusted-types": "^2.0.2", "@types/ws": "^8.5.5",
"@types/ws": "^8.5.3", "@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/parser": "^6.7.2",
"@typescript-eslint/parser": "^5.41.0", "audit-ci": "^6.6.1",
"audit-ci": "^6.0.0", "doctoc": "^2.2.1",
"doctoc": "2.2.1", "eslint": "^8.49.0",
"eslint": "^8.26.0", "eslint-config-prettier": "^9.0.0",
"eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.6.0",
"eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.28.1",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-prettier": "^4.2.1", "prettier": "^3.0.3",
"prettier": "2.8.0", "prettier-plugin-sh": "^0.13.1",
"prettier-plugin-sh": "^0.12.8", "ts-node": "^10.9.1",
"ts-node": "^10.0.0", "typescript": "^5.2.2"
"typescript": "^4.6.2"
},
"resolutions": {
"ansi-regex": "^5.0.1",
"normalize-package-data": "^5.0.0",
"doctoc/underscore": "^1.13.1",
"doctoc/**/trim": "^1.0.0",
"postcss": "^8.2.1",
"browserslist": "^4.16.5",
"safe-buffer": "^5.1.1",
"vfile-message": "^2.0.2",
"tar": "^6.1.9",
"path-parse": "^1.0.7",
"vm2": "^3.9.11",
"follow-redirects": "^1.14.8",
"node-fetch": "^2.6.7",
"nanoid": "^3.1.31",
"minimist": "npm:minimist-lite@2.2.1",
"glob-parent": "^6.0.1",
"@types/node": "^16.0.0",
"qs": "^6.7.3"
}, },
"dependencies": { "dependencies": {
"@coder/logger": "^3.0.0", "@coder/logger": "^3.0.1",
"argon2": "0.30.3", "argon2": "^0.31.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.6",
"env-paths": "^2.2.0", "env-paths": "^2.2.1",
"express": "5.0.0-alpha.8", "express": "5.0.0-alpha.8",
"http-proxy": "^1.18.0", "http-proxy": "^1.18.1",
"httpolyglot": "^0.1.2", "httpolyglot": "^0.1.2",
"i18next": "^22.4.6", "i18next": "^23.5.1",
"js-yaml": "^4.0.0", "js-yaml": "^4.1.0",
"limiter": "^2.1.0", "limiter": "^2.1.0",
"pem": "^1.14.2", "pem": "^1.14.8",
"proxy-agent": "^5.0.0", "proxy-agent": "^6.3.1",
"qs": "6.11.0", "qs": "6.9.7",
"rotating-file-stream": "^3.0.0", "rotating-file-stream": "^3.1.1",
"safe-buffer": "^5.1.1", "safe-buffer": "^5.2.1",
"safe-compare": "^1.1.4", "safe-compare": "^1.1.4",
"semver": "^7.1.3", "semver": "^7.5.4",
"split2": "^4.0.0", "ws": "^8.14.2",
"ws": "^8.0.0",
"xdg-basedir": "^4.0.0" "xdg-basedir": "^4.0.0"
}, },
"resolutions": {
"@types/node": "^18.0.0",
"qs": "6.9.7"
},
"overrides": {
"qs": "6.9.7"
},
"bin": { "bin": {
"code-server": "out/node/entry.js" "code-server": "out/node/entry.js"
}, },
@@ -122,7 +107,7 @@
"remote-development" "remote-development"
], ],
"engines": { "engines": {
"node": "16" "node": "18"
}, },
"jest": { "jest": {
"transform": { "transform": {

View File

@@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/base/common/network.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/base/common/network.ts
@@ -167,7 +167,9 @@ class RemoteAuthoritiesImpl { @@ -168,7 +168,9 @@ class RemoteAuthoritiesImpl {
return URI.from({ return URI.from({
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
authority: `${host}:${port}`, authority: `${host}:${port}`,
@@ -99,26 +99,19 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -274,6 +274,7 @@ export class BrowserSocketFactory implem @@ -280,6 +280,7 @@ export class BrowserSocketFactory implem
connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> {
connect(host: string, port: number, path: string, query: string, debugLabel: string, callback: IConnectCallback): void { return new Promise<ISocket>((resolve, reject) => {
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws'); const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/") + path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
const errorListener = socket.onError((err) => callback(err, undefined)); const errorListener = socket.onError(reject);
socket.onOpen(() => { socket.onOpen(() => {
@@ -282,6 +283,3 @@ export class BrowserSocketFactory implem
});
}
}
-
-
-
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -267,12 +267,11 @@ export class WebClientServer { @@ -269,16 +269,15 @@ export class WebClientServer {
return void res.end(); return void res.end();
} }
@@ -127,16 +120,20 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
- return Array.isArray(val) ? val[0] : val; - return Array.isArray(val) ? val[0] : val;
- }; - };
- -
- const remoteAuthority = getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host; const useTestResolver = (!this._environmentService.isBuilt && this._environmentService.args['use-test-resolver']);
+ // For now we are getting the remote authority from the client to avoid + // For now we are getting the remote authority from the client to avoid
+ // needing specific configuration for reverse proxies to work. Set this to + // needing specific configuration for reverse proxies to work. Set this to
+ // something invalid to make sure we catch code that is using this value + // something invalid to make sure we catch code that is using this value
+ // from the backend when it should not. + // from the backend when it should not.
+ const remoteAuthority = 'remote'; const remoteAuthority = (
useTestResolver
? 'test+test'
- : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host)
+ : 'remote'
);
if (!remoteAuthority) { if (!remoteAuthority) {
return serveError(req, res, 400, `Bad request.`); return serveError(req, res, 400, `Bad request.`);
} @@ -305,8 +304,12 @@ export class WebClientServer {
@@ -298,8 +297,12 @@ export class WebClientServer {
scopes: [['user:email'], ['repo']] scopes: [['user:email'], ['repo']]
} : undefined; } : undefined;
@@ -149,7 +146,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
embedderIdentifier: 'server-distro', embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? { extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery, ...this._productService.extensionsGallery,
@@ -334,11 +337,12 @@ export class WebClientServer { @@ -341,8 +344,10 @@ export class WebClientServer {
const values: { [key: string]: string } = { const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration), WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '', WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
@@ -161,20 +158,17 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
+ VS_BASE: vscodeBase, + VS_BASE: vscodeBase,
}; };
- if (useTestResolver) {
let data; @@ -367,7 +372,7 @@ export class WebClientServer {
try {
const workbenchTemplate = (await fsp.readFile(filePath)).toString();
@@ -352,7 +356,7 @@ export class WebClientServer {
'default-src \'self\';', 'default-src \'self\';',
'img-src \'self\' https: data: blob:;', 'img-src \'self\' https: data: blob:;',
'media-src \'self\';', 'media-src \'self\';',
- `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' http://${remoteAuthority};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html - `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
+ `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=';`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html + `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' ${useTestResolver ? '' : ''};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
'child-src \'self\';', 'child-src \'self\';',
`frame-src 'self' https://*.vscode-cdn.net data:;`, `frame-src 'self' https://*.vscode-cdn.net data:;`,
'worker-src \'self\' data: blob:;', 'worker-src \'self\' data: blob:;',
@@ -425,3 +429,70 @@ export class WebClientServer { @@ -440,3 +445,70 @@ export class WebClientServer {
return void res.end(data); return void res.end(data);
} }
} }
@@ -261,23 +255,25 @@ 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.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
@@ -489,6 +489,7 @@ function doCreateUri(path: string, query @@ -304,7 +304,8 @@ class LocalStorageURLCallbackProvider ex
}); this.startListening();
}
- return URI.parse(window.location.href).with({ path: this._callbackRoute, query: queryParams.join('&') });
+ const path = (window.location.pathname + "/" + this._callbackRoute).replace(/\/\/+/g, "/");
+ return URI.parse(window.location.href).with({ path: path, query: queryParams.join('&') });
} }
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/") private startListening(): void {
return URI.parse(window.location.href).with({ path, query }); @@ -569,7 +570,7 @@ function readCookie(name: string): strin
}
@@ -500,7 +501,7 @@ function doCreateUri(path: string, query
if (!configElement || !configElementAttribute) { if (!configElement || !configElementAttribute) {
throw new Error('Missing web configuration element'); throw new Error('Missing web configuration element');
} }
- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute); - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute);
+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host } + const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host }
const secretStorageKeyPath = readCookie('vscode-secret-key-path');
// Create workbench const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported()
create(document.body, { ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto();
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts --- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
@@ -288,12 +284,12 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { RemoteAuthorities } from 'vs/base/common/network'; -import { RemoteAuthorities } from 'vs/base/common/network';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; @@ -102,7 +101,7 @@ export abstract class AbstractExtensionR
@@ -75,7 +74,7 @@ export abstract class AbstractExtensionR : version,
public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined { path: 'extension'
if (this._extensionGalleryResourceUrlTemplate) { }));
const uri = URI.parse(format2(this._extensionGalleryResourceUrlTemplate, { publisher: galleryExtension.publisher, name: galleryExtension.name, version: galleryExtension.version, path: 'extension' }));
- return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri; - return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri;
+ return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri; + return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri;
} }

View File

@@ -17,7 +17,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
@@ -97,10 +97,14 @@ class RemoteTerminalBackend extends Base @@ -104,10 +104,14 @@ class RemoteTerminalBackend extends Base
} }
const reqId = e.reqId; const reqId = e.reqId;
const commandId = e.commandId; const commandId = e.commandId;

62
patches/dependencies.diff Normal file
View File

@@ -0,0 +1,62 @@
Modify VS Code dependencies
1. Kerberos: this is not building in our cross-compile step. It does not look
like something code-server uses right now anyway.
Index: code-server/lib/vscode/remote/package.json
===================================================================
--- code-server.orig/lib/vscode/remote/package.json
+++ code-server/lib/vscode/remote/package.json
@@ -18,7 +18,6 @@
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"jschardet": "3.0.0",
- "kerberos": "^2.0.1",
"keytar": "7.9.0",
"minimist": "^1.2.6",
"native-watchdog": "^1.4.1",
Index: code-server/lib/vscode/remote/yarn.lock
===================================================================
--- code-server.orig/lib/vscode/remote/yarn.lock
+++ code-server/lib/vscode/remote/yarn.lock
@@ -454,15 +454,6 @@ jschardet@3.0.0:
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882"
integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==
-kerberos@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5"
- integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw==
- dependencies:
- bindings "^1.5.0"
- node-addon-api "^4.3.0"
- prebuild-install "7.1.1"
-
keytar@7.9.0:
version "7.9.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb"
@@ -604,24 +595,6 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-prebuild-install@7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
- integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==
- dependencies:
- detect-libc "^2.0.0"
- expand-template "^2.0.3"
- github-from-package "0.0.0"
- minimist "^1.2.3"
- mkdirp-classic "^0.5.3"
- napi-build-utils "^1.0.1"
- node-abi "^3.3.0"
- pump "^3.0.0"
- rc "^1.2.7"
- simple-get "^4.0.0"
- tar-fs "^2.0.0"
- tunnel-agent "^0.6.0"
-
prebuild-install@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"

View File

@@ -7,7 +7,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
@@ -243,6 +243,10 @@ export class Extension implements IExten @@ -244,6 +244,10 @@ export class Extension implements IExten
if (this.type === ExtensionType.System && this.productService.quality === 'stable') { if (this.type === ExtensionType.System && this.productService.quality === 'stable') {
return false; return false;
} }

View File

@@ -12,7 +12,7 @@ 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.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -260,6 +260,11 @@ export interface IWorkbenchConstructionO @@ -288,6 +288,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly userDataPath?: string readonly userDataPath?: string
@@ -28,19 +28,19 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -32,6 +32,11 @@ export interface IBrowserWorkbenchEnviro @@ -34,6 +34,11 @@ export interface IBrowserWorkbenchEnviro
* Options used to configure the workbench.
*/
readonly options?: IWorkbenchConstructionOptions; readonly options?: IWorkbenchConstructionOptions;
+
+ /** /**
+ * Enable downloading files via menu actions. + * Enable downloading files via menu actions.
+ */ + */
+ readonly isEnabledFileDownloads?: boolean; + readonly isEnabledFileDownloads?: boolean;
} +
+ /**
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { * Gets whether a resolver extension is expected for the environment.
@@ -101,6 +106,13 @@ export class BrowserWorkbenchEnvironment */
readonly expectsResolverExtension: boolean;
@@ -111,6 +116,13 @@ export class BrowserWorkbenchEnvironment
return this.options.userDataPath; return this.options.userDataPath;
} }
@@ -58,7 +58,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -14,6 +14,7 @@ export const serverOptions: OptionDescri @@ -16,6 +16,7 @@ export const serverOptions: OptionDescri
/* ----- code-server ----- */ /* ----- code-server ----- */
'disable-update-check': { type: 'boolean' }, 'disable-update-check': { type: 'boolean' },
'auth': { type: 'string' }, 'auth': { type: 'string' },
@@ -66,7 +66,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -94,6 +95,7 @@ export interface ServerParsedArgs { @@ -97,6 +98,7 @@ export interface ServerParsedArgs {
/* ----- code-server ----- */ /* ----- code-server ----- */
'disable-update-check'?: boolean; 'disable-update-check'?: boolean;
'auth'?: string 'auth'?: string
@@ -78,7 +78,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -325,6 +325,7 @@ export class WebClientServer { @@ -332,6 +332,7 @@ export class WebClientServer {
remoteAuthority, remoteAuthority,
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
userDataPath: this._environmentService.userDataPath, userDataPath: this._environmentService.userDataPath,
@@ -90,29 +90,22 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@@ -7,12 +7,11 @@ import { Event } from 'vs/base/common/ev @@ -7,12 +7,12 @@ import { Event } from 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys'; -import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; +import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace'; import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService';
@@ -25,6 +24,7 @@ import { IPaneCompositePartService } fro @@ -79,7 +79,7 @@ export class WorkbenchContextKeysHandler
import { Schemas } from 'vs/base/common/network';
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
import { IProductService } from 'vs/platform/product/common/productService';
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
export class WorkbenchContextKeysHandler extends Disposable {
private inputFocusedContext: IContextKey<boolean>;
@@ -77,7 +77,7 @@ export class WorkbenchContextKeysHandler
@IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextKeyService private readonly contextKeyService: IContextKeyService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IConfigurationService private readonly configurationService: IConfigurationService, @IConfigurationService private readonly configurationService: IConfigurationService,
@@ -121,7 +114,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@IProductService private readonly productService: IProductService, @IProductService private readonly productService: IProductService,
@IEditorService private readonly editorService: IEditorService, @IEditorService private readonly editorService: IEditorService,
@IEditorResolverService private readonly editorResolverService: IEditorResolverService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService,
@@ -205,6 +205,9 @@ export class WorkbenchContextKeysHandler @@ -209,6 +209,9 @@ export class WorkbenchContextKeysHandler
this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService); this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)); this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
@@ -144,7 +137,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ThemeIcon } from 'vs/base/common/themables'; import { ThemeIcon } from 'vs/base/common/themables';
@@ -485,13 +485,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo @@ -489,13 +489,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
id: DOWNLOAD_COMMAND_ID, id: DOWNLOAD_COMMAND_ID,
title: DOWNLOAD_LABEL title: DOWNLOAD_LABEL
}, },
@@ -172,7 +165,7 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts --- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
@@ -35,6 +35,8 @@ export const HasWebFileSystemAccess = ne @@ -38,6 +38,8 @@ export const HasWebFileSystemAccess = ne
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined')); export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));

View File

@@ -21,15 +21,15 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
@@ -233,6 +233,9 @@ export async function setupServerService @@ -231,6 +231,9 @@ export async function setupServerService
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)); const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel); socketServer.registerChannel('extensions', channel);
+ const languagePackChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ILanguagePackService)); + const languagePackChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ILanguagePackService));
+ socketServer.registerChannel('languagePacks', languagePackChannel); + socketServer.registerChannel('languagePacks', languagePackChannel);
+ +
const encryptionChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(IEncryptionMainService)); const credentialsChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ICredentialsMainService));
socketServer.registerChannel('encryption', encryptionChannel); socketServer.registerChannel('credentials', credentialsChannel);
Index: code-server/lib/vscode/src/vs/base/common/platform.ts Index: code-server/lib/vscode/src/vs/base/common/platform.ts
=================================================================== ===================================================================
@@ -89,10 +89,10 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
<!-- Workbench Icon/Manifest/CSS --> <!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" /> <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" type="image/x-icon" /> <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" type="image/x-icon" />
@@ -46,15 +49,26 @@ @@ -48,15 +51,26 @@
// Set up nls if the user is not using the default language (English) // Normalize locale to lowercase because translationServiceUrl is case-sensitive.
const nlsConfig = {}; // ref: https://github.com/microsoft/vscode/issues/187795
const locale = window.localStorage.getItem('vscode.nls.locale') || navigator.language; const locale = window.localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase();
- if (!locale.startsWith('en')) { - if (!locale.startsWith('en')) {
- nlsConfig['vs/nls'] = { - nlsConfig['vs/nls'] = {
- availableLanguages: { - availableLanguages: {
@@ -203,15 +203,15 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -26,6 +26,7 @@ import { URI } from 'vs/base/common/uri' @@ -27,6 +27,7 @@ import { URI } from 'vs/base/common/uri'
import { streamToBuffer } from 'vs/base/common/buffer'; import { streamToBuffer } from 'vs/base/common/buffer';
import { IProductConfiguration } from 'vs/base/common/product'; import { IProductConfiguration } from 'vs/base/common/product';
import { isString } from 'vs/base/common/types'; import { isString } from 'vs/base/common/types';
+import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks'; +import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@@ -337,6 +338,8 @@ export class WebClientServer { @@ -344,6 +345,8 @@ export class WebClientServer {
callbackRoute: this._callbackRoute callbackRoute: this._callbackRoute
}; };
@@ -220,19 +220,19 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl; const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
const values: { [key: string]: string } = { const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration), WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
@@ -345,6 +348,7 @@ export class WebClientServer { @@ -352,6 +355,7 @@ export class WebClientServer {
WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''), WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
BASE: base, BASE: base,
VS_BASE: vscodeBase, VS_BASE: vscodeBase,
+ NLS_CONFIGURATION: asJSON(nlsConfiguration), + NLS_CONFIGURATION: asJSON(nlsConfiguration),
}; };
let data; if (useTestResolver) {
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -15,6 +15,7 @@ export const serverOptions: OptionDescri @@ -17,6 +17,7 @@ export const serverOptions: OptionDescri
'disable-update-check': { type: 'boolean' }, 'disable-update-check': { type: 'boolean' },
'auth': { type: 'string' }, 'auth': { type: 'string' },
'disable-file-downloads': { type: 'boolean' }, 'disable-file-downloads': { type: 'boolean' },
@@ -240,7 +240,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -96,6 +97,7 @@ export interface ServerParsedArgs { @@ -99,6 +100,7 @@ export interface ServerParsedArgs {
'disable-update-check'?: boolean; 'disable-update-check'?: boolean;
'auth'?: string 'auth'?: string
'disable-file-downloads'?: boolean; 'disable-file-downloads'?: boolean;
@@ -261,7 +261,7 @@ Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
import 'vs/workbench/services/path/browser/pathService'; import 'vs/workbench/services/path/browser/pathService';
import 'vs/workbench/services/themes/browser/browserHostColorSchemeService'; import 'vs/workbench/services/themes/browser/browserHostColorSchemeService';
import 'vs/workbench/services/encryption/browser/encryptionService'; import 'vs/workbench/services/encryption/browser/encryptionService';
@@ -116,8 +116,9 @@ registerSingleton(ILanguagePackService, @@ -117,8 +117,9 @@ registerSingleton(ILanguagePackService,
// Logs // Logs
import 'vs/workbench/contrib/logs/browser/logs.contribution'; import 'vs/workbench/contrib/logs/browser/logs.contribution';
@@ -339,7 +339,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -335,9 +335,6 @@ export abstract class AbstractInstallAct @@ -321,9 +321,6 @@ export class InstallAction extends Exten
if (this.extension.isBuiltin) { if (this.extension.isBuiltin) {
return; return;
} }
@@ -349,7 +349,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) { if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) {
this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion; this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion;
this.updateLabel(); this.updateLabel();
@@ -715,7 +712,7 @@ export abstract class InstallInOtherServ @@ -591,7 +588,7 @@ export abstract class InstallInOtherServ
} }
if (isLanguagePackExtension(this.extension.local.manifest)) { if (isLanguagePackExtension(this.extension.local.manifest)) {
@@ -358,7 +358,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
} }
// Prefers to run on UI // Prefers to run on UI
@@ -1803,17 +1800,6 @@ export class SetLanguageAction extends E @@ -1683,17 +1680,6 @@ export class SetLanguageAction extends E
update(): void { update(): void {
this.enabled = false; this.enabled = false;
this.class = SetLanguageAction.DisabledClass; this.class = SetLanguageAction.DisabledClass;
@@ -376,7 +376,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
} }
override async run(): Promise<any> { override async run(): Promise<any> {
@@ -1830,7 +1816,6 @@ export class ClearLanguageAction extends @@ -1710,7 +1696,6 @@ export class ClearLanguageAction extends
private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`; private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`;
constructor( constructor(
@@ -384,7 +384,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
@ILocaleService private readonly localeService: ILocaleService, @ILocaleService private readonly localeService: ILocaleService,
) { ) {
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false); super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
@@ -1840,17 +1825,6 @@ export class ClearLanguageAction extends @@ -1720,17 +1705,6 @@ export class ClearLanguageAction extends
update(): void { update(): void {
this.enabled = false; this.enabled = false;
this.class = ClearLanguageAction.DisabledClass; this.class = ClearLanguageAction.DisabledClass;

View File

@@ -10,16 +10,25 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
@@ -60,7 +60,7 @@ import { GettingStartedIndexList } from @@ -10,7 +10,7 @@ import { IInstantiationService } from 'v
import { IEditorSerializer, IEditorOpenContext } from 'vs/workbench/common/editor';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { assertIsDefined } from 'vs/base/common/types';
-import { $, addDisposableListener, append, clearNode, Dimension, reset } from 'vs/base/browser/dom';
+import { $, addDisposableListener, append, clearNode, Dimension, reset, prepend } from 'vs/base/browser/dom';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IProductService } from 'vs/platform/product/common/productService';
import { hiddenEntriesConfigurationKey, IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService';
@@ -59,7 +59,7 @@ import { GettingStartedIndexList } from
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes'; import { KeyCode } from 'vs/base/common/keyCodes';
import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils';
-import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; -import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
+import { IsEnabledCoderGettingStarted, WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { IsEnabledCoderGettingStarted, WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { OpenFolderAction, OpenFileFolderAction, OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions';
import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle';
@@ -817,6 +817,72 @@ export class GettingStartedPage extends @@ -783,6 +783,72 @@ export class GettingStartedPage extends
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")) $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
); );
@@ -92,62 +101,16 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
const leftColumn = $('.categories-column.categories-column-left', {},); const leftColumn = $('.categories-column.categories-column-left', {},);
const rightColumn = $('.categories-column.categories-column-right', {},); const rightColumn = $('.categories-column.categories-column-right', {},);
@@ -835,13 +901,23 @@ export class GettingStartedPage extends @@ -832,6 +898,9 @@ export class GettingStartedPage extends
const layoutLists = () => { recentList.setLimit(5);
if (gettingStartedList.itemCount) { reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
this.container.classList.remove('noWalkthroughs');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ }
} }
else { + if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
this.container.classList.add('noWalkthroughs'); + prepend(rightColumn, gettingStartedCoder)
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement()); + }
- reset(rightColumn, featuredExtensionList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement());
+ }
}
setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50);
};
@@ -849,13 +925,23 @@ export class GettingStartedPage extends
const layoutFeaturedExtension = () => {
if (featuredExtensionList.itemCount) {
this.container.classList.remove('noExtensions');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ }
}
else {
this.container.classList.add('noExtensions');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, gettingStartedList.getDomElement());
+ }
}
setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50);
}; };
featuredExtensionList.onDidChange(layoutFeaturedExtension);
Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css --- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css
@@ -172,7 +135,7 @@ 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.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -265,6 +265,11 @@ export interface IWorkbenchConstructionO @@ -293,6 +293,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly isEnabledFileDownloads?: boolean readonly isEnabledFileDownloads?: boolean
@@ -188,19 +151,19 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -37,6 +37,11 @@ export interface IBrowserWorkbenchEnviro @@ -39,6 +39,11 @@ export interface IBrowserWorkbenchEnviro
* Enable downloading files via menu actions.
*/
readonly isEnabledFileDownloads?: boolean; readonly isEnabledFileDownloads?: boolean;
+
+ /** /**
+ * Enable Coder's custom getting started text. + * Enable Coder's custom getting started text.
+ */ + */
+ readonly isEnabledCoderGettingStarted?: boolean; + readonly isEnabledCoderGettingStarted?: boolean;
} +
+ /**
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { * Gets whether a resolver extension is expected for the environment.
@@ -113,6 +118,13 @@ export class BrowserWorkbenchEnvironment */
readonly expectsResolverExtension: boolean;
@@ -123,6 +128,13 @@ export class BrowserWorkbenchEnvironment
return this.options.isEnabledFileDownloads; return this.options.isEnabledFileDownloads;
} }
@@ -218,7 +181,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -16,6 +16,7 @@ export const serverOptions: OptionDescri @@ -18,6 +18,7 @@ export const serverOptions: OptionDescri
'auth': { type: 'string' }, 'auth': { type: 'string' },
'disable-file-downloads': { type: 'boolean' }, 'disable-file-downloads': { type: 'boolean' },
'locale': { type: 'string' }, 'locale': { type: 'string' },
@@ -226,7 +189,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -98,6 +99,7 @@ export interface ServerParsedArgs { @@ -101,6 +102,7 @@ export interface ServerParsedArgs {
'auth'?: string 'auth'?: string
'disable-file-downloads'?: boolean; 'disable-file-downloads'?: boolean;
'locale'?: string 'locale'?: string
@@ -238,7 +201,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -328,6 +328,7 @@ export class WebClientServer { @@ -335,6 +335,7 @@ export class WebClientServer {
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
userDataPath: this._environmentService.userDataPath, userDataPath: this._environmentService.userDataPath,
isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'],
@@ -254,12 +217,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; -import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted } from 'vs/workbench/common/contextkeys'; +import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -207,6 +207,7 @@ export class WorkbenchContextKeysHandler @@ -211,6 +211,7 @@ export class WorkbenchContextKeysHandler
// code-server // code-server
IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true)
@@ -271,7 +234,7 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts --- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
@@ -36,6 +36,7 @@ export const HasWebFileSystemAccess = ne @@ -39,6 +39,7 @@ export const HasWebFileSystemAccess = ne
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined')); export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));
export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true); export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true);

View File

@@ -176,7 +176,7 @@ 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.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
+++ code-server/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 { IndexedDB } from 'vs/base/brows @@ -67,6 +67,7 @@ import { IndexedDB } from 'vs/base/brows
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService'; import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
@@ -184,7 +184,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress'; import { IProgressService } from 'vs/platform/progress/common/progress';
import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel';
@@ -123,6 +124,9 @@ export class BrowserMain extends Disposa @@ -132,6 +133,9 @@ export class BrowserMain extends Disposa
// Startup // Startup
const instantiationService = workbench.startup(); const instantiationService = workbench.startup();
@@ -264,7 +264,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -299,6 +299,7 @@ export class WebClientServer { @@ -306,6 +306,7 @@ export class WebClientServer {
} : undefined; } : undefined;
const productConfiguration = <Partial<IProductConfiguration>>{ const productConfiguration = <Partial<IProductConfiguration>>{

View File

@@ -20,7 +20,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -320,6 +320,7 @@ export class WebClientServer { @@ -327,6 +327,7 @@ export class WebClientServer {
const workbenchWebConfiguration = { const workbenchWebConfiguration = {
remoteAuthority, remoteAuthority,
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
@@ -32,7 +32,7 @@ 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.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -255,6 +255,11 @@ export interface IWorkbenchConstructionO @@ -283,6 +283,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly configurationDefaults?: Record<string, any>; readonly configurationDefaults?: Record<string, any>;
@@ -48,7 +48,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -92,7 +92,14 @@ export class BrowserWorkbenchEnvironment @@ -102,7 +102,14 @@ export class BrowserWorkbenchEnvironment
get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); } get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }
@memoize @memoize

View File

@@ -20,7 +20,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -13,6 +13,7 @@ import { IEnvironmentService, INativeEnv @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = { export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
/* ----- code-server ----- */ /* ----- code-server ----- */
'disable-update-check': { type: 'boolean' }, 'disable-update-check': { type: 'boolean' },
@@ -28,7 +28,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -92,6 +93,7 @@ export const serverOptions: OptionDescri @@ -95,6 +96,7 @@ export const serverOptions: OptionDescri
export interface ServerParsedArgs { export interface ServerParsedArgs {
/* ----- code-server ----- */ /* ----- code-server ----- */
'disable-update-check'?: boolean; 'disable-update-check'?: boolean;
@@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -304,6 +304,7 @@ export class WebClientServer { @@ -311,6 +311,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion, codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base, rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,

View File

@@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -111,7 +111,7 @@ export class WebClientServer { @@ -113,7 +113,7 @@ export class WebClientServer {
const serverRootPath = getRemoteServerRootPath(_productService); const serverRootPath = getRemoteServerRootPath(_productService);
this._staticRoute = `${serverRootPath}/static`; this._staticRoute = `${serverRootPath}/static`;
this._callbackRoute = `${serverRootPath}/callback`; this._callbackRoute = `${serverRootPath}/callback`;
@@ -49,7 +49,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
} }
/** /**
@@ -304,14 +304,7 @@ export class WebClientServer { @@ -311,14 +311,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion, codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base, rootEndpoint: base,
embedderIdentifier: 'server-distro', embedderIdentifier: 'server-distro',
@@ -74,10 +74,10 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -77,7 +76,7 @@ export abstract class AbstractExtensionR
@@ -60,7 +59,7 @@ export abstract class AbstractExtensionR
private readonly _environmentService: IEnvironmentService, private readonly _environmentService: IEnvironmentService,
private readonly _configurationService: IConfigurationService, private readonly _configurationService: IConfigurationService,
) { ) {

View File

@@ -42,17 +42,8 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityReso
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
@@ -11,7 +11,7 @@ import { StopWatch } from 'vs/base/commo @@ -33,7 +33,7 @@ export class RemoteAuthorityResolverServ
import { URI } from 'vs/base/common/uri'; isWorkbenchOptionsBasedResolution: boolean,
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts';
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
@@ -29,7 +29,7 @@ export class RemoteAuthorityResolverServ
constructor(
connectionToken: Promise<string> | string | undefined, connectionToken: Promise<string> | string | undefined,
resourceUriProvider: ((uri: URI) => URI) | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined,
- @IProductService productService: IProductService, - @IProductService productService: IProductService,
@@ -60,7 +51,7 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityReso
@ILogService private readonly _logService: ILogService, @ILogService private readonly _logService: ILogService,
) { ) {
super(); super();
@@ -75,9 +75,14 @@ export class RemoteAuthorityResolverServ @@ -84,9 +84,14 @@ export class RemoteAuthorityResolverServ
const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken); const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken);
performance.mark(`code/didResolveConnectionToken/${authorityPrefix}`); performance.mark(`code/didResolveConnectionToken/${authorityPrefix}`);
this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`); this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`);
@@ -71,16 +62,16 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityReso
+ } + }
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80); const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort); const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort);
- const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken } }; - const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken } };
+ const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken }, options }; + const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken }, options };
RemoteAuthorities.set(authority, result.authority.host, result.authority.port); RemoteAuthorities.set(authority, host, port);
this._cache.set(authority, result); this._cache.set(authority, result);
this._onDidChangeConnectionData.fire(); this._onDidChangeConnectionData.fire();
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -305,6 +305,7 @@ export class WebClientServer { @@ -312,6 +312,7 @@ export class WebClientServer {
rootEndpoint: base, rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined, logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
@@ -92,7 +83,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalE
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
@@ -383,7 +383,7 @@ export async function createTerminalEnvi @@ -271,7 +271,7 @@ export async function createTerminalEnvi
// Sanitize the environment, removing any undesirable VS Code and Electron environment // Sanitize the environment, removing any undesirable VS Code and Electron environment
// variables // variables
@@ -105,33 +96,32 @@ 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.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
@@ -21,6 +21,7 @@ import type { ICredentialsProvider } fro @@ -19,6 +19,7 @@ import { isFolderToOpen, isWorkspaceToOp
import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api';
import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService';
import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService';
+import { extractLocalHostUriMetaDataForPortMapping, TunnelOptions, TunnelCreationOptions } from 'vs/platform/tunnel/common/tunnel'; +import { extractLocalHostUriMetaDataForPortMapping, TunnelOptions, TunnelCreationOptions } from 'vs/platform/tunnel/common/tunnel';
import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import { create } from 'vs/workbench/workbench.web.main';
interface ICredential { @@ -582,6 +583,39 @@ function readCookie(name: string): strin
service: string; settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
@@ -511,6 +512,38 @@ function doCreateUri(path: string, query
} : undefined,
workspaceProvider: WorkspaceProvider.create(config), workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
- credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider() // with a remote, we don't use a local credentials provider
+ credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider(), // with a remote, we don't use a local credentials provider
+ resolveExternalUri: (uri: URI): Promise<URI> => { + resolveExternalUri: (uri: URI): Promise<URI> => {
+ let resolvedUri = uri + let resolvedUri = uri
+ const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri) + const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
+
+ if (localhostMatch && resolvedUri.authority !== location.host) { + if (localhostMatch && resolvedUri.authority !== location.host) {
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) { + if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
+ resolvedUri = URI.parse(new URL(config.productConfiguration.proxyEndpointTemplate.replace('{{port}}', localhostMatch.port.toString()), window.location.href).toString()) + const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
+ .replace('{{port}}', localhostMatch.port.toString())
+ .replace('{{host}}', window.location.host)
+ resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
+ } else { + } else {
+ throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`) + throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
+ } + }
+ } + }
+ + // If not localhost, return unmodified.
+ // If not localhost, return unmodified
+ return Promise.resolve(resolvedUri) + return Promise.resolve(resolvedUri)
+ }, + },
+ tunnelProvider: { + tunnelProvider: {
@@ -150,19 +140,20 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
+ } + }
+ }) + })
+ } + }
+ } + },
}); secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath
})(); ? undefined /* with a remote without embedder-preferred storage, store on the remote */
: new LocalStorageSecretStorageProvider(secretStorageCrypto),
Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
@@ -73,7 +73,7 @@ export class ForwardedPortsView extends @@ -76,7 +76,7 @@ export class ForwardedPortsView extends
this.contextKeyListener = undefined; this.contextKeyListener = undefined;
} }
- const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService); - const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
+ const viewEnabled: boolean = true; + const viewEnabled: boolean = true;
if (this.environmentService.remoteAuthority && viewEnabled) { if (viewEnabled) {
const viewContainer = await this.getViewContainer(); const viewContainer = await this.getViewContainer();

68
patches/safari.diff Normal file
View File

@@ -0,0 +1,68 @@
Revert back to es2020
es2022 outputs static blocks when using static properties that are not
compatible with Safari, or at least not older versions of Safari.
Index: code-server/lib/vscode/src/tsconfig.base.json
===================================================================
--- code-server.orig/lib/vscode/src/tsconfig.base.json
+++ code-server/lib/vscode/src/tsconfig.base.json
@@ -17,9 +17,30 @@
"./vs/*"
]
},
- "target": "es2022",
- "useDefineForClassFields": false,
+ "target": "es2020",
"lib": [
+ "ES2016",
+ "ES2017.Object",
+ "ES2017.String",
+ "ES2017.Intl",
+ "ES2017.TypedArrays",
+ "ES2018.AsyncIterable",
+ "ES2018.AsyncGenerator",
+ "ES2018.Promise",
+ "ES2018.Regexp",
+ "ES2018.Intl",
+ "ES2019.Array",
+ "ES2019.Object",
+ "ES2019.String",
+ "ES2019.Symbol",
+ "ES2020.BigInt",
+ "ES2020.Promise",
+ "ES2020.String",
+ "ES2020.Symbol.WellKnown",
+ "ES2020.Intl",
+ "ES2021.Promise",
+ "ES2021.String",
+ "ES2021.WeakRef",
"ES2022",
"DOM",
"DOM.Iterable",
Index: code-server/lib/vscode/build/lib/tsb/transpiler.js
===================================================================
--- code-server.orig/lib/vscode/build/lib/tsb/transpiler.js
+++ code-server/lib/vscode/build/lib/tsb/transpiler.js
@@ -293,7 +293,7 @@ class SwcTranspiler {
tsx: false,
decorators: true
},
- target: 'es2022',
+ target: 'es2020',
loose: false,
minify: {
compress: false,
Index: code-server/lib/vscode/build/lib/tsb/transpiler.ts
===================================================================
--- code-server.orig/lib/vscode/build/lib/tsb/transpiler.ts
+++ code-server/lib/vscode/build/lib/tsb/transpiler.ts
@@ -376,7 +376,7 @@ export class SwcTranspiler implements IT
tsx: false,
decorators: true
},
- target: 'es2022',
+ target: 'es2020',
loose: false,
minify: {
compress: false,

View File

@@ -19,3 +19,4 @@ telemetry.diff
display-language.diff display-language.diff
cli-window-open.diff cli-window-open.diff
getting-started.diff getting-started.diff
safari.diff

View File

@@ -54,7 +54,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -306,6 +306,10 @@ export class WebClientServer { @@ -313,6 +313,10 @@ export class WebClientServer {
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined, logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/', proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',

View File

@@ -10,7 +10,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/build/gulpfile.reh.js --- code-server.orig/lib/vscode/build/gulpfile.reh.js
+++ code-server/lib/vscode/build/gulpfile.reh.js +++ code-server/lib/vscode/build/gulpfile.reh.js
@@ -197,8 +197,7 @@ function packageTask(type, platform, arc @@ -238,8 +238,7 @@ function packageTask(type, platform, arc
const src = gulp.src(sourceFolderName + '/**', { base: '.' }) const src = gulp.src(sourceFolderName + '/**', { base: '.' })
.pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); })) .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); }))
@@ -20,7 +20,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
const workspaceExtensionPoints = ['debuggers', 'jsonValidation']; const workspaceExtensionPoints = ['debuggers', 'jsonValidation'];
const isUIExtension = (manifest) => { const isUIExtension = (manifest) => {
@@ -237,9 +236,9 @@ function packageTask(type, platform, arc @@ -278,9 +277,9 @@ function packageTask(type, platform, arc
.map(name => `.build/extensions/${name}/**`); .map(name => `.build/extensions/${name}/**`);
const extensions = gulp.src(extensionPaths, { base: '.build', dot: true }); const extensions = gulp.src(extensionPaths, { base: '.build', dot: true });
@@ -32,7 +32,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
let version = packageJson.version; let version = packageJson.version;
const quality = product.quality; const quality = product.quality;
@@ -394,7 +393,7 @@ function tweakProductForServerWeb(produc @@ -427,7 +426,7 @@ function tweakProductForServerWeb(produc
const minifyTask = task.define(`minify-vscode-${type}`, task.series( const minifyTask = task.define(`minify-vscode-${type}`, task.series(
optimizeTask, optimizeTask,
util.rimraf(`out-vscode-${type}-min`), util.rimraf(`out-vscode-${type}-min`),

View File

@@ -1,4 +1,4 @@
Store a static reference to the IPC socket Store the IPC socket with workspace metadata.
This lets us use it to open files inside code-server from outside of This lets us use it to open files inside code-server from outside of
code-server. code-server.
@@ -9,29 +9,121 @@ To test this:
It should open in your existing code-server instance. It should open in your existing code-server instance.
When the extension host is terminated, the socket is unregistered.
Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts 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.orig/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
+++ code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts +++ code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
@@ -2,7 +2,9 @@ @@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
- -
+import { promises as fs } from 'fs'; +import * as _http from 'http';
+import * as os from 'os'
+import * as path from 'vs/base/common/path';
import * as performance from 'vs/base/common/performance'; import * as performance from 'vs/base/common/performance';
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
@@ -72,6 +74,10 @@ export class ExtHostExtensionService ext @@ -17,6 +17,7 @@ import { ExtensionRuntime } from 'vs/wor
if (this._initData.remote.isRemote && this._initData.remote.authority) { import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
const cliServer = this._instaService.createInstance(CLIServer); import { realpathSync } from 'vs/base/node/extpath';
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath; import { ExtHostConsoleForwarder } from 'vs/workbench/api/node/extHostConsoleForwarder';
+ +import { IExtHostWorkspace } from '../common/extHostWorkspace';
+ fs.writeFile(path.join(os.tmpdir(), 'vscode-ipc'), cliServer.ipcHandlePath).catch((error) => { import { ExtHostDiskFileSystemProvider } from 'vs/workbench/api/node/extHostDiskFileSystemProvider';
+ this._logService.error(error);
+ });
}
// Module loading tricks class NodeModuleRequireInterceptor extends RequireInterceptor {
@@ -83,6 +84,52 @@ export class ExtHostExtensionService ext
await interceptor.install();
performance.mark('code/extHost/didInitAPI');
+ (async () => {
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
+ if (!socketPath || !codeServerSocketPath) {
+ return;
+ }
+ const workspace = this._instaService.invokeFunction((accessor) => {
+ const workspaceService = accessor.get(IExtHostWorkspace);
+ return workspaceService.workspace;
+ });
+ const entry = {
+ workspace,
+ socketPath
+ };
+ const message = JSON.stringify({entry});
+ await new Promise<void>((resolve, reject) => {
+ const opts: _http.RequestOptions = {
+ path: '/add-session',
+ socketPath: codeServerSocketPath,
+ method: 'POST',
+ headers: {
+ 'content-type': 'application/json',
+ }
+ };
+ const req = _http.request(opts, (res) => {
+ res.on('error', reject);
+ res.on('end', () => {
+ try {
+ if (res.statusCode === 200) {
+ resolve();
+ } else {
+ reject(new Error('Unexpected status code: ' + res.statusCode));
+ }
+ } catch (e: unknown) {
+ reject(e);
+ }
+ });
+ });
+ req.on('error', reject);
+ req.write(message);
+ req.end();
+ });
+ })().catch(error => {
+ this._logService.error(error);
+ });
+
// Do this when extension service exists, but extensions are not being activated yet.
const configProvider = await this._extHostConfiguration.getConfigProvider();
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy, this._initData);
Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
+++ code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import * as _http from 'http';
import * as nativeWatchdog from 'native-watchdog';
import * as net from 'net';
import * as minimist from 'minimist';
@@ -400,7 +401,28 @@ async function startExtensionHostProcess
);
// rewrite onTerminate-function to be a proper shutdown
- onTerminate = (reason: string) => extensionHostMain.terminate(reason);
+ onTerminate = (reason: string) => {
+ extensionHostMain.terminate(reason);
+
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
+ if (!socketPath || !codeServerSocketPath) {
+ return;
+ }
+ const message = JSON.stringify({socketPath});
+ const opts: _http.RequestOptions = {
+ path: '/delete-session',
+ socketPath: codeServerSocketPath,
+ method: 'POST',
+ headers: {
+ 'content-type': 'application/json',
+ 'accept': 'application/json'
+ }
+ };
+ const req = _http.request(opts);
+ req.write(message);
+ req.end();
+ };
}
startExtensionHostProcess().catch((err) => console.log(err));

View File

@@ -12,7 +12,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
@@ -69,6 +69,7 @@ import { IExtensionsScannerService } fro @@ -67,6 +67,7 @@ import { IExtensionsScannerService } fro
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
@@ -20,22 +20,18 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { NullPolicyService } from 'vs/platform/policy/common/policy';
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
import { LoggerService } from 'vs/platform/log/node/loggerService'; import { LoggerService } from 'vs/platform/log/node/loggerService';
@@ -150,10 +151,13 @@ export async function setupServerService @@ -150,7 +151,10 @@ export async function setupServerService
let oneDsAppender: ITelemetryAppender = NullAppender; let oneDsAppender: ITelemetryAppender = NullAppender;
const isInternal = isInternalTelemetry(productService, configurationService); const isInternal = isInternalTelemetry(productService, configurationService);
if (supportsTelemetry(productService, environmentService)) { if (supportsTelemetry(productService, environmentService)) {
- if (productService.aiConfig && productService.aiConfig.ariaKey) { - if (productService.aiConfig && productService.aiConfig.ariaKey) {
+ const telemetryEndpoint = process.env.CS_TELEMETRY_URL || "https://v1.telemetry.coder.com/track"; + const telemetryEndpoint = process.env.CS_TELEMETRY_URL || "https://v1.telemetry.coder.com/track";
+ if (telemetryEndpoint) { + if (telemetryEndpoint) {
+ oneDsAppender = new OneDataSystemAppender(false, eventPrefix, null, () => new TelemetryClient(telemetryEndpoint)); + oneDsAppender = new OneDataSystemAppender(requestService, false, eventPrefix, null, () => new TelemetryClient(telemetryEndpoint));
+ } else if (productService.aiConfig && productService.aiConfig.ariaKey) { + } else if (productService.aiConfig && productService.aiConfig.ariaKey) {
oneDsAppender = new OneDataSystemAppender(isInternal, eventPrefix, null, productService.aiConfig.ariaKey); oneDsAppender = new OneDataSystemAppender(requestService, isInternal, eventPrefix, null, productService.aiConfig.ariaKey);
- disposables.add(toDisposable(() => oneDsAppender?.flush())); // Ensure the AI appender is disposed so that it flushes remaining data disposables.add(toDisposable(() => oneDsAppender?.flush())); // Ensure the AI appender is disposed so that it flushes remaining data
} }
+ disposables.add(toDisposable(() => oneDsAppender?.flush())); // Ensure the AI appender is disposed so that it flushes remaining data
const config: ITelemetryServiceConfig = {
appenders: [oneDsAppender],
Index: code-server/lib/vscode/src/vs/server/node/telemetryClient.ts Index: code-server/lib/vscode/src/vs/server/node/telemetryClient.ts
=================================================================== ===================================================================
--- /dev/null --- /dev/null
@@ -94,7 +90,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -310,6 +310,7 @@ export class WebClientServer { @@ -317,6 +317,7 @@ export class WebClientServer {
scope: vscodeBase + '/', scope: vscodeBase + '/',
path: base + '/_static/out/browser/serviceWorker.js', path: base + '/_static/out/browser/serviceWorker.js',
}, },

View File

@@ -105,7 +105,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -303,6 +303,7 @@ export class WebClientServer { @@ -310,6 +310,7 @@ export class WebClientServer {
const productConfiguration = <Partial<IProductConfiguration>>{ const productConfiguration = <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion, codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base, rootEndpoint: base,
@@ -117,8 +117,8 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -11,6 +11,8 @@ import { refineServiceDecorator } from ' @@ -13,6 +13,8 @@ import { memoize } from 'vs/base/common/
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri';
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = { export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
+ /* ----- code-server ----- */ + /* ----- code-server ----- */
@@ -126,7 +126,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -88,6 +90,8 @@ export const serverOptions: OptionDescri @@ -91,6 +93,8 @@ export const serverOptions: OptionDescri
}; };
export interface ServerParsedArgs { export interface ServerParsedArgs {

View File

@@ -41,7 +41,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -215,7 +215,7 @@ export class BrowserWorkbenchEnvironment @@ -225,7 +225,7 @@ export class BrowserWorkbenchEnvironment
@memoize @memoize
get webviewExternalEndpoint(): string { get webviewExternalEndpoint(): string {
@@ -54,7 +54,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
=================================================================== ===================================================================
--- code-server.orig/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 +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -316,6 +316,7 @@ export class WebClientServer { @@ -323,6 +323,7 @@ export class WebClientServer {
const workbenchWebConfiguration = { const workbenchWebConfiguration = {
remoteAuthority, remoteAuthority,
@@ -70,12 +70,12 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" <meta http-equiv="Content-Security-Policy"
- content="default-src 'none'; script-src 'sha256-N4YFn5ze5crjPqMK/opogKs7bSGWtf3lmjV/3LfbSOs=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> - content="default-src 'none'; script-src 'sha256-QA1gXilHYAUFCvp7MpjgcmyBCFzSKV0SpiecMU8aUVc=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-B5FRTRmagxqZ2yiS/ip5EgFZJPHAF0G0O3NgwgN6hhg=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> + content="default-src 'none'; script-src 'sha256-5X5RiKYn8NTJVx919WStPrAmsV80rIIBbePhKquPcAQ=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming --> <!-- Disable pinch zooming -->
<meta name="viewport" <meta name="viewport"
@@ -334,6 +334,12 @@ @@ -335,6 +335,12 @@
const hostname = location.hostname; const hostname = location.hostname;
@@ -92,7 +92,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html --- code-server.orig/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
+++ code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
@@ -333,6 +333,12 @@ @@ -334,6 +334,12 @@
const hostname = location.hostname; const hostname = location.hostname;

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />

View File

@@ -46,7 +46,9 @@ button {
.card-box { .card-box {
background-color: rgb(250, 253, 258); background-color: rgb(250, 253, 258);
border-radius: 5px; border-radius: 5px;
box-shadow: rgba(60, 66, 87, 0.117647) 0px 7px 14px 0px, rgba(0, 0, 0, 0.117647) 0px 3px 6px 0px; box-shadow:
rgba(60, 66, 87, 0.117647) 0px 7px 14px 0px,
rgba(0, 0, 0, 0.117647) 0px 3px 6px 0px;
max-width: 650px; max-width: 650px;
width: 100%; width: 100%;
} }

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />

View File

@@ -14,7 +14,11 @@ export enum HttpCode {
* used in the HTTP response. * used in the HTTP response.
*/ */
export class HttpError extends Error { export class HttpError extends Error {
public constructor(message: string, public readonly statusCode: HttpCode, public readonly details?: object) { public constructor(
message: string,
public readonly statusCode: HttpCode,
public readonly details?: object,
) {
super(message) super(message)
this.name = this.constructor.name this.name = this.constructor.name
} }

View File

@@ -9,9 +9,11 @@ import * as util from "../common/util"
import { DefaultedArgs } from "./cli" import { DefaultedArgs } from "./cli"
import { disposer } from "./http" import { disposer } from "./http"
import { isNodeJSErrnoException } from "./util" import { isNodeJSErrnoException } from "./util"
import { EditorSessionManager, makeEditorSessionManagerServer } from "./vscodeSocket"
import { handleUpgrade } from "./wsRouter" import { handleUpgrade } from "./wsRouter"
type ListenOptions = Pick<DefaultedArgs, "socket-mode" | "socket" | "port" | "host"> type SocketOptions = { socket: string; "socket-mode"?: string }
type ListenOptions = DefaultedArgs | SocketOptions
export interface App extends Disposable { export interface App extends Disposable {
/** Handles regular HTTP requests. */ /** Handles regular HTTP requests. */
@@ -20,12 +22,18 @@ export interface App extends Disposable {
wsRouter: Express wsRouter: Express
/** The underlying HTTP server. */ /** The underlying HTTP server. */
server: http.Server server: http.Server
/** Handles requests to the editor session management API. */
editorSessionManagerServer: http.Server
} }
export const listen = async (server: http.Server, { host, port, socket, "socket-mode": mode }: ListenOptions) => { const isSocketOpts = (opts: ListenOptions): opts is SocketOptions => {
if (socket) { return !!(opts as SocketOptions).socket || !(opts as DefaultedArgs).host
}
export const listen = async (server: http.Server, opts: ListenOptions) => {
if (isSocketOpts(opts)) {
try { try {
await fs.unlink(socket) await fs.unlink(opts.socket)
} catch (error: any) { } catch (error: any) {
handleArgsSocketCatchError(error) handleArgsSocketCatchError(error)
} }
@@ -38,18 +46,20 @@ export const listen = async (server: http.Server, { host, port, socket, "socket-
server.on("error", (err) => util.logError(logger, "http server error", err)) server.on("error", (err) => util.logError(logger, "http server error", err))
resolve() resolve()
} }
if (socket) { if (isSocketOpts(opts)) {
server.listen(socket, onListen) server.listen(opts.socket, onListen)
} else { } else {
// [] is the correct format when using :: but Node errors with them. // [] is the correct format when using :: but Node errors with them.
server.listen(port, host.replace(/^\[|\]$/g, ""), onListen) server.listen(opts.port, opts.host.replace(/^\[|\]$/g, ""), onListen)
} }
}) })
// NOTE@jsjoeio: we need to chmod after the server is finished // NOTE@jsjoeio: we need to chmod after the server is finished
// listening. Otherwise, the socket may not have been created yet. // listening. Otherwise, the socket may not have been created yet.
if (socket && mode) { if (isSocketOpts(opts)) {
await fs.chmod(socket, mode) if (opts["socket-mode"]) {
await fs.chmod(opts.socket, opts["socket-mode"])
}
} }
} }
@@ -70,14 +80,22 @@ export const createApp = async (args: DefaultedArgs): Promise<App> => {
) )
: http.createServer(router) : http.createServer(router)
const dispose = disposer(server) const disposeServer = disposer(server)
await listen(server, args) await listen(server, args)
const wsRouter = express() const wsRouter = express()
handleUpgrade(wsRouter, server) handleUpgrade(wsRouter, server)
return { router, wsRouter, server, dispose } const editorSessionManager = new EditorSessionManager()
const editorSessionManagerServer = await makeEditorSessionManagerServer(args["session-socket"], editorSessionManager)
const disposeEditorSessionManagerServer = disposer(editorSessionManagerServer)
const dispose = async () => {
await Promise.all([disposeServer(), disposeEditorSessionManagerServer()])
}
return { router, wsRouter, server, dispose, editorSessionManagerServer }
} }
/** /**

View File

@@ -1,19 +1,9 @@
import { field, Level, logger } from "@coder/logger" import { field, Level, logger } from "@coder/logger"
import { promises as fs } from "fs" import { promises as fs } from "fs"
import { load } from "js-yaml" import { load } from "js-yaml"
import * as os from "os"
import * as path from "path" import * as path from "path"
import { import { generateCertificate, generatePassword, paths, splitOnFirstEquals } from "./util"
canConnect, import { EditorSessionManagerClient } from "./vscodeSocket"
generateCertificate,
generatePassword,
humanPath,
paths,
isNodeJSErrnoException,
splitOnFirstEquals,
} from "./util"
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
export enum Feature { export enum Feature {
// No current experimental features! // No current experimental features!
@@ -60,6 +50,8 @@ export interface UserProvidedCodeArgs {
"disable-file-downloads"?: boolean "disable-file-downloads"?: boolean
"disable-workspace-trust"?: boolean "disable-workspace-trust"?: boolean
"disable-getting-started-override"?: boolean "disable-getting-started-override"?: boolean
"disable-proxy"?: boolean
"session-socket"?: string
} }
/** /**
@@ -87,6 +79,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"bind-addr"?: string "bind-addr"?: string
socket?: string socket?: string
"socket-mode"?: string "socket-mode"?: string
"trusted-origins"?: string[]
version?: boolean version?: boolean
"proxy-domain"?: string[] "proxy-domain"?: string[]
"reuse-window"?: boolean "reuse-window"?: boolean
@@ -169,6 +162,9 @@ export const options: Options<Required<UserProvidedArgs>> = {
"Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" + "Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" +
"then notifies you once every week that a new release is available.", "then notifies you once every week that a new release is available.",
}, },
"session-socket": {
type: "string",
},
"disable-file-downloads": { "disable-file-downloads": {
type: "boolean", type: "boolean",
description: description:
@@ -182,6 +178,10 @@ export const options: Options<Required<UserProvidedArgs>> = {
type: "boolean", type: "boolean",
description: "Disable the coder/coder override in the Help: Getting Started page.", description: "Disable the coder/coder override in the Help: Getting Started page.",
}, },
"disable-proxy": {
type: "boolean",
description: "Disable domain and path proxy routes.",
},
// --enable can be used to enable experimental features. These features // --enable can be used to enable experimental features. These features
// provide no guarantees. // provide no guarantees.
enable: { type: "string[]" }, enable: { type: "string[]" },
@@ -213,6 +213,11 @@ export const options: Options<Required<UserProvidedArgs>> = {
socket: { type: "string", path: true, description: "Path to a socket (bind-addr will be ignored)." }, socket: { type: "string", path: true, description: "Path to a socket (bind-addr will be ignored)." },
"socket-mode": { type: "string", description: "File mode of the socket." }, "socket-mode": { type: "string", description: "File mode of the socket." },
"trusted-origins": {
type: "string[]",
description:
"Disables authenticate origin check for trusted origin. Useful if not able to access reverse proxy configuration.",
},
version: { type: "boolean", short: "v", description: "Display version information." }, version: { type: "boolean", short: "v", description: "Display version information." },
_: { type: "string[]" }, _: { type: "string[]" },
@@ -468,6 +473,7 @@ export interface DefaultedArgs extends ConfigArgs {
usingEnvHashedPassword: boolean usingEnvHashedPassword: boolean
"extensions-dir": string "extensions-dir": string
"user-data-dir": string "user-data-dir": string
"session-socket": string
/* Positional arguments. */ /* Positional arguments. */
_: string[] _: string[]
} }
@@ -488,6 +494,11 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions") args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
} }
if (!args["session-socket"]) {
args["session-socket"] = path.join(args["user-data-dir"], "code-server-ipc.sock")
}
process.env.CODE_SERVER_SESSION_SOCKET = args["session-socket"]
// --verbose takes priority over --log and --log takes priority over the // --verbose takes priority over --log and --log takes priority over the
// environment variable. // environment variable.
if (args.verbose) { if (args.verbose) {
@@ -557,6 +568,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["disable-getting-started-override"] = true args["disable-getting-started-override"] = true
} }
if (process.env.CS_DISABLE_PROXY?.match(/^(1|true)$/)) {
args["disable-proxy"] = true
}
const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD
if (process.env.HASHED_PASSWORD) { if (process.env.HASHED_PASSWORD) {
args["hashed-password"] = process.env.HASHED_PASSWORD args["hashed-password"] = process.env.HASHED_PASSWORD
@@ -574,14 +589,24 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
// Filter duplicate proxy domains and remove any leading `*.`. // Filter duplicate proxy domains and remove any leading `*.`.
const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, ""))) const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
args["proxy-domain"] = Array.from(proxyDomains) const finalProxies = []
if (args["proxy-domain"].length > 0 && !process.env.VSCODE_PROXY_URI) {
process.env.VSCODE_PROXY_URI = `{{port}}.${args["proxy-domain"][0]}` for (const proxyDomain of proxyDomains) {
if (!proxyDomain.includes("{{port}}")) {
finalProxies.push("{{port}}." + proxyDomain)
} else {
finalProxies.push(proxyDomain)
}
} }
if (typeof args._ === "undefined") { // all proxies are of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
args._ = [] // e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}
if (finalProxies.length > 0 && !process.env.VSCODE_PROXY_URI) {
process.env.VSCODE_PROXY_URI = `//${finalProxies[0]}`
} }
args["proxy-domain"] = finalProxies
args._ = getResolvedPathsFromArgs(args)
return { return {
...args, ...args,
@@ -590,6 +615,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled. } as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
} }
export function getResolvedPathsFromArgs(args: UserProvidedArgs): string[] {
return (args._ ?? []).map((p) => path.resolve(p))
}
/** /**
* Helper function to return the default config file. * Helper function to return the default config file.
* *
@@ -633,7 +662,7 @@ export async function readConfigFile(configPath?: string): Promise<ConfigArgs> {
await fs.writeFile(configPath, defaultConfigFile(generatedPassword), { await fs.writeFile(configPath, defaultConfigFile(generatedPassword), {
flag: "wx", // wx means to fail if the path exists. flag: "wx", // wx means to fail if the path exists.
}) })
logger.info(`Wrote default config file to ${humanPath(os.homedir(), configPath)}`) logger.info(`Wrote default config file to ${configPath}`)
} catch (error: any) { } catch (error: any) {
// EEXIST is fine; we don't want to overwrite existing configurations. // EEXIST is fine; we don't want to overwrite existing configurations.
if (error.code !== "EEXIST") { if (error.code !== "EEXIST") {
@@ -703,6 +732,9 @@ export function bindAddrFromArgs(addr: Addr, args: UserProvidedArgs): Addr {
if (args["bind-addr"]) { if (args["bind-addr"]) {
addr = parseBindAddr(args["bind-addr"]) addr = parseBindAddr(args["bind-addr"])
} }
if (process.env.CODE_SERVER_HOST) {
addr.host = process.env.CODE_SERVER_HOST
}
if (args.host) { if (args.host) {
addr.host = args.host addr.host = args.host
} }
@@ -729,47 +761,38 @@ function bindAddrFromAllSources(...argsConfig: UserProvidedArgs[]): Addr {
return addr return addr
} }
/**
* Reads the socketPath based on path passed in.
*
* The one usually passed in is the DEFAULT_SOCKET_PATH.
*
* If it can't read the path, it throws an error and returns undefined.
*/
export async function readSocketPath(path: string): Promise<string | undefined> {
try {
return await fs.readFile(path, "utf8")
} catch (error) {
// If it doesn't exist, we don't care.
// But if it fails for some reason, we should throw.
// We want to surface that to the user.
if (!isNodeJSErrnoException(error) || error.code !== "ENOENT") {
throw error
}
}
return undefined
}
/** /**
* Determine if it looks like the user is trying to open a file or folder in an * Determine if it looks like the user is trying to open a file or folder in an
* existing instance. The arguments here should be the arguments the user * existing instance. The arguments here should be the arguments the user
* explicitly passed on the command line, *NOT DEFAULTS* or the configuration. * explicitly passed on the command line, *NOT DEFAULTS* or the configuration.
*/ */
export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Promise<string | undefined> => { export const shouldOpenInExistingInstance = async (
args: UserProvidedArgs,
sessionSocket: string,
): Promise<string | undefined> => {
// Always use the existing instance if we're running from VS Code's terminal. // Always use the existing instance if we're running from VS Code's terminal.
if (process.env.VSCODE_IPC_HOOK_CLI) { if (process.env.VSCODE_IPC_HOOK_CLI) {
logger.debug("Found VSCODE_IPC_HOOK_CLI") logger.debug("Found VSCODE_IPC_HOOK_CLI")
return process.env.VSCODE_IPC_HOOK_CLI return process.env.VSCODE_IPC_HOOK_CLI
} }
const paths = getResolvedPathsFromArgs(args)
const client = new EditorSessionManagerClient(sessionSocket)
// If these flags are set then assume the user is trying to open in an // If these flags are set then assume the user is trying to open in an
// existing instance since these flags have no effect otherwise. // existing instance since these flags have no effect otherwise. That means
// if there is no existing instance we should error rather than falling back
// to spawning code-server normally.
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => { const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
return args[cur as keyof UserProvidedArgs] ? prev + 1 : prev return args[cur as keyof UserProvidedArgs] ? prev + 1 : prev
}, 0) }, 0)
if (openInFlagCount > 0) { if (openInFlagCount > 0) {
logger.debug("Found --reuse-window or --new-window") logger.debug("Found --reuse-window or --new-window")
return readSocketPath(DEFAULT_SOCKET_PATH) const socketPath = await client.getConnectedSocketPath(paths[0])
if (!socketPath) {
throw new Error(`No opened code-server instances found to handle ${paths[0]}`)
}
return socketPath
} }
// It's possible the user is trying to spawn another instance of code-server. // It's possible the user is trying to spawn another instance of code-server.
@@ -777,9 +800,13 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
// code-server is invoked exactly like this: `code-server my-file`). // code-server is invoked exactly like this: `code-server my-file`).
// 2. That a file or directory was passed. // 2. That a file or directory was passed.
// 3. That the socket is active. // 3. That the socket is active.
// 4. That an instance exists to handle the path (implied by #3).
if (Object.keys(args).length === 1 && typeof args._ !== "undefined" && args._.length > 0) { if (Object.keys(args).length === 1 && typeof args._ !== "undefined" && args._.length > 0) {
const socketPath = await readSocketPath(DEFAULT_SOCKET_PATH) if (!(await client.canConnect())) {
if (socketPath && (await canConnect(socketPath))) { return undefined
}
const socketPath = await client.getConnectedSocketPath(paths[0])
if (socketPath) {
logger.debug("Found existing code-server socket") logger.debug("Found existing code-server socket")
return socketPath return socketPath
} }

View File

@@ -51,7 +51,7 @@ async function entry(): Promise<void> {
return runCodeCli(args) return runCodeCli(args)
} }
const socketPath = await shouldOpenInExistingInstance(cliArgs) const socketPath = await shouldOpenInExistingInstance(cliArgs, args["session-socket"])
if (socketPath) { if (socketPath) {
logger.debug("Trying to open in existing instance") logger.debug("Trying to open in existing instance")
return openInExistingInstance(args, socketPath) return openInExistingInstance(args, socketPath)

View File

@@ -9,7 +9,10 @@ export class Heart {
private heartbeatInterval = 60000 private heartbeatInterval = 60000
public lastHeartbeat = 0 public lastHeartbeat = 0
public constructor(private readonly heartbeatPath: string, private readonly isActive: () => Promise<boolean>) { public constructor(
private readonly heartbeatPath: string,
private readonly isActive: () => Promise<boolean>,
) {
this.beat = this.beat.bind(this) this.beat = this.beat.bind(this)
this.alive = this.alive.bind(this) this.alive = this.alive.bind(this)
} }

View File

@@ -75,6 +75,25 @@ export const replaceTemplates = <T extends object>(
.replace("{{OPTIONS}}", () => escapeJSON(serverOptions)) .replace("{{OPTIONS}}", () => escapeJSON(serverOptions))
} }
/**
* Throw an error if proxy is not enabled. Call `next` if provided.
*/
export const ensureProxyEnabled = (req: express.Request, _?: express.Response, next?: express.NextFunction): void => {
if (!proxyEnabled(req)) {
throw new HttpError("Forbidden", HttpCode.Forbidden)
}
if (next) {
next()
}
}
/**
* Return true if proxy is enabled.
*/
export const proxyEnabled = (req: express.Request): boolean => {
return !req.args["disable-proxy"]
}
/** /**
* Throw an error if not authorized. Call `next` if provided. * Throw an error if not authorized. Call `next` if provided.
*/ */
@@ -355,6 +374,11 @@ export function authenticateOrigin(req: express.Request): void {
throw new Error(`unable to parse malformed origin "${originRaw}"`) throw new Error(`unable to parse malformed origin "${originRaw}"`)
} }
const trustedOrigins = req.args["trusted-origins"] || []
if (trustedOrigins.includes(origin) || trustedOrigins.includes("*")) {
return
}
const host = getHost(req) const host = getHost(req)
if (typeof host === "undefined") { if (typeof host === "undefined") {
// A missing host likely means the reverse proxy has not been configured to // A missing host likely means the reverse proxy has not been configured to
@@ -373,7 +397,7 @@ export function authenticateOrigin(req: express.Request): void {
/** /**
* Get the host from headers. It will be trimmed and lowercased. * Get the host from headers. It will be trimmed and lowercased.
*/ */
function getHost(req: express.Request): string | undefined { export function getHost(req: express.Request): string | undefined {
// Honor Forwarded if present. // Honor Forwarded if present.
const forwardedRaw = getFirstHeader(req, "forwarded") const forwardedRaw = getFirstHeader(req, "forwarded")
if (forwardedRaw) { if (forwardedRaw) {

View File

@@ -1,7 +1,10 @@
import i18next, { init } from "i18next" import i18next, { init } from "i18next"
import * as en from "./locales/en.json" import * as en from "./locales/en.json"
import * as zhCn from "./locales/zh-cn.json" import * as ja from "./locales/ja.json"
import * as th from "./locales/th.json" import * as th from "./locales/th.json"
import * as ur from "./locales/ur.json"
import * as zhCn from "./locales/zh-cn.json"
init({ init({
lng: "en", lng: "en",
fallbackLng: "en", // language to use if translations in user language are not available. fallbackLng: "en", // language to use if translations in user language are not available.
@@ -18,6 +21,12 @@ init({
th: { th: {
translation: th, translation: th,
}, },
ja: {
translation: ja,
},
ur: {
translation: ur,
},
}, },
}) })

View File

@@ -0,0 +1,13 @@
{
"LOGIN_TITLE": "{{app}} ログイン",
"LOGIN_BELOW": "以下によりログインしてください。",
"WELCOME": "ようこそ {{app}} へ!",
"LOGIN_PASSWORD": "パスワードは設定ファイル( {{configFile}} )を確認してください。",
"LOGIN_USING_ENV_PASSWORD": "パスワードは環境変数 $PASSWORD で設定されています。",
"LOGIN_USING_HASHED_PASSWORD": "パスワードは環境変数 $HASHED_PASSWORD で設定されています。",
"SUBMIT": "実行",
"PASSWORD_PLACEHOLDER": "パスワード",
"LOGIN_RATE_LIMIT": "ログイン制限を超えました!",
"MISS_PASSWORD": "パスワードを入力してください。",
"INCORRECT_PASSWORD": "パスワードが間違っています。"
}

View File

@@ -0,0 +1,13 @@
{
"LOGIN_TITLE": "{{app}} لاگ ان کریں",
"LOGIN_BELOW": "براہ کرم نیچے لاگ ان کریں۔",
"WELCOME": "میں خوش آمدید {{app}}",
"LOGIN_PASSWORD": "پاس ورڈ کے لیے {{configFile}} پر کنفگ فائل چیک کریں۔",
"LOGIN_USING_ENV_PASSWORD": "پاس ورڈ $PASSWORD سے سیٹ کیا گیا تھا۔",
"LOGIN_USING_HASHED_PASSWORD": "پاس ورڈ $HASHED_PASSWORD سے سیٹ کیا گیا تھا۔",
"SUBMIT": "جمع کرائیں",
"PASSWORD_PLACEHOLDER": "پاس ورڈ",
"LOGIN_RATE_LIMIT": "لاگ ان کی شرح محدود!",
"MISS_PASSWORD": "پاس ورڈ غائب ہے۔",
"INCORRECT_PASSWORD": "غلط پاس ورڈ"
}

View File

@@ -1,14 +1,12 @@
import { field, logger } from "@coder/logger" import { field, logger } from "@coder/logger"
import http from "http" import http from "http"
import * as os from "os"
import path from "path"
import { Disposable } from "../common/emitter" import { Disposable } from "../common/emitter"
import { plural } from "../common/util" import { plural } from "../common/util"
import { createApp, ensureAddress } from "./app" import { createApp, ensureAddress } from "./app"
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli" import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
import { commit, version } from "./constants" import { commit, version } from "./constants"
import { register } from "./routes" import { register } from "./routes"
import { humanPath, isDirectory, loadAMDModule, open } from "./util" import { isDirectory, loadAMDModule, open } from "./util"
/** /**
* Return true if the user passed an extension-related VS Code flag. * Return true if the user passed an extension-related VS Code flag.
@@ -70,9 +68,8 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
forceNewWindow: args["new-window"], forceNewWindow: args["new-window"],
gotoLineMode: true, gotoLineMode: true,
} }
const paths = args._ || [] for (let i = 0; i < args._.length; i++) {
for (let i = 0; i < paths.length; i++) { const fp = args._[i]
const fp = path.resolve(paths[i])
if (await isDirectory(fp)) { if (await isDirectory(fp)) {
pipeArgs.folderURIs.push(fp) pipeArgs.folderURIs.push(fp)
} else { } else {
@@ -111,8 +108,8 @@ export const runCodeServer = async (
): Promise<{ dispose: Disposable["dispose"]; server: http.Server }> => { ): Promise<{ dispose: Disposable["dispose"]; server: http.Server }> => {
logger.info(`code-server ${version} ${commit}`) logger.info(`code-server ${version} ${commit}`)
logger.info(`Using user-data-dir ${humanPath(os.homedir(), args["user-data-dir"])}`) logger.info(`Using user-data-dir ${args["user-data-dir"]}`)
logger.trace(`Using extensions-dir ${humanPath(os.homedir(), args["extensions-dir"])}`) logger.trace(`Using extensions-dir ${args["extensions-dir"]}`)
if (args.auth === AuthType.Password && !args.password && !args["hashed-password"]) { if (args.auth === AuthType.Password && !args.password && !args["hashed-password"]) {
throw new Error( throw new Error(
@@ -125,9 +122,8 @@ export const runCodeServer = async (
const serverAddress = ensureAddress(app.server, protocol) const serverAddress = ensureAddress(app.server, protocol)
const disposeRoutes = await register(app, args) const disposeRoutes = await register(app, args)
logger.info(`Using config file ${humanPath(os.homedir(), args.config)}`) logger.info(`Using config file ${args.config}`)
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`) logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
if (args.auth === AuthType.Password) { if (args.auth === AuthType.Password) {
logger.info(" - Authentication is enabled") logger.info(" - Authentication is enabled")
if (args.usingEnvPassword) { if (args.usingEnvPassword) {
@@ -135,21 +131,31 @@ export const runCodeServer = async (
} else if (args.usingEnvHashedPassword) { } else if (args.usingEnvHashedPassword) {
logger.info(" - Using password from $HASHED_PASSWORD") logger.info(" - Using password from $HASHED_PASSWORD")
} else { } else {
logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`) logger.info(` - Using password from ${args.config}`)
} }
} else { } else {
logger.info(" - Authentication is disabled") logger.info(" - Authentication is disabled")
} }
if (args.cert) { if (args.cert) {
logger.info(` - Using certificate for HTTPS: ${humanPath(os.homedir(), args.cert.value)}`) logger.info(` - Using certificate for HTTPS: ${args.cert.value}`)
} else { } else {
logger.info(" - Not serving HTTPS") logger.info(" - Not serving HTTPS")
} }
if (args["proxy-domain"].length > 0) { if (args["disable-proxy"]) {
logger.info(" - Proxy disabled")
} else if (args["proxy-domain"].length > 0) {
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`) logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`)) args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`))
}
if (process.env.VSCODE_PROXY_URI) {
logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`)
}
const sessionServerAddress = app.editorSessionManagerServer.address()
if (sessionServerAddress) {
logger.info(`Session server listening on ${sessionServerAddress.toString()}`)
} }
if (args.enable && args.enable.length > 0) { if (args.enable && args.enable.length > 0) {

View File

@@ -1,34 +1,56 @@
import { Request, Router } from "express" import { Request, Router } from "express"
import { HttpCode, HttpError } from "../../common/http" import { HttpCode, HttpError } from "../../common/http"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http" import { getHost, ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { proxy } from "../proxy" import { proxy } from "../proxy"
import { Router as WsRouter } from "../wsRouter" import { Router as WsRouter } from "../wsRouter"
export const router = Router() export const router = Router()
const proxyDomainToRegex = (matchString: string): RegExp => {
const escapedMatchString = matchString.replace(/[.*+?^$()|[\]\\]/g, "\\$&")
// Replace {{port}} with a regex group to capture the port
// Replace {{host}} with .+ to allow any host match (so rely on DNS record here)
let regexString = escapedMatchString.replace("{{port}}", "(\\d+)")
regexString = regexString.replace("{{host}}", ".+")
regexString = regexString.replace(/[{}]/g, "\\$&") //replace any '{}' that might be left
return new RegExp("^" + regexString + "$")
}
let proxyRegexes: RegExp[] = []
const proxyDomainsToRegex = (proxyDomains: string[]): RegExp[] => {
if (proxyDomains.length !== proxyRegexes.length) {
proxyRegexes = proxyDomains.map(proxyDomainToRegex)
}
return proxyRegexes
}
/** /**
* Return the port if the request should be proxied. Anything that ends in a * Return the port if the request should be proxied.
* proxy domain and has a *single* subdomain should be proxied. Anything else *
* should return `undefined` and will be handled as normal. * The proxy-domain should be of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
* e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}.
* *
* For example if `coder.com` is specified `8080.coder.com` will be proxied
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
*/ */
const maybeProxy = (req: Request): string | undefined => { const maybeProxy = (req: Request): string | undefined => {
// Split into parts. const reqDomain = getHost(req)
const host = req.headers.host || "" if (reqDomain === undefined) {
const idx = host.indexOf(":")
const domain = idx !== -1 ? host.substring(0, idx) : host
const parts = domain.split(".")
// There must be an exact match.
const port = parts.shift()
const proxyDomain = parts.join(".")
if (!port || !req.args["proxy-domain"].includes(proxyDomain)) {
return undefined return undefined
} }
return port const regexs = proxyDomainsToRegex(req.args["proxy-domain"])
for (const regex of regexs) {
const match = reqDomain.match(regex)
if (match) {
return match[1] // match[1] contains the port
}
}
return undefined
} }
router.all("*", async (req, res, next) => { router.all("*", async (req, res, next) => {
@@ -37,6 +59,8 @@ router.all("*", async (req, res, next) => {
return next() return next()
} }
ensureProxyEnabled(req)
// Must be authenticated to use the proxy. // Must be authenticated to use the proxy.
const isAuthenticated = await authenticated(req) const isAuthenticated = await authenticated(req)
if (!isAuthenticated) { if (!isAuthenticated) {
@@ -78,6 +102,8 @@ wsRouter.ws("*", async (req, _, next) => {
if (!port) { if (!port) {
return next() return next()
} }
ensureProxyEnabled(req)
ensureOrigin(req) ensureOrigin(req)
await ensureAuthenticated(req) await ensureAuthenticated(req)
proxy.ws(req, req.ws, req.head, { proxy.ws(req, req.ws, req.head, {

View File

@@ -1,13 +1,12 @@
import { Router, Request } from "express" import { Router, Request } from "express"
import { promises as fs } from "fs" import { promises as fs } from "fs"
import { RateLimiter as Limiter } from "limiter" import { RateLimiter as Limiter } from "limiter"
import * as os from "os"
import * as path from "path" import * as path from "path"
import { CookieKeys } from "../../common/http" import { CookieKeys } from "../../common/http"
import { rootPath } from "../constants" import { rootPath } from "../constants"
import { authenticated, getCookieOptions, redirect, replaceTemplates } from "../http" import { authenticated, getCookieOptions, redirect, replaceTemplates } from "../http"
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
import i18n from "../i18n" import i18n from "../i18n"
import { getPasswordMethod, handlePasswordValidation, sanitizeString, escapeHtml } from "../util"
// RateLimiter wraps around the limiter library for logins. // RateLimiter wraps around the limiter library for logins.
// It allows 2 logins every minute plus 12 logins every hour. // It allows 2 logins every minute plus 12 logins every hour.
@@ -33,7 +32,7 @@ const getRoot = async (req: Request, error?: Error): Promise<string> => {
i18n.changeLanguage(locale) i18n.changeLanguage(locale)
const appName = req.args["app-name"] || "code-server" const appName = req.args["app-name"] || "code-server"
const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string) const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string)
let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: humanPath(os.homedir(), req.args.config) }) let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: req.args.config })
if (req.args.usingEnvPassword) { if (req.args.usingEnvPassword) {
passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD") passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD")
} else if (req.args.usingEnvHashedPassword) { } else if (req.args.usingEnvHashedPassword) {

View File

@@ -3,7 +3,7 @@ import * as path from "path"
import * as qs from "qs" import * as qs from "qs"
import * as pluginapi from "../../../typings/pluginapi" import * as pluginapi from "../../../typings/pluginapi"
import { HttpCode, HttpError } from "../../common/http" import { HttpCode, HttpError } from "../../common/http"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http" import { ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { proxy as _proxy } from "../proxy" import { proxy as _proxy } from "../proxy"
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => { const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
@@ -21,6 +21,8 @@ export async function proxy(
passthroughPath?: boolean passthroughPath?: boolean
}, },
): Promise<void> { ): Promise<void> {
ensureProxyEnabled(req)
if (!(await authenticated(req))) { if (!(await authenticated(req))) {
// If visiting the root (/:port only) redirect to the login page. // If visiting the root (/:port only) redirect to the login page.
if (!req.params[0] || req.params[0] === "/") { if (!req.params[0] || req.params[0] === "/") {
@@ -50,6 +52,7 @@ export async function wsProxy(
passthroughPath?: boolean passthroughPath?: boolean
}, },
): Promise<void> { ): Promise<void> {
ensureProxyEnabled(req)
ensureOrigin(req) ensureOrigin(req)
await ensureAuthenticated(req) await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, { _proxy.ws(req, req.ws, req.head, {

View File

@@ -1,7 +1,7 @@
import { field, logger } from "@coder/logger" import { field, logger } from "@coder/logger"
import * as http from "http" import * as http from "http"
import * as https from "https" import * as https from "https"
import ProxyAgent from "proxy-agent" import { ProxyAgent } from "proxy-agent"
import * as semver from "semver" import * as semver from "semver"
import * as url from "url" import * as url from "url"
import { httpProxyUri, version } from "./constants" import { httpProxyUri, version } from "./constants"
@@ -104,7 +104,9 @@ export class UpdateProvider {
const request = (uri: string): void => { const request = (uri: string): void => {
logger.debug("Making request", field("uri", uri)) logger.debug("Making request", field("uri", uri))
const isHttps = uri.startsWith("https") const isHttps = uri.startsWith("https")
const agent = httpProxyUri ? new ProxyAgent(httpProxyUri) : undefined const agent = new ProxyAgent({
getProxyForUrl: () => httpProxyUri || "",
})
const httpx = isHttps ? https : http const httpx = isHttps ? https : http
const client = httpx.get(uri, { headers: { "User-Agent": "code-server" }, agent }, (response) => { const client = httpx.get(uri, { headers: { "User-Agent": "code-server" }, agent }, (response) => {
if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 400) { if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 400) {

View File

@@ -87,20 +87,6 @@ export function getEnvPaths(platform = process.platform): Paths {
} }
} }
/**
* humanPath replaces the home directory in path with ~.
* Makes it more readable.
*
* @param homedir - the home directory(i.e. `os.homedir()`)
* @param path - a file path
*/
export function humanPath(homedir: string, path?: string): string {
if (!path) {
return ""
}
return path.replace(homedir, "~")
}
export const generateCertificate = async (hostname: string): Promise<{ cert: string; certKey: string }> => { export const generateCertificate = async (hostname: string): Promise<{ cert: string; certKey: string }> => {
const certPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.crt`) const certPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.crt`)
const certKeyPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.key`) const certKeyPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.key`)

206
src/node/vscodeSocket.ts Normal file
View File

@@ -0,0 +1,206 @@
import { logger } from "@coder/logger"
import express from "express"
import * as http from "http"
import * as path from "path"
import { HttpCode } from "../common/http"
import { listen } from "./app"
import { canConnect } from "./util"
export interface EditorSessionEntry {
workspace: {
id: string
folders: {
uri: {
path: string
}
}[]
}
socketPath: string
}
interface DeleteSessionRequest {
socketPath: string
}
interface AddSessionRequest {
entry: EditorSessionEntry
}
interface GetSessionResponse {
socketPath?: string
}
export async function makeEditorSessionManagerServer(
codeServerSocketPath: string,
editorSessionManager: EditorSessionManager,
): Promise<http.Server> {
const router = express()
// eslint-disable-next-line import/no-named-as-default-member
router.use(express.json())
router.get("/session", async (req, res) => {
const filePath = req.query.filePath as string
if (!filePath) {
res.status(HttpCode.BadRequest).send("filePath is required")
return
}
try {
const socketPath = await editorSessionManager.getConnectedSocketPath(filePath)
const response: GetSessionResponse = { socketPath }
res.json(response)
} catch (error: unknown) {
res.status(HttpCode.ServerError).send(error)
}
})
router.post("/add-session", async (req, res) => {
const request = req.body as AddSessionRequest
if (!request.entry) {
res.status(400).send("entry is required")
}
editorSessionManager.addSession(request.entry)
res.status(200).send()
})
router.post("/delete-session", async (req, res) => {
const request = req.body as DeleteSessionRequest
if (!request.socketPath) {
res.status(400).send("socketPath is required")
}
editorSessionManager.deleteSession(request.socketPath)
res.status(200).send()
})
const server = http.createServer(router)
try {
await listen(server, { socket: codeServerSocketPath })
} catch (e) {
logger.warn(`Could not create socket at ${codeServerSocketPath}`)
}
return server
}
export class EditorSessionManager {
// Map from socket path to EditorSessionEntry.
private entries = new Map<string, EditorSessionEntry>()
addSession(entry: EditorSessionEntry): void {
logger.debug(`Adding session to session registry: ${entry.socketPath}`)
this.entries.set(entry.socketPath, entry)
}
getCandidatesForFile(filePath: string): EditorSessionEntry[] {
const matchCheckResults = new Map<string, boolean>()
const checkMatch = (entry: EditorSessionEntry): boolean => {
if (matchCheckResults.has(entry.socketPath)) {
return matchCheckResults.get(entry.socketPath)!
}
const result = entry.workspace.folders.some((folder) => filePath.startsWith(folder.uri.path + path.sep))
matchCheckResults.set(entry.socketPath, result)
return result
}
return Array.from(this.entries.values())
.reverse() // Most recently registered first.
.sort((a, b) => {
// Matches first.
const aMatch = checkMatch(a)
const bMatch = checkMatch(b)
if (aMatch === bMatch) {
return 0
}
if (aMatch) {
return -1
}
return 1
})
}
deleteSession(socketPath: string): void {
logger.debug(`Deleting session from session registry: ${socketPath}`)
this.entries.delete(socketPath)
}
/**
* Returns the best socket path that we can connect to.
* We also delete any sockets that we can't connect to.
*/
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
const candidates = this.getCandidatesForFile(filePath)
let match: EditorSessionEntry | undefined = undefined
for (const candidate of candidates) {
if (await canConnect(candidate.socketPath)) {
match = candidate
break
}
this.deleteSession(candidate.socketPath)
}
return match?.socketPath
}
}
export class EditorSessionManagerClient {
constructor(private codeServerSocketPath: string) {}
async canConnect() {
return canConnect(this.codeServerSocketPath)
}
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
const response = await new Promise<GetSessionResponse>((resolve, reject) => {
const opts = {
path: "/session?filePath=" + encodeURIComponent(filePath),
socketPath: this.codeServerSocketPath,
method: "GET",
}
const req = http.request(opts, (res) => {
let rawData = ""
res.setEncoding("utf8")
res.on("data", (chunk) => {
rawData += chunk
})
res.on("end", () => {
try {
const obj = JSON.parse(rawData)
if (res.statusCode === 200) {
resolve(obj)
} else {
reject(new Error("Unexpected status code: " + res.statusCode))
}
} catch (e: unknown) {
reject(e)
}
})
})
req.on("error", reject)
req.end()
})
return response.socketPath
}
// Currently only used for tests.
async addSession(request: AddSessionRequest): Promise<void> {
await new Promise<void>((resolve, reject) => {
const opts = {
path: "/add-session",
socketPath: this.codeServerSocketPath,
method: "POST",
headers: {
"content-type": "application/json",
accept: "application/json",
},
}
const req = http.request(opts, () => {
resolve()
})
req.on("error", reject)
req.write(JSON.stringify(request))
req.end()
})
}
}

View File

@@ -78,7 +78,10 @@ type ChildMessage = RelaunchMessage | ChildHandshakeMessage
type ParentMessage = ParentHandshakeMessage type ParentMessage = ParentHandshakeMessage
class ProcessError extends Error { class ProcessError extends Error {
public constructor(message: string, public readonly code: number | undefined) { public constructor(
message: string,
public readonly code: number | undefined,
) {
super(message) super(message)
this.name = this.constructor.name this.name = this.constructor.name
Error.captureStackTrace(this, this.constructor) Error.captureStackTrace(this, this.constructor)

View File

@@ -117,40 +117,26 @@ export class CodeServer {
* directories. * directories.
*/ */
private async spawn(): Promise<CodeServerProcess> { private async spawn(): Promise<CodeServerProcess> {
// This will be used both as the workspace and data directory to ensure
// instances don't bleed into each other.
const dir = await this.createWorkspace() const dir = await this.createWorkspace()
const args = await this.argsWithDefaults([
"--auth",
"none",
// The workspace to open.
...(this.args.includes("--ignore-last-opened") ? [] : [dir]),
...this.args,
// Using port zero will spawn on a random port.
"--bind-addr",
"127.0.0.1:0",
])
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const args = [
this.entry,
"--extensions-dir",
path.join(dir, "extensions"),
"--auth",
"none",
// The workspace to open.
...(this.args.includes("--ignore-last-opened") ? [] : [dir]),
...this.args,
// Using port zero will spawn on a random port.
"--bind-addr",
"127.0.0.1:0",
// Setting the XDG variables would be easier and more thorough but the
// modules we import ignores those variables for non-Linux operating
// systems so use these flags instead.
"--config",
path.join(dir, "config.yaml"),
"--user-data-dir",
dir,
]
this.logger.debug("spawning `node " + args.join(" ") + "`") this.logger.debug("spawning `node " + args.join(" ") + "`")
const proc = cp.spawn("node", args, { const proc = cp.spawn("node", args, {
cwd: path.join(__dirname, "../../.."), cwd: path.join(__dirname, "../../.."),
env: { env: {
...process.env, ...process.env,
...this.env, ...this.env,
// Set to empty string to prevent code-server from // Prevent code-server from using the existing instance when running
// using the existing instance when running the e2e tests // the e2e tests from an integrated terminal.
// from an integrated terminal.
VSCODE_IPC_HOOK_CLI: "", VSCODE_IPC_HOOK_CLI: "",
PASSWORD, PASSWORD,
}, },
@@ -173,11 +159,15 @@ export class CodeServer {
reject(error) reject(error)
}) })
// Tracks when the HTTP and session servers are ready.
let httpAddress: string | undefined
let sessionAddress: string | undefined
let resolved = false let resolved = false
proc.stdout.setEncoding("utf8") proc.stdout.setEncoding("utf8")
onLine(proc, (line) => { onLine(proc, (line) => {
// As long as we are actively getting input reset the timer. If we stop // As long as we are actively getting input reset the timer. If we stop
// getting input and still have not found the address the timer will // getting input and still have not found the addresses the timer will
// reject. // reject.
timer.reset() timer.reset()
@@ -186,20 +176,69 @@ export class CodeServer {
if (resolved) { if (resolved) {
return return
} }
const match = line.trim().match(/HTTPS? server listening on (https?:\/\/[.:\d]+)\/?$/)
let match = line.trim().match(/HTTPS? server listening on (https?:\/\/[.:\d]+)\/?$/)
if (match) { if (match) {
// Cookies don't seem to work on IP address so swap to localhost. // Cookies don't seem to work on IP addresses so swap to localhost.
// TODO: Investigate whether this is a bug with code-server. // TODO: Investigate whether this is a bug with code-server.
const address = match[1].replace("127.0.0.1", "localhost") httpAddress = match[1].replace("127.0.0.1", "localhost")
this.logger.debug(`spawned on ${address}`) }
match = line.trim().match(/Session server listening on (.+)$/)
if (match) {
sessionAddress = match[1]
}
if (typeof httpAddress !== "undefined" && typeof sessionAddress !== "undefined") {
resolved = true resolved = true
timer.dispose() timer.dispose()
resolve({ process: proc, address }) this.logger.debug(`code-server is ready: ${httpAddress} ${sessionAddress}`)
resolve({ process: proc, address: httpAddress })
} }
}) })
}) })
} }
/**
* Execute a short-lived command.
*/
async run(args: string[]): Promise<void> {
args = await this.argsWithDefaults(args)
this.logger.debug("executing `node " + args.join(" ") + "`")
await util.promisify(cp.exec)("node " + args.join(" "), {
cwd: path.join(__dirname, "../../.."),
env: {
...process.env,
...this.env,
// Prevent code-server from using the existing instance when running
// the e2e tests from an integrated terminal.
VSCODE_IPC_HOOK_CLI: "",
},
})
}
/**
* Combine arguments with defaults.
*/
private async argsWithDefaults(args: string[]): Promise<string[]> {
// This will be used both as the workspace and data directory to ensure
// instances don't bleed into each other.
const dir = await this.workspaceDir
return [
this.entry,
"--extensions-dir",
path.join(dir, "extensions"),
...args,
// Setting the XDG variables would be easier and more thorough but the
// modules we import ignores those variables for non-Linux operating
// systems so use these flags instead.
"--config",
path.join(dir, "config.yaml"),
"--user-data-dir",
dir,
]
}
/** /**
* Close the code-server process. * Close the code-server process.
*/ */
@@ -230,7 +269,10 @@ export class CodeServer {
export class CodeServerPage { export class CodeServerPage {
private readonly editorSelector = "div.monaco-workbench" private readonly editorSelector = "div.monaco-workbench"
constructor(private readonly codeServer: CodeServer, public readonly page: Page) { constructor(
private readonly codeServer: CodeServer,
public readonly page: Page,
) {
this.page.on("console", (message) => { this.page.on("console", (message) => {
this.codeServer.logger.debug(message.text()) this.codeServer.logger.debug(message.text())
}) })
@@ -364,6 +406,13 @@ export class CodeServerPage {
await this.waitForTab(file) await this.waitForTab(file)
} }
/**
* Open a file through an external command.
*/
async openFileExternally(file: string) {
await this.codeServer.run(["--reuse-window", file])
}
/** /**
* Wait for a tab to open for the specified file. * Wait for a tab to open for the specified file.
*/ */

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from "./baseFixture"
import { clean, getMaybeProxiedPathname } from "../utils/helpers" import { clean, getMaybeProxiedPathname } from "../utils/helpers"
import { describe, test, expect } from "./baseFixture"
const routes = ["/", "/vscode", "/vscode/"] const routes = ["/", "/vscode", "/vscode/"]
@@ -103,7 +103,7 @@ describe("VS Code Routes with no workspace or folder", ["--disable-workspace-tru
// Closing the folder should stop the redirecting. // Closing the folder should stop the redirecting.
await codeServerPage.navigate("/?ew=true") await codeServerPage.navigate("/?ew=true")
let url = new URL(codeServerPage.page.url()) const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url) const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/") expect(pathname).toBe("/")
expect(url.search).toBe("?ew=true") expect(url.search).toBe("?ew=true")

View File

@@ -35,13 +35,19 @@ describe("Integrated Terminal", ["--disable-workspace-trust"], {}, () => {
const tmpFolderPath = await tmpdir(testName) const tmpFolderPath = await tmpdir(testName)
const tmpFile = path.join(tmpFolderPath, "test-file") const tmpFile = path.join(tmpFolderPath, "test-file")
await fs.writeFile(tmpFile, "test") await fs.writeFile(tmpFile, "test")
const fileName = path.basename(tmpFile)
await codeServerPage.focusTerminal() await codeServerPage.focusTerminal()
await codeServerPage.page.keyboard.type(`code-server ${tmpFile}`) await codeServerPage.page.keyboard.type(`code-server ${tmpFile}`)
await codeServerPage.page.keyboard.press("Enter") await codeServerPage.page.keyboard.press("Enter")
await codeServerPage.waitForTab(fileName) await codeServerPage.waitForTab(path.basename(tmpFile))
const externalTmpFile = path.join(tmpFolderPath, "test-external-file")
await fs.writeFile(externalTmpFile, "foobar")
await codeServerPage.openFileExternally(externalTmpFile)
await codeServerPage.waitForTab(path.basename(externalTmpFile))
}) })
}) })

View File

@@ -17,7 +17,7 @@ describe("createApp", () => {
beforeAll(async () => { beforeAll(async () => {
mockLogger() mockLogger()
const testName = "unlink-socket" const testName = "app"
await clean(testName) await clean(testName)
tmpDirPath = await tmpdir(testName) tmpDirPath = await tmpdir(testName)
tmpFilePath = path.join(tmpDirPath, "unlink-socket-file") tmpFilePath = path.join(tmpDirPath, "unlink-socket-file")
@@ -92,7 +92,7 @@ describe("createApp", () => {
app.dispose() app.dispose()
} }
expect(() => masterBall()).rejects.toThrow(`listen EACCES: permission denied 127.0.0.1:${port}`) expect(() => masterBall()).rejects.toThrow("listen EACCES: permission denied")
}) })
it("should unlink a socket before listening on the socket", async () => { it("should unlink a socket before listening on the socket", async () => {
@@ -103,7 +103,7 @@ describe("createApp", () => {
const app = await createApp(defaultArgs) const app = await createApp(defaultArgs)
expect(unlinkSpy).toHaveBeenCalledTimes(1) expect(unlinkSpy).toHaveBeenCalledWith(tmpFilePath)
app.dispose() app.dispose()
}) })

View File

@@ -1,14 +1,11 @@
import { Level, logger } from "@coder/logger" import { Level, logger } from "@coder/logger"
import { promises as fs } from "fs" import { promises as fs } from "fs"
import * as net from "net"
import * as os from "os"
import * as path from "path" import * as path from "path"
import { import {
UserProvidedArgs, UserProvidedArgs,
bindAddrFromArgs, bindAddrFromArgs,
defaultConfigFile, defaultConfigFile,
parse, parse,
readSocketPath,
setDefaults, setDefaults,
shouldOpenInExistingInstance, shouldOpenInExistingInstance,
toCodeArgs, toCodeArgs,
@@ -20,7 +17,12 @@ import {
} from "../../../src/node/cli" } from "../../../src/node/cli"
import { shouldSpawnCliProcess } from "../../../src/node/main" import { shouldSpawnCliProcess } from "../../../src/node/main"
import { generatePassword, paths } from "../../../src/node/util" import { generatePassword, paths } from "../../../src/node/util"
import { clean, useEnv, tmpdir } from "../../utils/helpers" import {
EditorSessionManager,
EditorSessionManagerClient,
makeEditorSessionManagerServer,
} from "../../../src/node/vscodeSocket"
import { clean, useEnv, tmpdir, listenOn } from "../../utils/helpers"
// The parser should not set any defaults so the caller can determine what // The parser should not set any defaults so the caller can determine what
// values the user actually set. These are only set after explicitly calling // values the user actually set. These are only set after explicitly calling
@@ -34,6 +36,7 @@ const defaults = {
usingEnvHashedPassword: false, usingEnvHashedPassword: false,
"extensions-dir": path.join(paths.data, "extensions"), "extensions-dir": path.join(paths.data, "extensions"),
"user-data-dir": paths.data, "user-data-dir": paths.data,
"session-socket": path.join(paths.data, "code-server-ipc.sock"),
_: [], _: [],
} }
@@ -44,6 +47,7 @@ describe("parser", () => {
delete process.env.CS_DISABLE_FILE_DOWNLOADS delete process.env.CS_DISABLE_FILE_DOWNLOADS
delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
delete process.env.VSCODE_PROXY_URI delete process.env.VSCODE_PROXY_URI
delete process.env.CS_DISABLE_PROXY
console.log = jest.fn() console.log = jest.fn()
}) })
@@ -100,6 +104,10 @@ describe("parser", () => {
"--disable-getting-started-override", "--disable-getting-started-override",
"--disable-proxy",
["--session-socket", "/tmp/override-code-server-ipc-socket"],
["--host", "0.0.0.0"], ["--host", "0.0.0.0"],
"4", "4",
"--", "--",
@@ -118,6 +126,7 @@ describe("parser", () => {
}, },
"disable-file-downloads": true, "disable-file-downloads": true,
"disable-getting-started-override": true, "disable-getting-started-override": true,
"disable-proxy": true,
enable: ["feature1", "feature2"], enable: ["feature1", "feature2"],
help: true, help: true,
host: "0.0.0.0", host: "0.0.0.0",
@@ -133,6 +142,7 @@ describe("parser", () => {
"welcome-text": "welcome to code", "welcome-text": "welcome to code",
version: true, version: true,
"bind-addr": "192.169.0.1:8080", "bind-addr": "192.169.0.1:8080",
"session-socket": "/tmp/override-code-server-ipc-socket",
}) })
}) })
@@ -386,6 +396,30 @@ describe("parser", () => {
}) })
}) })
it("should use env var CS_DISABLE_PROXY", async () => {
process.env.CS_DISABLE_PROXY = "1"
const args = parse([])
expect(args).toEqual({})
const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
"disable-proxy": true,
})
})
it("should use env var CS_DISABLE_PROXY set to true", async () => {
process.env.CS_DISABLE_PROXY = "true"
const args = parse([])
expect(args).toEqual({})
const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
"disable-proxy": true,
})
})
it("should error if password passed in", () => { it("should error if password passed in", () => {
expect(() => parse(["--password", "supersecret123"])).toThrowError( expect(() => parse(["--password", "supersecret123"])).toThrowError(
"--password can only be set in the config file or passed in via $PASSWORD", "--password can only be set in the config file or passed in via $PASSWORD",
@@ -413,7 +447,7 @@ describe("parser", () => {
const defaultArgs = await setDefaults(args) const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({ expect(defaultArgs).toEqual({
...defaults, ...defaults,
"proxy-domain": ["coder.com", "coder.org"], "proxy-domain": ["{{port}}.coder.com", "{{port}}.coder.org"],
}) })
}) })
it("should allow '=,$/' in strings", async () => { it("should allow '=,$/' in strings", async () => {
@@ -466,14 +500,14 @@ describe("parser", () => {
it("should set proxy uri", async () => { it("should set proxy uri", async () => {
await setDefaults(parse(["--proxy-domain", "coder.org"])) await setDefaults(parse(["--proxy-domain", "coder.org"]))
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.org") expect(process.env.VSCODE_PROXY_URI).toEqual("//{{port}}.coder.org")
}) })
it("should set proxy uri to first domain", async () => { it("should set proxy uri to first domain", async () => {
await setDefaults( await setDefaults(
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]), parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
) )
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.com") expect(process.env.VSCODE_PROXY_URI).toEqual("//{{port}}.coder.com")
}) })
it("should not override existing proxy uri", async () => { it("should not override existing proxy uri", async () => {
@@ -487,7 +521,7 @@ describe("parser", () => {
describe("cli", () => { describe("cli", () => {
const testName = "cli" const testName = "cli"
const vscodeIpcPath = path.join(os.tmpdir(), "vscode-ipc") let tmpDirPath: string
beforeAll(async () => { beforeAll(async () => {
await clean(testName) await clean(testName)
@@ -495,68 +529,170 @@ describe("cli", () => {
beforeEach(async () => { beforeEach(async () => {
delete process.env.VSCODE_IPC_HOOK_CLI delete process.env.VSCODE_IPC_HOOK_CLI
await fs.rm(vscodeIpcPath, { force: true, recursive: true }) tmpDirPath = await tmpdir(testName)
}) })
it("should use existing if inside code-server", async () => { it("should use existing if inside code-server", async () => {
process.env.VSCODE_IPC_HOOK_CLI = "test" process.env.VSCODE_IPC_HOOK_CLI = "test"
const args: UserProvidedArgs = {} const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test") expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")
args.port = 8081 args.port = 8081
args._ = ["./file"] args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test") expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")
}) })
it("should use existing if --reuse-window is set", async () => { it("should use existing if --reuse-window is set", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {} const args: UserProvidedArgs = {}
args["reuse-window"] = true args["reuse-window"] = true
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(undefined) await expect(shouldOpenInExistingInstance(args, sessionSocket)).rejects.toThrow()
await fs.writeFile(vscodeIpcPath, "test") const socketPath = path.join(tmpDirPath, "socket")
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual("test") const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath,
},
})
const vscodeSockets = listenOn(socketPath)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)
args.port = 8081 args.port = 8081
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual("test") await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)
server.close()
vscodeSockets.close()
}) })
it("should use existing if --new-window is set", async () => { it("should use existing if --new-window is set", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {} const args: UserProvidedArgs = {}
args["new-window"] = true args["new-window"] = true
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) await expect(shouldOpenInExistingInstance(args, sessionSocket)).rejects.toThrow()
await fs.writeFile(vscodeIpcPath, "test") const socketPath = path.join(tmpDirPath, "socket")
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test") const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath,
},
})
const vscodeSockets = listenOn(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
args.port = 8081 args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test") expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
server.close()
vscodeSockets.close()
}) })
it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => { it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {} const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
args._ = ["./file"] args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
const testDir = await tmpdir(testName) const client = new EditorSessionManagerClient(sessionSocket)
const socketPath = path.join(testDir, "socket") const socketPath = path.join(tmpDirPath, "socket")
await fs.writeFile(vscodeIpcPath, socketPath) await client.addSession({
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) entry: {
workspace: {
await new Promise((resolve) => { id: "aaa",
const server = net.createServer(() => { folders: [
// Close after getting the first connection. {
server.close() uri: {
}) path: "/aaa",
server.once("listening", () => resolve(server)) },
server.listen(socketPath) },
],
},
socketPath,
},
}) })
const vscodeSockets = listenOn(socketPath)
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(socketPath) expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
args.port = 8081 args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
server.close()
vscodeSockets.close()
})
it("should prefer matching sessions for only the first path", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
},
})
await client.addSession({
entry: {
workspace: {
id: "bbb",
folders: [
{
uri: {
path: "/bbb",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
},
})
listenOn(`${tmpDirPath}/vscode-ipc-aaa.sock`, `${tmpDirPath}/vscode-ipc-bbb.sock`)
const args: UserProvidedArgs = {}
args._ = ["/aaa/file", "/bbb/file"]
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(`${tmpDirPath}/vscode-ipc-aaa.sock`)
server.close()
}) })
}) })
@@ -653,6 +789,50 @@ describe("bindAddrFromArgs", () => {
expect(actual).toStrictEqual(expected) expect(actual).toStrictEqual(expected)
}) })
it("should use process.env.CODE_SERVER_HOST if set", () => {
const [setValue, resetValue] = useEnv("CODE_SERVER_HOST")
setValue("coder")
const args: UserProvidedArgs = {}
const addr = {
host: "localhost",
port: 8080,
}
const actual = bindAddrFromArgs(addr, args)
const expected = {
host: "coder",
port: 8080,
}
expect(actual).toStrictEqual(expected)
resetValue()
})
it("should use the args.host over process.env.CODE_SERVER_HOST if both set", () => {
const [setValue, resetValue] = useEnv("CODE_SERVER_HOST")
setValue("coder")
const args: UserProvidedArgs = {
host: "123.123.123.123",
}
const addr = {
host: "localhost",
port: 8080,
}
const actual = bindAddrFromArgs(addr, args)
const expected = {
host: "123.123.123.123",
port: 8080,
}
expect(actual).toStrictEqual(expected)
resetValue()
})
it("should use process.env.PORT if set", () => { it("should use process.env.PORT if set", () => {
const [setValue, resetValue] = useEnv("PORT") const [setValue, resetValue] = useEnv("PORT")
setValue("8000") setValue("8000")
@@ -729,44 +909,6 @@ cert: false`)
}) })
}) })
describe("readSocketPath", () => {
const fileContents = "readSocketPath file contents"
let tmpDirPath: string
let tmpFilePath: string
const testName = "readSocketPath"
beforeAll(async () => {
await clean(testName)
})
beforeEach(async () => {
tmpDirPath = await tmpdir(testName)
tmpFilePath = path.join(tmpDirPath, "readSocketPath.txt")
await fs.writeFile(tmpFilePath, fileContents)
})
it("should throw an error if it can't read the file", async () => {
// TODO@jsjoeio - implement
// Test it on a directory.... ESDIR
// TODO@jsjoeio - implement
expect(() => readSocketPath(tmpDirPath)).rejects.toThrow("EISDIR")
})
it("should return undefined if it can't read the file", async () => {
// TODO@jsjoeio - implement
const socketPath = await readSocketPath(path.join(tmpDirPath, "not-a-file"))
expect(socketPath).toBeUndefined()
})
it("should return the file contents", async () => {
const contents = await readSocketPath(tmpFilePath)
expect(contents).toBe(fileContents)
})
it("should return the same file contents for two different calls", async () => {
const contents1 = await readSocketPath(tmpFilePath)
const contents2 = await readSocketPath(tmpFilePath)
expect(contents2).toBe(contents1)
})
})
describe("toCodeArgs", () => { describe("toCodeArgs", () => {
const vscodeDefaults = { const vscodeDefaults = {
...defaults, ...defaults,

View File

@@ -70,6 +70,7 @@ describe("http", () => {
origin: test.origin, origin: test.origin,
[key]: value, [key]: value,
}, },
args: {},
}) })
if (typeof test.expected === "string") { if (typeof test.expected === "string") {
expect(() => http.authenticateOrigin(req)).toThrow(test.expected) expect(() => http.authenticateOrigin(req)).toThrow(test.expected)

View File

@@ -43,6 +43,7 @@ describe("plugin", () => {
usingEnvHashedPassword: false, usingEnvHashedPassword: false,
"extensions-dir": "", "extensions-dir": "",
"user-data-dir": "", "user-data-dir": "",
"session-socket": "",
} }
next() next()
} }

View File

@@ -1,4 +1,3 @@
import * as bodyParser from "body-parser"
import * as express from "express" import * as express from "express"
import * as http from "http" import * as http from "http"
import nodeFetch from "node-fetch" import nodeFetch from "node-fetch"
@@ -45,6 +44,17 @@ describe("proxy", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
it("should return 403 Forbidden if proxy is disabled", async () => {
e.get("/wsup", (req, res) => {
res.json("you cannot see this")
})
codeServer = await integration.setup(["--auth=none", "--disable-proxy"], "")
const resp = await codeServer.fetch(proxyPath)
expect(resp.status).toBe(403)
const json = await resp.json()
expect(json).toEqual({ error: "Forbidden" })
})
it("should rewrite the base path", async () => { it("should rewrite the base path", async () => {
e.get("/wsup", (req, res) => { e.get("/wsup", (req, res) => {
res.json("asher is the best") res.json("asher is the best")
@@ -99,7 +109,7 @@ describe("proxy", () => {
}) })
it("should allow post bodies", async () => { it("should allow post bodies", async () => {
e.use(bodyParser.json({ strict: false })) e.use(express.json({ strict: false }))
e.post("/wsup", (req, res) => { e.post("/wsup", (req, res) => {
res.json(req.body) res.json(req.body)
}) })
@@ -116,7 +126,7 @@ describe("proxy", () => {
}) })
it("should handle bad requests", async () => { it("should handle bad requests", async () => {
e.use(bodyParser.json({ strict: false })) e.use(express.json({ strict: false }))
e.post("/wsup", (req, res) => { e.post("/wsup", (req, res) => {
res.json(req.body) res.json(req.body)
}) })
@@ -143,7 +153,7 @@ describe("proxy", () => {
}) })
it("should handle errors", async () => { it("should handle errors", async () => {
e.use(bodyParser.json({ strict: false })) e.use(express.json({ strict: false }))
e.post("/wsup", (req, res) => { e.post("/wsup", (req, res) => {
throw new Error("BROKEN") throw new Error("BROKEN")
}) })

View File

@@ -1,6 +1,6 @@
import { mockLogger } from "../../../utils/helpers"
import * as httpserver from "../../../utils/httpserver" import * as httpserver from "../../../utils/httpserver"
import * as integration from "../../../utils/integration" import * as integration from "../../../utils/integration"
import { mockLogger } from "../../../utils/helpers"
describe("vscode", () => { describe("vscode", () => {
let codeServer: httpserver.HttpServer | undefined let codeServer: httpserver.HttpServer | undefined

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@@ -1,10 +1,10 @@
import { logger } from "@coder/logger" import { logger } from "@coder/logger"
import * as http from "http" import * as http from "http"
import { AddressInfo } from "net"
import * as path from "path" import * as path from "path"
import { ensureAddress } from "../../../src/node/app"
import { SettingsProvider, UpdateSettings } from "../../../src/node/settings" import { SettingsProvider, UpdateSettings } from "../../../src/node/settings"
import { LatestResponse, UpdateProvider } from "../../../src/node/update" import { LatestResponse, UpdateProvider } from "../../../src/node/update"
import { clean, isAddressInfo, mockLogger, tmpdir } from "../../utils/helpers" import { clean, mockLogger, tmpdir } from "../../utils/helpers"
describe("update", () => { describe("update", () => {
let version = "1.0.0" let version = "1.0.0"
@@ -79,7 +79,6 @@ describe("update", () => {
} }
let _provider: UpdateProvider | undefined let _provider: UpdateProvider | undefined
let _address: string | AddressInfo | null
const provider = (): UpdateProvider => { const provider = (): UpdateProvider => {
if (!_provider) { if (!_provider) {
throw new Error("Update provider has not been created") throw new Error("Update provider has not been created")
@@ -87,6 +86,7 @@ describe("update", () => {
return _provider return _provider
} }
let address = new URL("http://localhost")
beforeAll(async () => { beforeAll(async () => {
mockLogger() mockLogger()
@@ -105,12 +105,13 @@ describe("update", () => {
}) })
}) })
_address = server.address() const addr = ensureAddress(server, "http")
if (!isAddressInfo(_address)) { if (typeof addr === "string") {
throw new Error("unexpected address") throw new Error("unable to run update tests with unix sockets")
} }
address = addr
_provider = new UpdateProvider(`http://${_address?.address}:${_address?.port}/latest`, _settings) address.pathname = "/latest"
_provider = new UpdateProvider(address.toString(), _settings)
}) })
afterAll(() => { afterAll(() => {
@@ -220,59 +221,51 @@ describe("update", () => {
}) })
it("should reject if response has status code 500", async () => { it("should reject if response has status code 500", async () => {
if (isAddressInfo(_address)) { address.pathname = "/reject-status-code"
const mockURL = `http://${_address.address}:${_address.port}/reject-status-code` const provider = new UpdateProvider(address.toString(), settings())
const provider = new UpdateProvider(mockURL, settings()) const update = await provider.getUpdate(true)
const update = await provider.getUpdate(true)
expect(update.version).toBe("unknown") expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled() expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", { expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error", identifier: "error",
value: `${mockURL}: 500`, value: `${address.toString()}: 500`,
}) })
}
}) })
it("should reject if no location header provided", async () => { it("should reject if no location header provided", async () => {
if (isAddressInfo(_address)) { address.pathname = "/no-location-header"
const mockURL = `http://${_address.address}:${_address.port}/no-location-header` const provider = new UpdateProvider(address.toString(), settings())
const provider = new UpdateProvider(mockURL, settings()) const update = await provider.getUpdate(true)
const update = await provider.getUpdate(true)
expect(update.version).toBe("unknown") expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled() expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", { expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error", identifier: "error",
value: `received redirect with no location header`, value: `received redirect with no location header`,
}) })
}
}) })
it("should resolve the request with response.headers.location", async () => { it("should resolve the request with response.headers.location", async () => {
version = "4.1.1" version = "4.1.1"
if (isAddressInfo(_address)) { address.pathname = "/with-location-header"
const mockURL = `http://${_address.address}:${_address.port}/with-location-header` const provider = new UpdateProvider(address.toString(), settings())
const provider = new UpdateProvider(mockURL, settings()) const update = await provider.getUpdate(true)
const update = await provider.getUpdate(true)
expect(logger.error).not.toHaveBeenCalled() expect(logger.error).not.toHaveBeenCalled()
expect(update.version).toBe("4.1.1") expect(update.version).toBe("4.1.1")
}
}) })
it("should reject if more than 10 redirects", async () => { it("should reject if more than 10 redirects", async () => {
if (isAddressInfo(_address)) { address.pathname = "/redirect/11"
const mockURL = `http://${_address.address}:${_address.port}/redirect/11` const provider = new UpdateProvider(address.toString(), settings())
const provider = new UpdateProvider(mockURL, settings()) const update = await provider.getUpdate(true)
const update = await provider.getUpdate(true)
expect(update.version).toBe("unknown") expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled() expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", { expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error", identifier: "error",
value: `reached max redirects`, value: `reached max redirects`,
}) })
}
}) })
}) })

View File

@@ -491,22 +491,6 @@ describe("isDirectory", () => {
}) })
}) })
describe("humanPath", () => {
it("should return an empty string if no path provided", () => {
const mockHomedir = "/home/coder"
const actual = util.humanPath(mockHomedir)
const expected = ""
expect(actual).toBe(expected)
})
it("should replace the homedir with ~", () => {
const mockHomedir = "/home/coder"
const path = `${mockHomedir}/code-server`
const actual = util.humanPath(mockHomedir, path)
const expected = "~/code-server"
expect(actual).toBe(expected)
})
})
describe("isWsl", () => { describe("isWsl", () => {
const testName = "wsl" const testName = "wsl"

View File

@@ -0,0 +1,277 @@
import { logger } from "@coder/logger"
import * as app from "../../../src/node/app"
import { EditorSessionManager, makeEditorSessionManagerServer } from "../../../src/node/vscodeSocket"
import { clean, tmpdir, listenOn, mockLogger } from "../../utils/helpers"
describe("makeEditorSessionManagerServer", () => {
let tmpDirPath: string
const testName = "mesms"
beforeAll(async () => {
jest.clearAllMocks()
mockLogger()
await clean(testName)
})
afterAll(() => {
jest.resetModules()
})
beforeEach(async () => {
tmpDirPath = await tmpdir(testName)
})
it("warns if socket cannot be created", async () => {
jest.spyOn(app, "listen").mockImplementation(() => {
throw new Error()
})
const server = await makeEditorSessionManagerServer(
`${tmpDirPath}/code-server-ipc.sock`,
new EditorSessionManager(),
)
expect(logger.warn).toHaveBeenCalledWith(`Could not create socket at ${tmpDirPath}/code-server-ipc.sock`)
server.close()
})
})
describe("EditorSessionManager", () => {
let tmpDirPath: string
const testName = "esm"
beforeAll(async () => {
await clean(testName)
})
beforeEach(async () => {
tmpDirPath = await tmpdir(testName)
})
describe("getCandidatesForFile", () => {
it("should prefer the last added socket path for a matching path", async () => {
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa-1.sock`,
})
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa-2.sock`,
})
manager.addSession({
workspace: {
id: "bbb",
folders: [
{
uri: {
path: "/bbb",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
})
const socketPaths = manager.getCandidatesForFile("/aaa/some-file:1:1")
expect(socketPaths.map((x) => x.socketPath)).toEqual([
// Matches
`${tmpDirPath}/vscode-ipc-aaa-2.sock`,
`${tmpDirPath}/vscode-ipc-aaa-1.sock`,
// Non-matches
`${tmpDirPath}/vscode-ipc-bbb.sock`,
])
})
it("should return the last added socketPath if there are no matches", async () => {
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
})
manager.addSession({
workspace: {
id: "bbb",
folders: [
{
uri: {
path: "/bbb",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
})
const socketPaths = manager.getCandidatesForFile("/ccc/some-file:1:1")
expect(socketPaths.map((x) => x.socketPath)).toEqual([
`${tmpDirPath}/vscode-ipc-bbb.sock`,
`${tmpDirPath}/vscode-ipc-aaa.sock`,
])
})
it("does not just directly do a substring match", async () => {
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
})
manager.addSession({
workspace: {
id: "bbb",
folders: [
{
uri: {
path: "/bbb",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
})
const entries = manager.getCandidatesForFile("/aaaxxx/some-file:1:1")
expect(entries.map((x) => x.socketPath)).toEqual([
`${tmpDirPath}/vscode-ipc-bbb.sock`,
`${tmpDirPath}/vscode-ipc-aaa.sock`,
])
})
})
describe("getConnectedSocketPath", () => {
it("should return socket path if socket is active", async () => {
listenOn(`${tmpDirPath}/vscode-ipc-aaa.sock`).once()
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
})
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-aaa.sock`)
})
it("should return undefined if socket is inactive", async () => {
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
})
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
expect(socketPath).toBeUndefined()
})
it("should return undefined given no matching active sockets", async () => {
const vscodeSockets = listenOn(`${tmpDirPath}/vscode-ipc-bbb.sock`)
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
})
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
expect(socketPath).toBeUndefined()
vscodeSockets.close()
})
it("should return undefined if there are no entries", async () => {
const manager = new EditorSessionManager()
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
expect(socketPath).toBeUndefined()
})
it("should return most recently used socket path available", async () => {
listenOn(`${tmpDirPath}/vscode-ipc-aaa-1.sock`).once()
const manager = new EditorSessionManager()
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa-1.sock`,
})
manager.addSession({
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa-2.sock`,
})
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-aaa-1.sock`)
// Failed sockets should be removed from the entries.
expect((manager as any).entries.has(`${tmpDirPath}/vscode-ipc-aaa-2.sock`)).toBe(false)
})
})
})

View File

@@ -1,8 +1,8 @@
import { promises as fs } from "fs"
import * as path from "path"
import { workspaceDir } from "./constants" import { workspaceDir } from "./constants"
import { clean, tmpdir } from "./helpers" import { clean, tmpdir } from "./helpers"
import * as wtfnode from "./wtfnode" import * as wtfnode from "./wtfnode"
import * as path from "path"
import { promises as fs } from "fs"
/** /**
* Perform workspace cleanup and authenticate. This should be ran before e2e * Perform workspace cleanup and authenticate. This should be ran before e2e

View File

@@ -108,20 +108,6 @@ export function idleTimer(message: string, reject: (error: Error) => void, delay
} }
} }
/**
* A helper function which returns a boolean indicating whether
* the given address is AddressInfo and has .address
* and a .port property.
*/
export function isAddressInfo(address: unknown): address is net.AddressInfo {
return (
address !== null &&
typeof address !== "string" &&
(address as net.AddressInfo).port !== undefined &&
(address as net.AddressInfo).address !== undefined
)
}
/** /**
* If using a proxy, return the address of the proxy. * If using a proxy, return the address of the proxy.
* *
@@ -150,3 +136,52 @@ export function getMaybeProxiedPathname(url: URL): string {
return url.pathname return url.pathname
} }
interface FakeVscodeSockets {
/* If called, closes all servers after the first connection. */
once(): FakeVscodeSockets
/* Manually close all servers. */
close(): Promise<void>
}
/**
* Creates servers for each socketPath specified.
*/
export function listenOn(...socketPaths: string[]): FakeVscodeSockets {
let once = false
const servers = socketPaths.map((socketPath) => {
const server = net.createServer(() => {
if (once) {
close()
}
})
server.listen(socketPath)
return server
})
async function close() {
await Promise.all(
servers.map(
(server) =>
new Promise<void>((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err)
return
}
resolve()
})
}),
),
)
}
const fakeVscodeSockets = {
close,
once: () => {
once = true
return fakeVscodeSockets
},
}
return fakeVscodeSockets
}

View File

@@ -35,7 +35,7 @@ export class HttpServer {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.hs.on("error", reject) this.hs.on("error", reject)
this.hs.listen(0, "localhost", () => { this.hs.listen(0, "127.0.0.1", () => {
this.hs.off("error", reject) this.hs.off("error", reject)
resolve() resolve()

View File

@@ -2856,6 +2856,11 @@ qs@^6.10.1:
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
react-is@^17.0.1: react-is@^17.0.1:
version "17.0.2" version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
@@ -2875,6 +2880,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
resolve-cwd@^3.0.0: resolve-cwd@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -2936,16 +2946,16 @@ saxes@^5.0.1:
xmlchars "^2.2.0" xmlchars "^2.2.0"
semver@7.x, semver@^7.3.2, semver@^7.3.5: semver@7.x, semver@^7.3.2, semver@^7.3.5:
version "7.3.5" version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
semver@^6.0.0, semver@^6.3.0: semver@^6.0.0, semver@^6.3.0:
version "6.3.0" version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
set-blocking@^2.0.0: set-blocking@^2.0.0:
version "2.0.0" version "2.0.0"
@@ -3222,13 +3232,14 @@ to-regex-range@^5.0.1:
is-number "^7.0.0" is-number "^7.0.0"
tough-cookie@^4.0.0: tough-cookie@^4.0.0:
version "4.0.0" version "4.1.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
dependencies: dependencies:
psl "^1.1.33" psl "^1.1.33"
punycode "^2.1.1" punycode "^2.1.1"
universalify "^0.1.2" universalify "^0.2.0"
url-parse "^1.5.3"
tr46@^2.1.0: tr46@^2.1.0:
version "2.1.0" version "2.1.0"
@@ -3280,10 +3291,18 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
universalify@^0.1.2: universalify@^0.2.0:
version "0.1.2" version "0.2.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
util-deprecate@^1.0.1: util-deprecate@^1.0.1:
version "1.0.2" version "1.0.2"
@@ -3379,9 +3398,9 @@ wide-align@^1.1.2:
string-width "^1.0.2 || 2 || 3 || 4" string-width "^1.0.2 || 2 || 3 || 4"
word-wrap@~1.2.3: word-wrap@~1.2.3:
version "1.2.3" version "1.2.4"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
wrap-ansi@^7.0.0: wrap-ansi@^7.0.0:
version "7.0.0" version "7.0.0"

2496
yarn.lock

File diff suppressed because it is too large Load Diff