Compare commits

...

74 Commits

Author SHA1 Message Date
dependabot[bot]
1da7cda39e chore: bump vm2 from 3.9.15 to 3.9.17 (#6155)
Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.15 to 3.9.17.
- [Release notes](https://github.com/patriksimek/vm2/releases)
- [Changelog](https://github.com/patriksimek/vm2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/patriksimek/vm2/compare/3.9.15...3.9.17)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-20 10:58:58 -08:00
Asher
c829d74203 Update to Code 1.77.3 (#6148) 2023-04-13 10:34:33 -08:00
Asher
39075b2cf3 Update to Code 1.77.2 (#6142)
No changes necessary.
2023-04-12 08:52:16 -08:00
dependabot[bot]
aed1fc0119 chore: bump cross-fetch from 3.1.4 to 3.1.5 in /test (#6087)
Bumps [cross-fetch](https://github.com/lquixada/cross-fetch) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/lquixada/cross-fetch/releases)
- [Commits](https://github.com/lquixada/cross-fetch/compare/v3.1.4...v3.1.5)

---
updated-dependencies:
- dependency-name: cross-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-11 14:17:03 -05:00
Asher
6e411adb01 Update Code to 1.77.1 (#6135) 2023-04-10 10:28:13 -08:00
dependabot[bot]
36ba646bcb chore: bump json5 from 2.2.0 to 2.2.3 in /test (#6085)
Bumps [json5](https://github.com/json5/json5) from 2.2.0 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.0...v2.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-10 09:45:09 -08:00
dependabot[bot]
278d0f2184 chore: bump vm2 from 3.9.11 to 3.9.15 (#6134)
Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.11 to 3.9.15.
- [Release notes](https://github.com/patriksimek/vm2/releases)
- [Changelog](https://github.com/patriksimek/vm2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/patriksimek/vm2/compare/3.9.11...3.9.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-07 22:01:25 +00:00
dependabot[bot]
b954250018 chore: bump eslint-plugin-import from 2.26.0 to 2.27.5 (#6123)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.26.0 to 2.27.5.
- [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.26.0...v2.27.5)

---
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-04-07 21:40:43 +00:00
dependabot[bot]
2b72da22e6 chore: bump @typescript-eslint/parser from 5.41.0 to 5.57.1 (#6132)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.41.0 to 5.57.1.
- [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.57.1/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-04-07 21:21:36 +00:00
dependabot[bot]
3c838e2d02 chore: bump @typescript-eslint/eslint-plugin from 5.56.0 to 5.57.1 (#6133)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.56.0 to 5.57.1.
- [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.57.1/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-04-07 20:55:02 +00:00
dependabot[bot]
f8d563c49b chore: bump split2 from 4.1.0 to 4.2.0 (#6121)
Bumps [split2](https://github.com/mcollina/split2) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/mcollina/split2/releases)
- [Commits](https://github.com/mcollina/split2/compare/v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: split2
  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-04-07 20:36:33 +00:00
dependabot[bot]
39e63af359 chore: bump ts-node from 10.1.0 to 10.9.1 (#6125)
Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 10.1.0 to 10.9.1.
- [Release notes](https://github.com/TypeStrong/ts-node/releases)
- [Changelog](https://github.com/TypeStrong/ts-node/blob/main/development-docs/release-template.md)
- [Commits](https://github.com/TypeStrong/ts-node/compare/v10.1.0...v10.9.1)

---
updated-dependencies:
- dependency-name: ts-node
  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-04-07 12:17:22 -08:00
Asher
c995988b70 Set proxy URI to domain proxy when possible (#6115)
This will make the ports panel use it instead of the default path-based
proxy.
2023-03-30 12:01:49 -08:00
watchakorn-18k
a44bd71043 Add thai language i18n (#6103) 2023-03-30 13:00:58 -05:00
Asher
19bcd043d7 Add debug log for origin check (#6096)
Extracted host detection into a separate function to avoid multiple log
lines on each return and went with a thrown error to consolidate the
common log text.
2023-03-30 12:24:33 -05:00
dependabot[bot]
c32a31d802 chore: bump ws from 8.2.0 to 8.13.0 (#6100)
Bumps [ws](https://github.com/websockets/ws) from 8.2.0 to 8.13.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.2.0...8.13.0)

---
updated-dependencies:
- dependency-name: ws
  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-03-22 14:08:53 -05:00
dependabot[bot]
45ca68c362 chore: bump jpeg-js from 0.4.3 to 0.4.4 in /test (#6086)
Bumps [jpeg-js](https://github.com/eugeneware/jpeg-js) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/eugeneware/jpeg-js/releases)
- [Commits](https://github.com/eugeneware/jpeg-js/compare/v0.4.3...v0.4.4)

---
updated-dependencies:
- dependency-name: jpeg-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 14:03:39 -05:00
dependabot[bot]
8f1a70339d chore: bump node-fetch from 2.6.6 to 2.6.7 in /test (#6084)
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.6 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.6...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 14:03:09 -05:00
dependabot[bot]
35209aa5b5 chore: bump minimatch from 3.0.4 to 3.1.2 in /test (#6083)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 14:02:52 -05:00
dependabot[bot]
87f44599e5 chore: bump audit-ci from 6.0.0 to 6.6.1 (#6040)
Bumps [audit-ci](https://github.com/IBM/audit-ci) from 6.0.0 to 6.6.1.
- [Release notes](https://github.com/IBM/audit-ci/releases)
- [Commits](https://github.com/IBM/audit-ci/compare/v6.0.0...v6.6.1)

---
updated-dependencies:
- dependency-name: audit-ci
  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-03-22 14:02:41 -05:00
dependabot[bot]
71f01ec0f0 chore: bump @types/node from 16.11.41 to 16.18.18 (#6099)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.11.41 to 16.18.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  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-03-22 14:02:22 -05:00
dependabot[bot]
f8e2d00099 chore: bump @typescript-eslint/eslint-plugin from 5.51.0 to 5.56.0 (#6102)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.51.0 to 5.56.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.56.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-03-22 14:02:00 -05:00
dependabot[bot]
a485fec27c chore: bump minimist from 1.2.5 to 1.2.8 in /test (#6095)
Bumps [minimist](https://github.com/minimistjs/minimist) from 1.2.5 to 1.2.8.
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 18:58:55 +00:00
dependabot[bot]
aa2276293a chore: bump cookiejar from 2.1.3 to 2.1.4 in /test (#6094)
Bumps [cookiejar](https://github.com/bmeck/node-cookiejar) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/bmeck/node-cookiejar/releases)
- [Commits](https://github.com/bmeck/node-cookiejar/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 18:11:42 +00:00
Felix K
72eae01684 Add information on using PWA with desktop Firefox (#6098)
Co-authored-by: Felix Kuhlmann <felix-kuhlmann@gmx.de>
2023-03-22 17:52:35 +00:00
Ahmed El-Sharnoby
bca733de3d Allow app-name configuration to change the name of the PWA (#6093) 2023-03-22 01:02:31 +00:00
Asher
78282a1fd6 Make random formatting change to security.yaml
This is an attempt to set myself as the notification target.

Closes #6090.
2023-03-21 12:24:10 -08:00
Asher
dbdd2edb62 Fix proxy error on web sockets
Fixes #6088.
2023-03-21 12:24:09 -08:00
Asher
a9d61daa91 Release v4.11.0 2023-03-16 10:04:57 -08:00
Initial-heart
6c08466b05 Correct default shell docs for npm on Windows (#6078) 2023-03-16 12:46:11 -05:00
dependabot[bot]
85e083580d chore: bump aquasecurity/trivy-action from 0.9.0 to 0.9.2 (#6075)
Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.9.0 to 0.9.2.
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](cff3e9a7f6...1f0aa582c8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-14 16:49:25 -05:00
Asher
b0431069a1 Replace CI deprecations (#6072)
* Replace deprecated set-output usage in CI

* Update tj-actions/changed-files

CI is saying it uses Node 12 and that Node 12 actions are deprecated.
2023-03-14 15:52:53 -05:00
Asher
f9cc07926b Update Code to 1.76.1 (#6070)
* Update Code to 1.76.1

- worker-src already contains blob so we can avoid patching that.
- localeService moved.
- Remaining changes were just line changes.

* Make language extensions installable again

Still might want to look into making the native language support work
but for now it seems better not to break backwards compatibility since
the native implementation is quite different.

* Avoid "install in browser" for language packs

It will not work.

* Import correct locale service

I believe before the contributions imported this but now we have to do
it here.
2023-03-14 15:03:53 -05:00
Asher
be40eca5d9 Release v4.10.1 2023-03-03 22:24:54 -09:00
Asher
9ba08907da Fix Node version failure in publish workflow
Looks like the images got updated to v18 so they started failing.  For
npm install v16 and for Docker just run the script directly, it seems
silly to waste time installing v16 just to run a script through yarn.
2023-03-03 22:23:21 -09:00
Asher
d477972c68 Add origin checks to web sockets (#6048)
* Move splitOnFirstEquals to util

I will be making use of this to parse the forwarded header.

* Type splitOnFirstEquals with two items

Also add some test cases.

* Check origin header on web sockets

* Update changelog with origin check

* Fix web sockets not closing with error code
2023-03-03 03:12:34 -06:00
Asher
a47cd81d8c Update FAQ: code-server is not using a direct fork
Also the wrapper process is another major difference along with the
update notifications.
2023-02-27 14:29:55 -09:00
Asher
c9fbcffd53 Rewrite differences in FAQ
- Move differences to the Codespaces section since they apply to both
  Codespaces and OpenVSCode-Server
- Add some important missing differences
- Exclude settings sync (not being worked on)
- Exclude the plugin API (deprecated)
- Exclude certificate support (browsers these days are starting to
  require trusted certs so better not to recommend using this)
2023-02-27 14:21:11 -09:00
Michael Tinsley
befa76d09d Fix extra prefix dashes in install.sh (#6028) 2023-02-27 22:57:57 +00:00
Asher
e0ece195c1 Update changelog and chart with new version 2023-02-15 11:16:09 -09:00
Asher
ac1fba8bde Remove deprecated --link (#6018) 2023-02-13 16:52:48 -06:00
Blue
6d8ed77fb0 feat(helm-chart): Add support for extraSecretMounts subPath in helm-chart (#5961) 2023-02-13 13:01:32 -06:00
Samuel Walker
4fb87f920f feature: add ability to attach ports to code server (#6015) 2023-02-13 12:56:05 -06:00
Asher
36daac3031 Update to 1.75.1 2023-02-13 07:45:24 -09:00
Asher
45aef719d3 Make sure heartbeat isActive resolves
This does not seem to actually cause an issue (not resolving ends up
with the same behavior as resolving with false) but I am not sure if the
hanging promises would be a memory leak so seems best to fix.
2023-02-08 11:43:40 -09:00
Asher
6d6c5e18d1 Fix watch script ignoring stdout
This was lost due to the change from fork to spawn.
2023-02-08 11:42:28 -09:00
Asher
17bca521af Update changelog 2023-02-07 13:57:53 -09:00
Asher
82073743b1 Update release guide 2023-02-07 13:57:52 -09:00
Asher
71ff747c48 Update Code to 1.75.0 (#6004)
* Update Code to 1.75.0

- getting-started.diff: The way to get an icon's class changed
- proxy-uri.diff: The product service is passed in so we can get the
  proxy URI from that now instead of passing it in separately.

* Remove workspace trust test

Something in how/when Code displays the trust dialog appears to have
changed, failing the test.  I am not sure it makes sense for us to be
testing upstream code anyway.

* Use regular Node for watch

Since we spawn the watch script with ts-node it was using ts-node for
the web server spawn as well.  With latest Code there are for some
reason type errors (it cannot find @types/node) but this is already
compiled code which already passed type checks; any type errors here are
useless.  To fix spawn with regular Node.

* Fix some workers not loading
2023-02-07 16:22:06 -06:00
dependabot[bot]
e5a2537aee chore: bump limiter from 1.1.5 to 2.1.0 (#6001)
* chore: bump limiter from 1.1.5 to 2.1.0

Bumps [limiter](https://github.com/jhurliman/node-rate-limiter) from 1.1.5 to 2.1.0.
- [Release notes](https://github.com/jhurliman/node-rate-limiter/releases)
- [Commits](https://github.com/jhurliman/node-rate-limiter/commits)

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

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

* Update limiter 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-02-06 13:12:57 -06:00
dependabot[bot]
bce6239801 chore: bump @typescript-eslint/eslint-plugin from 5.41.0 to 5.51.0 (#6002)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.41.0 to 5.51.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.51.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-02-06 18:59:24 +00:00
dependabot[bot]
93589edb61 chore: bump eslint-config-prettier from 8.5.0 to 8.6.0 (#6003)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.5.0 to 8.6.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.5.0...v8.6.0)

---
updated-dependencies:
- dependency-name: eslint-config-prettier
  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-02-06 12:41:05 -06:00
Asher
bfcca5fcc0 Remove docs preview
This is failing CI on Dependabot PRs.  Opted to just remove it since
most (all?) PRs will be from forks and this workflow will not run
anyway.  If we figure out the secret situation we can add it back.
2023-02-06 09:07:34 -09:00
Asher
a76c0c5742 Remove PR npm package
It is causing CI to fail for Dependabot (no access to the token) and it
does not work with forks and currently there is no one who pushes
straight to the repo so this will never be used.

Can always add it back if we figure out how to make the secrets work.
2023-02-06 08:56:47 -09:00
renovate[bot]
6e1b9131e9 chore(deps): update aquasecurity/trivy-action digest to cff3e9a (#5994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-06 11:36:05 -06:00
Dean Sheather
776d57b12b chore: enable dependabot (#5997) 2023-02-06 11:32:52 -06:00
Asher
026879b78c Update Code to 1.74.3 (#5989) 2023-02-06 10:24:37 -06:00
Joe Previte
401d423dfd docs: add usage in Coder (#5975)
* docs: add difference between Coder

Add a short block explaining the difference between code-server and
Coder.

* docs: add new doc coder.md under Install

This adds a new doc explaining how to install code-server in a Coder
workspace using Terraform.
2023-01-27 17:14:54 +00:00
Joe Previte
96d9c5eb0f docs: add port-forwarding (#5979) 2023-01-27 16:41:27 +00:00
Kyle Carberry
134e9b40b7 chore: fix requirements so it navigates to docs (#5973)
This was navigating to the relative markdown file,
which is confusing when some links go to the docs.
2023-01-17 17:09:24 +00:00
zhaozhiming
7c2aa8c417 feat: add i18n in login page (#5947)
* feat: add i18n in login page

* fix: add word space and put the app name into the title

* fix: remove duplicate replace title

* fix: prettier format code

* fix: fix typescript check warning

* fix: add zh-cn locale file code owner

* fix: use existing flag locale to the login page

Co-authored-by: Joe Previte <jjprevite@gmail.com>
2023-01-13 17:42:49 +00:00
Joe Previte
d40a9742c0 feat(ci): add lint-actions step to build.yaml (#5957)
* feat(ci): add lint-actions step to build.yaml

This adds a new job to the Build CI pipeline to lint our GitHub Actions.

By doing this, we can prevent typos from slipping in.

Fixes #5776

* fix: disable shellcheck in actionlint

I don't think we want to enable this for now.

* fix: ignore set-output warnings for now

It's deprecated but there isn't a reason to move away from using it yet.
2023-01-13 17:21:56 +00:00
renovate[bot]
67416b7b79 fix(deps): update dependency argon2 to v0.30.3 (#5937)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 08:27:11 -07:00
Joe Previte
9e266db379 chore: use matchUpdateTypes (#5942) 2023-01-04 23:08:39 +00:00
Joe Previte
834d16df2a docs(CONTRIBUTING): add standalone release section (#5946)
* docs: update section in troubleshooting

* docs: add section on standalone release
2023-01-04 15:47:34 -07:00
Joe Previte
d835cb9865 docs: add new termux installation method (#5938)
* docs: add new termux installation method

* fixup: formatting
2023-01-04 17:16:23 +00:00
Joe Previte
9e9cbd846d chore: update renovate to include peer deps (#5940) 2023-01-03 15:14:21 -07:00
Joe Previte
077a8f6a6b chore: pin json5 to 1.0.2 (#5936)
* chore: pin json5 to 1.0.2

* fixup: formatting
2023-01-03 20:57:16 +00:00
Joe Previte
4e280811f9 chore: update renovate and deps (#5914)
* chore: update renovate.json ignoreDeps

ansi-regex, env-paths and limiter all switch to ESM which we can't
support at the moment so ignore updates for now.

* chore: update actions/cache@v3

* chore: update minor deps

* chore: add pretteir to renovate.json
2023-01-03 17:28:58 +00:00
Joe Previte
3eb35979f0 chore: update aur job in publish.yaml (#5915)
This uses the `env.VERSION` which should fix the issue with version being blank.
2022-12-22 13:58:20 -06:00
Joe Previte
8377bd23df chore: upgrade Code to 1.74.1 (#5909)
* chore: upgrade Code to 1.74.1

* chore: remove require in integration.diff

I don't know what the impact of this is but in 192c67db71
they removed the usage of `require` in `server.main.ts`.

More details in PR: https://github.com/microsoft/vscode/pull/165831

* chore: update marketplace.diff

* chore: update sha hash in webview.diff

* chore: update disable-builtin-ext-update.diff

If my logic is right, then this patch is now simplified thanks to this:
https://github.com/microsoft/vscode/blob/1.74.1/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts#L1238

* chore: refresh proxy-uri patch

* chore: refresh local-storage.diff

* chore: refresh sourcemaps.diff

* chore: refresh disable-downloads.diff

* chore: refresh display-language.diff

* chore: refresh getting-started.diff

* docs: update testing notes for cli-window-open

* docs: update telemetry testing instructions

* fix: add GITHUB_TOKEN to build code-server job

Downloading @vscode/ripgrep is failing only in CI so adding this
environment variable to see if it increases the rate limit.

Ref: https://github.com/microsoft/vscode-ripgrep#github-api-limit-note

* refactor: use own cache key build code-server job

* temp: disable vscode test

* refactor: delete wrapper test

* Revert "refactor: delete wrapper test"

This reverts commit 3999279b73.

* refactor: move vscode tests to e2e (#5911)

* wip: migrate vscode tests to e2e

* feat: add codeWorkspace to global setup

* refactor: only use dir in spawn when we should

* wip: migrate more tests

* refactor: move all vscode tests to e2e

* refactor(ci): move unit to own job

* fixup: add codecov to unit test step

* Update test/e2e/models/CodeServer.ts

* Update test/e2e/models/CodeServer.ts

* docs: add note about intercept requests

* refactor: rm unused clean() calls

* refactor: delete duplicate test

* refactor: update 'should not redirect' test

* refactor: rm unused imports

* refactor: rm unnecessary navigate call in test

* fixup: formatting

* wip: update test

* refactor: modify assertion for proxy

* fixup: use REVERSE_PROXY_BASE_PATH

* refactor: add helper fn getMaybeProxiedPathname

* fixup: formatting

* fixup: rm unused import

* chore: increase playwright timeout

* Revert "chore: increase playwright timeout"

This reverts commit a059129252.

* chore: rm timeout
2022-12-22 10:25:28 -07:00
Toby Cm
930791d249 Added how to disable file downloads in faq (#5907) 2022-12-19 16:00:42 +00:00
Joe Previte
5c21015dbe release: v4.9.1 (#5893)
* chore: add 4.9.1 to changelog

* chore: bump Helm to 4.9.1
2022-12-16 12:07:36 -07:00
Joe Previte
fa39d4761a fix: dst path in nfpm (#5875)
* fix: dst path in nfpm

* trigger ci

* Update ci/README.md
2022-12-16 11:35:13 -07:00
87 changed files with 1710 additions and 1100 deletions

2
.github/CODEOWNERS vendored
View File

@@ -3,3 +3,5 @@
ci/helm-chart/ @Matthew-Beckett @alexgorbatchev ci/helm-chart/ @Matthew-Beckett @alexgorbatchev
docs/install.md @GNUxeava docs/install.md @GNUxeava
src/node/i18n/locales/zh-cn.json @zhaozhiming

31
.github/dependabot.yaml vendored Normal file
View File

@@ -0,0 +1,31 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
labels: []
commit-message:
prefix: "chore"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
labels: []
ignore:
# Ignore patch updates for all dependencies
- dependency-name: "*"
update-types:
- version-update:semver-patch
# Ignore major updates to Node.js types, because they need to
# correspond to the Node.js engine version
- dependency-name: "@types/node"
update-types:
- version-update:semver-major

View File

@@ -45,7 +45,7 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v26.1 uses: tj-actions/changed-files@v35
with: with:
files: | files: |
docs/** docs/**
@@ -76,14 +76,14 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v26.1 uses: tj-actions/changed-files@v35
with: with:
files: | files: |
ci/helm-chart/** ci/helm-chart/**
- name: Install helm - name: Install helm
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: azure/setup-helm@v3.4 uses: azure/setup-helm@v3.5
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
@@ -107,7 +107,7 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v26.1 uses: tj-actions/changed-files@v35
with: with:
files: | files: |
**/*.ts **/*.ts
@@ -139,6 +139,67 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
run: yarn lint:ts run: yarn lint:ts
lint-actions:
name: Lint GitHub Actions
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Check workflow files
run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/7fdc9630cc360ea1a469eed64ac6d78caeda1234/scripts/download-actionlint.bash)
./actionlint -color -shellcheck= -ignore "set-output"
shell: bash
test-unit:
name: Run unit tests
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
with:
files: |
**/*.ts
files_ignore: |
lib/vscode/**
- name: Install Node.js v16
if: steps.changed-files.outputs.any_changed == 'true'
uses: actions/setup-node@v3
with:
node-version: "16"
- name: Fetch dependencies from cache
if: steps.changed-files.outputs.any_changed == 'true'
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true' && steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Run unit tests
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn test:unit
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
if: success()
build: build:
name: Build code-server name: Build code-server
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -170,22 +231,24 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: "**/node_modules" path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }} key: yarn-build-code-server-${{ hashFiles('**/yarn.lock') }}
restore-keys: | restore-keys: |
yarn-build- yarn-build-code-server-
- name: Install dependencies - name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true' if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile run: yarn --frozen-lockfile
- name: Build code-server - name: Build code-server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn build run: yarn build
# Get Code's git hash. When this changes it means the content is # Get Code's git hash. When this changes it means the content is
# different and we need to rebuild. # different and we need to rebuild.
- name: Get latest lib/vscode rev - name: Get latest lib/vscode rev
id: vscode-rev id: vscode-rev
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)" run: echo "rev=$(git rev-parse HEAD:./lib/vscode)" >> $GITHUB_OUTPUT
# We need to rebuild when we have a new version of Code, when any of # We need to rebuild when we have a new version of Code, when any of
# the patches changed, or when the code-server version changes (since # the patches changed, or when the code-server version changes (since
@@ -204,19 +267,6 @@ jobs:
if: steps.cache-vscode.outputs.cache-hit != 'true' if: steps.cache-vscode.outputs.cache-hit != 'true'
run: yarn build:vscode run: yarn build:vscode
# Our code imports code from VS Code's `out` directory meaning VS Code
# must be built before running these tests.
# TODO: Move to its own step?
- name: Run code-server unit tests
run: yarn test:unit
if: success()
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
if: success()
# The release package does not contain any native modules # The release package does not contain any native modules
# and is neutral to architecture/os/libc version. # and is neutral to architecture/os/libc version.
- name: Create release package - name: Create release package
@@ -233,65 +283,6 @@ jobs:
name: npm-package name: npm-package
path: ./package.tar.gz path: ./package.tar.gz
npm:
name: Publish npm package
# the npm-package gets uploaded as an artifact in Build
# so we need that to complete before this runs
needs: build
# This environment "npm" requires someone from
# coder/code-server-reviewers to approve the PR before this job runs.
environment: npm
# Only run if PR comes from base repo or event is not a PR
# Reason: forks cannot access secrets and this will always fail
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Download artifact
uses: actions/download-artifact@v3
id: download
with:
name: "npm-package"
path: release-npm-package
- name: Run ./ci/steps/publish-npm.sh
run: yarn publish:npm
env:
# NOTE@jsjoeio
# This is because npm enforces semantic versioning
# so it has to be a valid version. We only use this
# to publish dev versions from prs
# and beta versions from main.
VERSION: "0.0.0"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# NOTE@jsjoeio
# NPM_ENVIRONMENT intentionally not set here.
# Instead, itis determined in publish-npm.sh script
# using GITHUB environment variables
- name: Comment npm information
uses: marocchino/sticky-pull-request-comment@v2
with:
GITHUB_TOKEN: ${{ github.token }}
header: npm-dev-build
message: |
✨ code-server dev build published to npm for PR #${{ github.event.number }}!
* _Last publish status_: success
* _Commit_: ${{ github.event.pull_request.head.sha }}
To install in a local project, run:
```shell-session
npm install @coder/code-server-pr@${{ github.event.number }}
```
To install globally, run:
```shell-session
npm install -g @coder/code-server-pr@${{ github.event.number }}
```
test-e2e: test-e2e:
name: Run e2e tests name: Run e2e tests
needs: build needs: build
@@ -336,7 +327,7 @@ jobs:
./test/node_modules/.bin/playwright install ./test/node_modules/.bin/playwright install
- name: Run end-to-end tests - name: Run end-to-end tests
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e --global-timeout 840000 run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e
- name: Upload test artifacts - name: Upload test artifacts
if: always() if: always()
@@ -392,7 +383,7 @@ jobs:
./test/node_modules/.bin/playwright install ./test/node_modules/.bin/playwright install
- name: Cache Caddy - name: Cache Caddy
uses: actions/cache@v2 uses: actions/cache@v3
id: caddy-cache id: caddy-cache
with: with:
path: | path: |
@@ -405,7 +396,7 @@ jobs:
if: steps.caddy-cache.outputs.cache-hit != 'true' if: steps.caddy-cache.outputs.cache-hit != 'true'
run: | run: |
gh release download v2.5.2 --repo caddyserver/caddy --pattern "caddy_2.5.2_linux_amd64.tar.gz" gh release download v2.5.2 --repo caddyserver/caddy --pattern "caddy_2.5.2_linux_amd64.tar.gz"
mkdir -p ~/.cache/caddy mkdir -p ~/.cache/caddy
tar -xzf caddy_2.5.2_linux_amd64.tar.gz --directory ~/.cache/caddy tar -xzf caddy_2.5.2_linux_amd64.tar.gz --directory ~/.cache/caddy
- name: Start Caddy - name: Start Caddy

View File

@@ -1,46 +0,0 @@
name: Docs preview
on:
pull_request:
branches:
- main
paths:
- "docs/**"
permissions:
actions: none
checks: none
contents: read
deployments: none
issues: none
packages: none
pull-requests: write
repository-projects: none
security-events: none
statuses: none
jobs:
preview:
name: Docs preview
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Set outputs
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Comment Credentials
uses: marocchino/sticky-pull-request-comment@v2
# Only run if PR comes from base repo
# Reason: forks cannot access secrets and this will always fail
if: github.event.pull_request.head.repo.full_name == github.repository
with:
GITHUB_TOKEN: ${{ github.token }}
header: codercom-preview-docs
message: |
✨ code-server docs for PR #${{ github.event.number }} is ready! It will be updated on every commit.
* _Host_: https://coder.com/docs/code-server/${{ steps.vars.outputs.sha_short }}
* _Last deploy status_: success
* _Commit_: ${{ github.event.pull_request.head.sha }}
* _Workflow status_: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

View File

@@ -29,8 +29,14 @@ jobs:
- name: Checkout code-server - name: Checkout code-server
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Node.js v16
uses: actions/setup-node@v3
with:
node-version: "16"
cache: "yarn"
- name: Download npm package from release artifacts - name: Download npm package from release artifacts
uses: robinraju/release-downloader@v1.6 uses: robinraju/release-downloader@v1.7
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 }}
@@ -137,12 +143,14 @@ jobs:
- name: Open PR - name: Open PR
# We need to git push -u otherwise gh will prompt # We need to git push -u otherwise gh will prompt
# asking where to push the branch. # asking where to push the branch.
env:
VERSION: ${{ env.VERSION }}
run: | run: |
git checkout -b update-version-${{ steps.version.outputs.version }} git checkout -b update-version-${{ env.VERSION }}
git add . git add .
git commit -m "chore: updating version to ${{ steps.version.outputs.version }}" git commit -m "chore: updating version to ${{ env.VERSION }}"
git push -u origin $(git branch --show) git push -u origin $(git branch --show)
gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ steps.version.outputs.version }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ env.VERSION }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR
docker: docker:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
@@ -176,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.6 uses: robinraju/release-downloader@v1.7
with: with:
repository: "coder/code-server" repository: "coder/code-server"
tag: v${{ env.VERSION }} tag: v${{ env.VERSION }}
@@ -184,7 +192,7 @@ jobs:
out-file-path: "release-packages" out-file-path: "release-packages"
- name: Publish to Docker - name: Publish to Docker
run: yarn publish:docker run: ./ci/steps/docker-buildx-push.sh
env: env:
VERSION: ${{ env.VERSION }} VERSION: ${{ env.VERSION }}
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}

View File

@@ -12,9 +12,8 @@ on:
# Runs every Monday morning PST # Runs every Monday morning PST
- cron: "17 15 * * 1" - cron: "17 15 * * 1"
# Cancel in-progress runs for pull requests when developers push # Cancel in-progress runs for pull requests when developers push additional
# additional changes, and serialize builds in branches. # changes, and serialize builds in branches.
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }} cancel-in-progress: ${{ github.event_name == 'pull_request' }}
@@ -65,7 +64,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@9ab158e8597f3b310480b9a69402b419bc03dbd5 uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee
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@9ab158e8597f3b310480b9a69402b419bc03dbd5 uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee
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

@@ -20,6 +20,53 @@ Code v99.99.999
--> -->
## [4.11.0](https://github.com/coder/code-server/releases/tag/v4.11.0) - 2023-03-16
Code v1.76.1
### Changed
- Updated to Code 1.76.1
## [4.10.1](https://github.com/coder/code-server/releases/tag/v4.10.1) - 2023-03-04
Code v1.75.1
### Security
Added an origin check to web sockets to prevent cross-site hijacking attacks on
users using older or niche browser that do not support SameSite cookies and
attacks across sub-domains that share the same root domain.
The check requires the host header to be set so if you use a reverse proxy
ensure it forwards that information otherwise web sockets will be blocked.
## [4.10.0](https://github.com/coder/code-server/releases/tag/v4.10.0) - 2023-02-15
Code v1.75.1
### Changed
- Updated to Code 1.75.1
### Removed
- Removed `--link` (was deprecated over thirteen months ago in 4.0.1).
## [4.9.1](https://github.com/coder/code-server/releases/tag/v4.9.1) - 2022-12-15
Code v1.73.1
### Changed
- Updated a couple steps in the build and release process to ensure we're using
`npm` and `yarn` consistently depending on the step.
### Fixed
- Fixed an issue with code-server version not displaying in the Help > About window.
- Fixed terminal not loading on macOS clients.
## [4.9.0](https://github.com/coder/code-server/releases/tag/v4.9.0) - 2022-12-06 ## [4.9.0](https://github.com/coder/code-server/releases/tag/v4.9.0) - 2022-12-06
Code v1.73.1 Code v1.73.1

View File

@@ -14,22 +14,6 @@ main() {
sed -i.bak "1s;^;#!/usr/bin/env node\n;" out/node/entry.js && rm out/node/entry.js.bak sed -i.bak "1s;^;#!/usr/bin/env node\n;" out/node/entry.js && rm out/node/entry.js.bak
chmod +x out/node/entry.js chmod +x out/node/entry.js
fi fi
# for arch; we do not use OS from lib.sh and get our own.
# lib.sh normalizes macos to darwin - but cloud-agent's binaries do not
source ./ci/lib.sh
OS="$(uname | tr '[:upper:]' '[:lower:]')"
mkdir -p ./lib
if ! [ -f ./lib/coder-cloud-agent ]; then
echo "Downloading the cloud agent..."
set +e
curl -fsSL "https://github.com/coder/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent
chmod +x ./lib/coder-cloud-agent
set -e
fi
} }
main "$@" main "$@"

View File

@@ -63,8 +63,6 @@ EOF
if [ "$KEEP_MODULES" = 1 ]; then if [ "$KEEP_MODULES" = 1 ]; then
rsync node_modules/ "$RELEASE_PATH/node_modules" rsync node_modules/ "$RELEASE_PATH/node_modules"
mkdir -p "$RELEASE_PATH/lib"
rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib"
fi fi
} }

View File

@@ -22,4 +22,4 @@ contents:
dst: /usr/lib/systemd/user/code-server.service dst: /usr/lib/systemd/user/code-server.service
- src: ./release-standalone/* - src: ./release-standalone/*
dst: /usr/lib/code-server/ dst: /usr/lib/code-server

View File

@@ -1,18 +1,8 @@
#!/usr/bin/env sh #!/usr/bin/env sh
set -eu set -eu
# Copied from ../lib.sh. # Copied from ../lib.sh except we do not rename Darwin and we do not need to
arch() { # detect Alpine.
cpu="$(uname -m)"
case "$cpu" in
aarch64) cpu=arm64 ;;
x86_64) cpu=amd64 ;;
esac
echo "$cpu"
}
# Copied from ../lib.sh except we do not rename Darwin since the cloud agent
# uses "darwin" in the release names and we do not need to detect Alpine.
os() { os() {
osname=$(uname | tr '[:upper:]' '[:lower:]') osname=$(uname | tr '[:upper:]' '[:lower:]')
case $osname in case $osname in
@@ -61,7 +51,6 @@ symlink_bin_script() {
cd "$oldpwd" cd "$oldpwd"
} }
ARCH="${NPM_CONFIG_ARCH:-$(arch)}"
OS="$(os)" OS="$(os)"
# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 # This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2
@@ -102,14 +91,6 @@ main() {
;; ;;
esac esac
mkdir -p ./lib
if curl -fsSL "https://github.com/coder/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent; then
chmod +x ./lib/coder-cloud-agent
else
echo "Failed to download cloud agent; --link will not work"
fi
if ! vscode_install; then if ! vscode_install; then
echo "You may not have the required dependencies to build the native modules." echo "You may not have the required dependencies to build the native modules."
echo "Please see https://github.com/coder/code-server/blob/main/docs/npm.md" echo "Please see https://github.com/coder/code-server/blob/main/docs/npm.md"

View File

@@ -11,22 +11,6 @@ main() {
make -s out/index.js make -s out/index.js
popd popd
# Our code imports from `out` in order to work during development but if you
# have only built for production you will have not have this directory. In
# that case symlink `out` to a production build directory.
if [[ ! -e lib/vscode/out ]]; then
pushd lib
local out=(vscode-reh-web-*)
if [[ -d "${out[0]}" ]]; then
ln -s "../${out[0]}/out" ./vscode/out
else
echo "Could not find lib/vscode/out or lib/vscode-reh-web-*"
echo "Code must be built before running unit tests"
exit 1
fi
popd
fi
# We must keep jest in a sub-directory. See ../../test/package.json for more # We must keep jest in a sub-directory. See ../../test/package.json for more
# information. We must also run it from the root otherwise coverage will not # information. We must also run it from the root otherwise coverage will not
# include our source files. # include our source files.

View File

@@ -1,4 +1,4 @@
import { spawn, fork, ChildProcess } from "child_process" import { spawn, ChildProcess } from "child_process"
import * as path from "path" import * as path from "path"
import { onLine, OnLineCallback } from "../../src/node/util" import { onLine, OnLineCallback } from "../../src/node/util"
@@ -30,12 +30,13 @@ class Watcher {
// Pass CLI args, save for `node` and the initial script name. // Pass CLI args, save for `node` and the initial script name.
const args = process.argv.slice(2) const args = process.argv.slice(2)
this.webServer = fork(path.join(this.rootPath, "out/node/entry.js"), args) this.webServer = spawn("node", [path.join(this.rootPath, "out/node/entry.js"), ...args])
onLine(this.webServer, (line) => console.log("[code-server]", line))
const { pid } = this.webServer const { pid } = this.webServer
this.webServer.on("exit", () => console.log("[Code Server]", `Web process ${pid} exited`)) this.webServer.on("exit", () => console.log("[code-server]", `Web process ${pid} exited`))
console.log("\n[Code Server]", `Spawned web server process ${pid}`) console.log("\n[code-server]", `Spawned web server process ${pid}`)
} }
//#endregion //#endregion
@@ -82,10 +83,10 @@ class Watcher {
private parseVSCodeLine: OnLineCallback = (strippedLine, originalLine) => { private parseVSCodeLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return if (!strippedLine.length) return
console.log("[VS Code]", originalLine) console.log("[Code OSS]", originalLine)
if (strippedLine.includes("Finished compilation with")) { if (strippedLine.includes("Finished compilation with")) {
console.log("[VS Code] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)") console.log("[Code OSS] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)")
this.reloadWebServer() this.reloadWebServer()
} }
} }
@@ -93,10 +94,10 @@ class Watcher {
private parseCodeServerLine: OnLineCallback = (strippedLine, originalLine) => { private parseCodeServerLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return if (!strippedLine.length) return
console.log("[Compiler][Code Server]", originalLine) console.log("[Compiler][code-server]", originalLine)
if (strippedLine.includes("Watching for file changes")) { if (strippedLine.includes("Watching for file changes")) {
console.log("[Compiler][Code Server]", "Finished compiling!", "(Refresh your web browser ♻️)") console.log("[Compiler][code-server]", "Finished compiling!", "(Refresh your web browser ♻️)")
this.reloadWebServer() this.reloadWebServer()
} }
} }

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.4.0 version: 3.7.0
# 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.9.0 appVersion: 4.11.0

View File

@@ -102,6 +102,7 @@ spec:
{{- range .Values.extraSecretMounts }} {{- range .Values.extraSecretMounts }}
- name: {{ .name }} - name: {{ .name }}
mountPath: {{ .mountPath }} mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }} readOnly: {{ .readOnly }}
{{- end }} {{- end }}
{{- range .Values.extraVolumeMounts }} {{- range .Values.extraVolumeMounts }}
@@ -114,6 +115,11 @@ spec:
- name: http - name: http
containerPort: 8080 containerPort: 8080
protocol: TCP protocol: TCP
{{- range .Values.extraPorts }}
- name: {{ .name }}
containerPort: {{ .port }}
protocol: {{ .protocol }}
{{- end }}
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /

View File

@@ -14,6 +14,12 @@ spec:
targetPort: http targetPort: http
protocol: TCP protocol: TCP
name: http name: http
{{- range .Values.extraPorts }}
- port: {{ .port }}
targetPort: {{ .port }}
protocol: {{ .protocol }}
name: {{ .name }}
{{- end }}
selector: selector:
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 }}

View File

@@ -6,7 +6,7 @@ replicaCount: 1
image: image:
repository: codercom/code-server repository: codercom/code-server
tag: '4.9.0' tag: '4.11.0'
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
@@ -179,6 +179,7 @@ extraInitContainers: |
extraSecretMounts: [] extraSecretMounts: []
# - name: secret-files # - name: secret-files
# mountPath: /etc/secrets # mountPath: /etc/secrets
# subPath: private.key # (optional)
# secretName: code-server-secret-files # secretName: code-server-secret-files
# readOnly: true # readOnly: true
@@ -196,3 +197,8 @@ extraConfigmapMounts: []
# subPath: certificates.crt # (optional) # subPath: certificates.crt # (optional)
# configMap: certs-configmap # configMap: certs-configmap
# readOnly: true # readOnly: true
extraPorts: []
# - name: minecraft
# port: 25565
# protocol: tcp

View File

@@ -11,9 +11,10 @@
- [Version updates to Code](#version-updates-to-code) - [Version updates to Code](#version-updates-to-code)
- [Patching Code](#patching-code) - [Patching Code](#patching-code)
- [Build](#build) - [Build](#build)
- [Creating a Standalone Release](#creating-a-standalone-release)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [I see "Forbidden access" when I load code-server in the browser](#i-see-forbidden-access-when-i-load-code-server-in-the-browser) - [I see "Forbidden access" when I load code-server in the browser](#i-see-forbidden-access-when-i-load-code-server-in-the-browser)
- ["Can only have one anonymous define call per script"](#can-only-have-one-anonymous-define-call-per-script) - ["Can only have one anonymous define call per script"](#can-only-have-one-anonymous-define-call-per-script)
- [Help](#help) - [Help](#help)
- [Test](#test) - [Test](#test)
- [Unit tests](#unit-tests) - [Unit tests](#unit-tests)
@@ -170,6 +171,22 @@ yarn package
> If you need your builds to support older distros, run the build commands > If you need your builds to support older distros, run the build commands
> inside a Docker container with all the build requirements installed. > inside a Docker container with all the build requirements installed.
#### Creating a Standalone Release
Part of the build process involves creating standalone releases. At the time of
writing, we do this for the following platforms/architectures:
- Linux amd64 (.tar.gz, .deb, and .rpm)
- Linux arm64 (.tar.gz, .deb, and .rpm)
- Linux arm7l (.tar.gz)
- Linux armhf.deb
- Linux armhf.rpm
- macOS amd64 (Intel-based)
Currently, these are compiled in CI using the `yarn release-standalone` command
in the `release.yaml` workflow. We then upload them to the draft release and
distribute via GitHub Releases.
### Troubleshooting ### Troubleshooting
#### I see "Forbidden access" when I load code-server in the browser #### I see "Forbidden access" when I load code-server in the browser
@@ -178,7 +195,7 @@ This means your patches didn't apply correctly. We have a patch to remove the au
Try popping off the patches with `quilt pop -a` and reapplying with `quilt push -a`. Try popping off the patches with `quilt pop -a` and reapplying with `quilt push -a`.
### "Can only have one anonymous define call per script" #### "Can only have one anonymous define call per script"
Code might be trying to use a dev or prod HTML in the wrong context. You can try re-running code-server and setting `VSCODE_DEV=1`. Code might be trying to use a dev or prod HTML in the wrong context. You can try re-running code-server and setting `VSCODE_DEV=1`.

View File

@@ -26,6 +26,7 @@
- [Is multi-tenancy possible?](#is-multi-tenancy-possible) - [Is multi-tenancy possible?](#is-multi-tenancy-possible)
- [Can I use Docker in a code-server container?](#can-i-use-docker-in-a-code-server-container) - [Can I use Docker in a code-server container?](#can-i-use-docker-in-a-code-server-container)
- [How do I disable telemetry?](#how-do-i-disable-telemetry) - [How do I disable telemetry?](#how-do-i-disable-telemetry)
- [What's the difference between code-server and Coder?](#whats-the-difference-between-code-server-and-coder)
- [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia) - [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia)
- [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server) - [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server)
- [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces) - [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces)
@@ -33,6 +34,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 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 -->
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
@@ -86,6 +88,12 @@ app (PWA):
1. Start the editor 1. Start the editor
2. Click the **plus** icon in the URL toolbar to install the PWA 2. Click the **plus** icon in the URL toolbar to install the PWA
If you use Firefox, you can use the appropriate extension to install PWA.
1. Go to the installation [website](https://addons.mozilla.org/en-US/firefox/addon/pwas-for-firefox/) of the add-on
2. Add the add-on to Firefox
3. Follow the os-specific instructions on how to install the runtime counterpart
For other browsers, you'll have to remap keybindings for shortcuts to work. For other browsers, you'll have to remap keybindings for shortcuts to work.
## Why can't code-server use Microsoft's extension marketplace? ## Why can't code-server use Microsoft's extension marketplace?
@@ -362,6 +370,15 @@ Use the `--disable-telemetry` flag to disable telemetry.
> We use the data collected only to improve code-server. > We use the data collected only to improve code-server.
## What's the difference between code-server and Coder?
code-server and Coder are both applications that can be installed on any
machine. The main difference is who they serve. Out of the box, code-server is
simply VS Code in the browser while Coder is a tool for provisioning remote
development environments via Terraform.
code-server was built for individuals while Coder was built for teams. In Coder, you create Workspaces which can have applications like code-server. If you're looking for a team solution, you should reach for [Coder](https://github.com/coder/coder).
## What's the difference between code-server and Theia? ## What's the difference between code-server and Theia?
At a high level, code-server is a patched fork of VS Code that runs in the At a high level, code-server is a patched fork of VS Code that runs in the
@@ -379,19 +396,13 @@ Theia doesn't allow you to reuse your existing VS Code config.
## What's the difference between code-server and OpenVSCode-Server? ## What's the difference between code-server and OpenVSCode-Server?
code-server and OpenVSCode-Server both allow you to access VS Code via a code-server and OpenVSCode-Server both allow you to access VS Code via a
browser. The two projects also use their own [forks of VS Code](https://github.com/coder/vscode) to browser. OpenVSCode-Server is a direct fork of VS Code with changes comitted
leverage modern VS Code APIs and stay up to date with the upsteam version. directly while code-server pulls VS Code in via a submodule and makes changes
via patch files.
However, OpenVSCode-Server is scoped at only making VS Code available in the web browser. However, OpenVSCode-Server is scoped at only making VS Code available as-is in
code-server includes some other features: the web browser. code-server contains additional changes to make the self-hosted
experience better (see the next section for details).
- password auth
- proxy web ports
- certificate support
- plugin API
- settings sync (coming soon)
For more details, see [this discussion post](https://github.com/coder/code-server/discussions/4267#discussioncomment-1411583).
## What's the difference between code-server and GitHub Codespaces? ## What's the difference between code-server and GitHub Codespaces?
@@ -399,8 +410,24 @@ Both code-server and GitHub Codespaces allow you to access VS Code via a
browser. GitHub Codespaces, however, is a closed-source, paid service offered by browser. GitHub Codespaces, however, is a closed-source, paid service offered by
GitHub and Microsoft. GitHub and Microsoft.
On the other hand, code-server is self-hosted, free, open-source, and On the other hand, code-server is self-hosted, free, open-source, and can be run
can be run on any machine with few limitations. on any machine with few limitations.
Specific changes include:
- Password authentication
- The ability to host at sub-paths
- Self-contained web views that do not call out to Microsoft's servers
- The ability to use your own marketplace and collect your own telemetry
- Built-in proxy for accessing ports on the remote machine integrated into
VS Code's ports panel
- Wrapper process that spawns VS Code on-demand and has a separate CLI
- Notification when updates are available
- [Some other things](https://github.com/coder/code-server/tree/main/patches)
Some of these changes appear very unlikely to ever be adopted by Microsoft.
Some may make their way upstream, further closing the gap, but at the moment it
looks like there will always be some subtle differences.
## Does code-server have any security login validation? ## Does code-server have any security login validation?
@@ -425,3 +452,7 @@ There are two ways to change the port on which code-server runs:
You can pass the flag `--disable-getting-started-override` to `code-server` or 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 file download?
You can pass the flag `--disable-file-downloads` to `code-server`

View File

@@ -142,24 +142,28 @@ changelog](https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS).
### Publishing a release ### Publishing a release
1. Go to GitHub Actions > Draft release > Run workflow off commit you want to 1. Go to GitHub Actions > Draft release > Run workflow on the commit you want to
release. CI will automatically upload the artifacts to the release. Make sure CI release. Make sure CI has finished the build workflow on that commit or this
has finished on that commit. will fail.
1. CI will automatically grab the 2. CI will automatically grab the build artifact on that commit, inject the
artifacts, publish the NPM package from `npm-package`, and publish the Docker version into the `package.json`, put together platform-specific packages, and
Hub image from `release-images`. upload those packages to a draft release.
1. Publish release. 3. Summarize the major changes in the `CHANGELOG.md`.
1. After, create a new branch called `release/v0.0.0` (replace 0s with actual version aka v4.5.0) 4. Copy the relevant changelog section to the release then publish it.
1. Summarize the major changes in the `CHANGELOG.md` 5. CI will automatically publish the NPM package, Docker image, and update
1. Bump chart version in `Chart.yaml`. Homebrew using the published release assets.
6. Bump the chart version in `Chart.yaml` and merge in the changelog updates.
#### Release Candidates #### Release Candidates
We prefer to do release candidates so the community can test things before a full-blown release. To do this follow the same steps as above but: We prefer to do release candidates so the community can test things before a
full-blown release. To do this follow the same steps as above but:
1. Only bump version in `package.json` 1. Add a `-rc.<number>` suffix to the version.
1. use `0.0.0-rc.0` 2. When you publish the release select "pre-release". CI will not automatically
1. When you publish the release, select "pre-release" publish pre-releases.
3. Do not update the chart version or merge in the changelog until the final
release.
#### AUR #### AUR

View File

@@ -16,7 +16,7 @@ access it in the browser.
## Requirements ## Requirements
See [requirements](requirements.md) for minimum specs, as well as instructions See [requirements](https://coder.com/docs/code-server/latest/requirements) for minimum specs, as well as instructions
on how to set up a Google VM on which you can install code-server. on how to set up a Google VM on which you can install code-server.
**TL;DR:** Linux machine with WebSockets enabled, 1 GB RAM, and 2 vCPUs **TL;DR:** Linux machine with WebSockets enabled, 1 GB RAM, and 2 vCPUs

37
docs/coder.md Normal file
View File

@@ -0,0 +1,37 @@
# Coder
To install and run code-server in a Coder workspace, we suggest using the `install.sh`
script in your template like so:
```terraform
resource "coder_agent" "dev" {
arch = "amd64"
os = "linux"
startup_script = <<EOF
#!/bin/sh
set -x
# install and start code-server
curl -fsSL https://code-server.dev/install.sh | sh -s -- --version 4.8.3
code-server --auth none --port 13337 &
EOF
}
resource "coder_app" "code-server" {
agent_id = coder_agent.dev.id
slug = "code-server"
display_name = "code-server"
url = "http://localhost:13337/"
icon = "/icon/code.svg"
subdomain = false
share = "owner"
healthcheck {
url = "http://localhost:13337/healthz"
interval = 3
threshold = 10
}
}
```
If you run into issues, ask for help on the `coder/coder` [Discussions
here](https://github.com/coder/coder/discussions).

View File

@@ -14,6 +14,7 @@
- [Accessing web services](#accessing-web-services) - [Accessing web services](#accessing-web-services)
- [Using a subdomain](#using-a-subdomain) - [Using a subdomain](#using-a-subdomain)
- [Using a subpath](#using-a-subpath) - [Using a subpath](#using-a-subpath)
- [Using your own proxy](#using-your-own-proxy)
- [Stripping `/proxy/<port>` from the request path](#stripping-proxyport-from-the-request-path) - [Stripping `/proxy/<port>` from the request path](#stripping-proxyport-from-the-request-path)
- [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)
@@ -316,12 +317,32 @@ To set your domain, start code-server with the `--proxy-domain` flag:
code-server --proxy-domain <domain> code-server --proxy-domain <domain>
``` ```
Now you can browse to `<port>.<domain>`. Note that this uses the host header, so For instance, if you have code-server exposed on `domain.tld` and a Python
ensure your reverse proxy (if you're using one) forwards that information. server running on port 8080 of the same machine code-server is running on, you
could run code-server with `--proxy-domain domain.tld` and access the Python
server via `8080.domain.tld`.
Note that this uses the host header, so ensure your reverse proxy (if you're
using one) forwards that information.
### Using a subpath ### Using a subpath
Simply browse to `/proxy/<port>/`. Simply browse to `/proxy/<port>/`. For instance, if you have code-server
exposed on `domain.tld` and a Python server running on port 8080 of the same
machine code-server is running on, you could access the Python server via
`domain.tld/proxy/8000`.
### Using your own proxy
You can make extensions and the ports panel use your own proxy by setting
`VSCODE_PROXY_URI`. For example if you set
`VSCODE_PROXY_URI=https://{{port}}.kyle.dev` when an application is detected
running on port 3000 of the same machine code-server is running on the ports
panel will create a link to https://3000.kyle.dev instead of pointing to the
built-in subpath-based proxy.
Note: relative paths are also supported i.e.
`VSCODE_PROXY_URI=./proxy/{{port}}`
### Stripping `/proxy/<port>` from the request path ### Stripping `/proxy/<port>` from the request path

View File

@@ -297,9 +297,9 @@ You can install code-server using the [Helm package manager](https://coder.com/d
## Windows ## Windows
We currently [do not publish Windows releases](https://github.com/coder/code-server/issues/1397). We recommend installing code-server onto Windows with [`npm`](#npm). We currently [do not publish Windows
releases](https://github.com/coder/code-server/issues/1397). We recommend
> Note: You will also need to [build coder/cloud-agent manually](https://github.com/coder/cloud-agent/issues/17) if you would like to use `code-server --link` on Windows. installing code-server onto Windows with [`npm`](#npm).
## Raspberry Pi ## Raspberry Pi

View File

@@ -1,11 +0,0 @@
# code-server --link
> Note: This feature is no longer recommended due to instability. Stay tuned for a revised version.
Run code-server with the flag `--link` and you'll get TLS, authentication, and a dedicated URL
for accessing your IDE out of the box.
```console
$ code-server --link
Proxying code-server, you can access your IDE at https://example.coder.co
```

View File

@@ -38,9 +38,9 @@
"path": "./guide.md", "path": "./guide.md",
"children": [ "children": [
{ {
"title": "--link", "title": "Coder",
"description": "How to run code-server --link", "description": "How to run code-server in Coder",
"path": "./link.md" "path": "./coder.md"
}, },
{ {
"title": "iPad", "title": "iPad",

View File

@@ -97,7 +97,7 @@ code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml # Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
``` ```
A `postinstall.sh` script will attempt to run. Select your terminal (e.g., Git bash) as the default application for `.sh` files. If an additional dialog does not appear, run the install command again. A `postinstall.sh` script will attempt to run. Select your terminal (e.g., Git bash) as the default shell for npm run-scripts. If an additional dialog does not appear, run the install command again.
If the `code-server` command is not found, you'll need to [add a directory to your PATH](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/). To find the directory, use the following command: If the `code-server` command is not found, you'll need to [add a directory to your PATH](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/). To find the directory, use the following command:

View File

@@ -12,7 +12,6 @@
- [Create a new user](#create-a-new-user) - [Create a new user](#create-a-new-user)
- [Install Go](#install-go) - [Install Go](#install-go)
- [Install Python](#install-python) - [Install Python](#install-python)
- [Working with PRoot](#working-with-proot)
<!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
@@ -20,53 +19,9 @@
## Install ## Install
1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**. 1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**.
2. Install Debian by running the following: 2. Run `pkg install tur-repo`
- Run `termux-setup-storage` to allow storage access, or else code-server won't be able to read from `/sdcard`.\ 3. Run `pkg install code-server`
> The following command is from [proot-distro](https://github.com/termux/proot-distro), but you can also use [Andronix](https://andronix.app/). 4. You can now start code server by simply running `code-server`.
> After Debian is installed the `~ $` will change to `root@localhost`.
```bash
pkg update -y && pkg install proot-distro -y && proot-distro install debian && proot-distro login debian
```
3. Run the following commands to setup Debian:
```bash
apt update && apt upgrade -y && apt-get install sudo vim git -y
```
4. Install [NVM](https://github.com/nvm-sh/nvm#install--update-script) by following the install guide in the README, just a curl/wget command.
5. Set up NVM for multi-user. After installing NVM it automatically adds the necessary commands for it to work, but it will only work if you are logged in as root:
- Copy the lines NVM asks you to run after running the install script.
- Run `nano /root/.bashrc` and comment out those lines by adding a `#` at the start.
- Run `nano /etc/profile` and paste those lines at the end of the file. Make sure to replace `$HOME` with `/root` on the first line.
- Now run `exit`
- Start Debian again `proot-distro login debian`
6. After following the instructions and setting up NVM you can now install the [required node version](https://coder.com/docs/code-server/latest/npm#nodejs-version) by running:
```bash
nvm install v<major_version_here>
```
7. To install `code-server` run the following:
> To check the install process (Will not actually install code-server)
> If it all looks good, you can install code-server by running the second command
```bash
curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run
```
```bash
curl -fsSL https://code-server.dev/install.sh | sh
```
8. You can now start code server by simply running `code-server`.
> Consider using a new user instead of root, read [here](https://www.howtogeek.com/124950/htg-explains-why-you-shouldnt-log-into-your-linux-system-as-root/) why using root is not recommended.\
> Learn how to add a user [here](#create-a-new-user).
## NPM Installation ## NPM Installation
@@ -202,35 +157,3 @@ eval "$(pyenv virtualenv-init -)"
7. Run `touch /root/.pyenv/version && echo "your_version_here" > /root/.pyenv/version` 7. Run `touch /root/.pyenv/version && echo "your_version_here" > /root/.pyenv/version`
8. (You may have to start Debian again) Run `python3 -V` to verify if PATH works or not. 8. (You may have to start Debian again) Run `python3 -V` to verify if PATH works or not.
> If `python3` doesn't work but pyenv says that the install was successful in step 6 then try running `$PYENV_ROOT/versions/your_version/bin/python3`. > If `python3` doesn't work but pyenv says that the install was successful in step 6 then try running `$PYENV_ROOT/versions/your_version/bin/python3`.
### Working with PRoot
Debian PRoot Distro Dev Environment
- Since Node and code-server are installed in the Debian PRoot distro, your `~/.ssh/` configuration, `~/.bashrc`, git, npm packages, etc. should be setup in PRoot as well.
- The terminal accessible in code-server will bring up the filesystem and `~/.bashrc` in the Debian PRoot distro.
Accessing files in the Debian PRoot Distro
- The `/data/data/com.termux/files/home` directory in PRoot accesses the termux home directory (`~`)
- The `/sdcard` directory in PRoot accesses the Android storage directory, though there are [known issues with git and files in the `/sdcard` path](#git-wont-work-in-sdcard)
Accessing the Debian PRoot distro/Starting code-server
- Run the following command to access the Debian PRoot distro, from the termux shell:
```bash
proot-distro login debian
```
- Run the following command to start code-server directly in the Debian PRoot distro, from the termux shell:
```bash
proot-distro login debian -- code-server
```
- If you [created a new user](#create-a-new-user), you'll need to insert the `--user <username>` option between `login` and `debian` in the commands above to run as the user instead of root in PRoot.
Additional information on PRoot and Termux
- Additional information on using your Debian PRoot Distro can be [found here](https://github.com/termux/proot-distro#functionality-overview).

View File

@@ -46,7 +46,7 @@ Usage:
Sets the prefix used by standalone release archives. Defaults to ~/.local Sets the prefix used by standalone release archives. Defaults to ~/.local
The release is unarchived into ~/.local/lib/code-server-X.X.X The release is unarchived into ~/.local/lib/code-server-X.X.X
and the binary symlinked into ~/.local/bin/code-server and the binary symlinked into ~/.local/bin/code-server
To install system wide pass ---prefix=/usr/local To install system wide pass --prefix=/usr/local
--rsh <bin> --rsh <bin>
Specifies the remote shell for remote installation. Defaults to ssh. Specifies the remote shell for remote installation. Defaults to ssh.

View File

@@ -88,15 +88,16 @@
}, },
"dependencies": { "dependencies": {
"@coder/logger": "^3.0.0", "@coder/logger": "^3.0.0",
"argon2": "0.30.2", "argon2": "0.30.3",
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
"express": "5.0.0-alpha.8", "express": "5.0.0-alpha.8",
"http-proxy": "^1.18.0", "http-proxy": "^1.18.0",
"httpolyglot": "^0.1.2", "httpolyglot": "^0.1.2",
"i18next": "^22.4.6",
"js-yaml": "^4.0.0", "js-yaml": "^4.0.0",
"limiter": "^1.1.5", "limiter": "^2.1.0",
"pem": "^1.14.2", "pem": "^1.14.2",
"proxy-agent": "^5.0.0", "proxy-agent": "^5.0.0",
"qs": "6.11.0", "qs": "6.11.0",

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
@@ -162,7 +162,9 @@ class RemoteAuthoritiesImpl { @@ -166,7 +166,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}`,
@@ -174,7 +174,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
+ `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=';`, // 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:;', 'worker-src \'self\' data: blob:;',
@@ -417,3 +421,70 @@ export class WebClientServer { @@ -417,3 +421,70 @@ export class WebClientServer {
return void res.end(data); return void res.end(data);
} }
@@ -250,7 +250,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -32,6 +32,7 @@ export type ExtensionVirtualWorkspaceSup @@ -56,6 +56,7 @@ export type ExtensionVirtualWorkspaceSup
export interface IProductConfiguration { export interface IProductConfiguration {
readonly codeServerVersion?: string readonly codeServerVersion?: string
@@ -290,7 +290,7 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
-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';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -75,7 +74,7 @@ export abstract class AbstractExtensionR @@ -75,7 +74,7 @@ export abstract class AbstractExtensionR
public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined { public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined {
if (this._extensionGalleryResourceUrlTemplate) { if (this._extensionGalleryResourceUrlTemplate) {

View File

@@ -8,7 +8,7 @@ To test:
2. Open code-server 2. Open code-server
3. Open terminal 3. Open terminal
4. Open another code-server window 4. Open another code-server window
5. Run code-server with a file or directory argument 5. Run node ./out/node/entry.js with a file or directory argument
The file or directory should only open from the instance attached to that The file or directory should only open from the instance attached to that
terminal. terminal.

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
@@ -237,6 +237,10 @@ export class Extension implements IExten @@ -243,6 +243,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;
} }
@@ -18,14 +18,3 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
if (!this.local.preRelease && this.gallery.properties.isPreReleaseVersion) { if (!this.local.preRelease && this.gallery.properties.isPreReleaseVersion) {
return false; return false;
} }
@@ -1237,6 +1241,10 @@ export class ExtensionsWorkbenchService
// Skip if check updates only for builtin extensions and current extension is not builtin.
continue;
}
+ if (installed.type !== ExtensionType.User) {
+ // Never update builtin extensions.
+ continue;
+ }
if (installed.isBuiltin && (!installed.local?.identifier.uuid || (!isWeb && this.productService.quality === 'stable'))) {
// Skip checking updates for a builtin extension if it does not has Marketplace identifier or the current product is VS Code Desktop stable.
continue;

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
@@ -271,6 +271,11 @@ export interface IWorkbenchConstructionO @@ -260,6 +260,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly userDataPath?: string readonly userDataPath?: string
@@ -23,7 +23,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+ +
//#endregion //#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts --- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
} }
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -87,6 +92,13 @@ export class BrowserWorkbenchEnvironment @@ -101,6 +106,13 @@ export class BrowserWorkbenchEnvironment
return this.options.userDataPath; return this.options.userDataPath;
} }
@@ -92,10 +92,10 @@ Index: code-server/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,11 @@ import { Event } from 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey } 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, 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 } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, 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 } 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, DEFAULT_EDITOR_ASSOCIATION } 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';
@@ -121,7 +121,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,
@@ -202,6 +202,9 @@ export class WorkbenchContextKeysHandler @@ -205,6 +205,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));
@@ -143,8 +143,8 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions
+import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; +import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
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/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables';
@@ -483,13 +483,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo @@ -485,13 +485,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
id: DOWNLOAD_COMMAND_ID, id: DOWNLOAD_COMMAND_ID,
title: DOWNLOAD_LABEL title: DOWNLOAD_LABEL
}, },
@@ -172,9 +172,9 @@ 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
@@ -32,6 +32,8 @@ export const IsFullscreenContext = new R @@ -35,6 +35,8 @@ export const HasWebFileSystemAccess = ne
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access) 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

@@ -14,12 +14,14 @@ We can remove this once upstream supports all language packs.
one but is worse because it does not handle non-existent or empty files. one but is worse because it does not handle non-existent or empty files.
7. Replace some caching and Node requires because code-server does not restart 7. Replace some caching and Node requires because code-server does not restart
when changing the language unlike native Code. when changing the language unlike native Code.
8. Make language extensions installable like normal rather than using the
special set/clear language actions.
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts 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
@@ -216,6 +216,9 @@ export async function setupServerService @@ -233,6 +233,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);
@@ -42,7 +44,7 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
export const LANGUAGE_DEFAULT = 'en'; export const LANGUAGE_DEFAULT = 'en';
let _isWindows = false; let _isWindows = false;
@@ -83,17 +81,19 @@ if (typeof navigator === 'object' && !is @@ -90,17 +88,21 @@ if (typeof navigator === 'object' && !is
_isMobile = _userAgent?.indexOf('Mobi') >= 0; _isMobile = _userAgent?.indexOf('Mobi') >= 0;
_isWeb = true; _isWeb = true;
@@ -56,16 +58,18 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
- -
- _locale = configuredLocale || LANGUAGE_DEFAULT; - _locale = configuredLocale || LANGUAGE_DEFAULT;
+ _locale = LANGUAGE_DEFAULT; + _locale = LANGUAGE_DEFAULT;
_language = _locale; _language = _locale;
_platformLocale = navigator.language;
+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration'); + const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
+ const rawNlsConfig = el && el.getAttribute('data-settings'); + const rawNlsConfig = el && el.getAttribute('data-settings');
+ if (rawNlsConfig) { + if (rawNlsConfig) {
+ try { + try {
+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); + const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
+ const resolved = nlsConfig.availableLanguages['*'];
+ _locale = nlsConfig.locale; + _locale = nlsConfig.locale;
+ _platformLocale = nlsConfig.osLocale;
+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
+ _translationsConfigFile = nlsConfig._translationsConfigFile; + _translationsConfigFile = nlsConfig._translationsConfigFile;
+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT;
+ } catch (error) { /* Oh well. */ } + } catch (error) { /* Oh well. */ }
+ } + }
} }
@@ -125,7 +129,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentServ
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts --- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts
+++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts +++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
@@ -110,7 +110,7 @@ export abstract class AbstractNativeEnvi @@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvi
return URI.file(join(vscodePortable, 'argv.json')); return URI.file(join(vscodePortable, 'argv.json'));
} }
@@ -138,7 +142,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts --- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
+++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts +++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
@@ -30,6 +30,12 @@ export function getNLSConfiguration(lang @@ -32,6 +32,12 @@ export function getNLSConfiguration(lang
if (InternalNLSConfiguration.is(value)) { if (InternalNLSConfiguration.is(value)) {
value._languagePackSupport = true; value._languagePackSupport = true;
} }
@@ -151,7 +155,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
return value; return value;
}); });
_cache.set(key, result); _cache.set(key, result);
@@ -44,3 +50,43 @@ export namespace InternalNLSConfiguratio @@ -46,3 +52,43 @@ export namespace InternalNLSConfiguratio
return candidate && typeof candidate._languagePackId === 'string'; return candidate && typeof candidate._languagePackId === 'string';
} }
} }
@@ -248,6 +252,15 @@ Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts --- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts +++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
@@ -52,7 +52,7 @@ import 'vs/workbench/services/dialogs/br
import 'vs/workbench/services/host/browser/browserHostService';
import 'vs/workbench/services/lifecycle/browser/lifecycleService';
import 'vs/workbench/services/clipboard/browser/clipboardService';
-import 'vs/workbench/services/localization/browser/localeService';
+import 'vs/workbench/services/localization/electron-sandbox/localeService';
import 'vs/workbench/services/path/browser/pathService';
import 'vs/workbench/services/themes/browser/browserHostColorSchemeService';
import 'vs/workbench/services/encryption/browser/encryptionService';
@@ -119,8 +119,9 @@ import 'vs/workbench/contrib/logs/browse @@ -119,8 +119,9 @@ import 'vs/workbench/contrib/logs/browse
// Explorer // Explorer
import 'vs/workbench/contrib/files/browser/files.web.contribution'; import 'vs/workbench/contrib/files/browser/files.web.contribution';
@@ -264,9 +277,9 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts --- code-server.orig/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
+++ code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts +++ code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
@@ -6,18 +6,24 @@ @@ -5,18 +5,24 @@
import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Language } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
@@ -289,8 +302,8 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack
+ this.languagePackService = ProxyChannel.toService<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks')) + this.languagePackService = ProxyChannel.toService<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks'))
} }
async getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined> { async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
@@ -73,6 +79,6 @@ export class WebLanguagePacksService ext @@ -72,6 +78,6 @@ export class WebLanguagePacksService ext
// Web doesn't have a concept of language packs, so we just return an empty array // Web doesn't have a concept of language packs, so we just return an empty array
getInstalledLanguages(): Promise<ILanguagePackItem[]> { getInstalledLanguages(): Promise<ILanguagePackItem[]> {
@@ -298,11 +311,11 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack
+ return this.languagePackService.getInstalledLanguages() + return this.languagePackService.getInstalledLanguages()
} }
} }
Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts --- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts +++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
@@ -41,7 +41,8 @@ export class NativeLocaleService impleme @@ -51,7 +51,8 @@ class NativeLocaleService implements ILo
@IProductService private readonly productService: IProductService @IProductService private readonly productService: IProductService
) { } ) { }
@@ -312,7 +325,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-san
try { try {
const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' }); const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' });
@@ -68,9 +69,6 @@ export class NativeLocaleService impleme @@ -78,9 +79,6 @@ class NativeLocaleService implements ILo
} }
private async writeLocaleValue(locale: string | undefined): Promise<boolean> { private async writeLocaleValue(locale: string | undefined): Promise<boolean> {
@@ -322,3 +335,70 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-san
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true); await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
return true; return true;
} }
Index: code-server/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
@@ -335,9 +335,6 @@ export abstract class AbstractInstallAct
if (this.extension.isBuiltin) {
return;
}
- if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) {
this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion;
this.updateLabel();
@@ -715,7 +712,7 @@ export abstract class InstallInOtherServ
}
if (isLanguagePackExtension(this.extension.local.manifest)) {
- return true;
+ return false;
}
// Prefers to run on UI
@@ -1803,17 +1800,6 @@ export class SetLanguageAction extends E
update(): void {
this.enabled = false;
this.class = SetLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language === getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = SetLanguageAction.EnabledClass;
}
override async run(): Promise<any> {
@@ -1830,7 +1816,6 @@ export class ClearLanguageAction extends
private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`;
constructor(
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILocaleService private readonly localeService: ILocaleService,
) {
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
@@ -1840,17 +1825,6 @@ export class ClearLanguageAction extends
update(): void {
this.enabled = false;
this.class = ClearLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = ClearLanguageAction.EnabledClass;
}
override async run(): Promise<any> {

View File

@@ -10,7 +10,7 @@ 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
@@ -62,7 +62,7 @@ import { GettingStartedIndexList } from @@ -60,7 +60,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';
@@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { 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';
@@ -758,6 +758,72 @@ export class GettingStartedPage extends @@ -796,6 +796,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"))
); );
@@ -72,7 +72,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ 'align-items: center', + 'align-items: center',
+ ].join(';'), + ].join(';'),
+ }, 'Get started ', $('span', { + }, 'Get started ', $('span', {
+ class: Codicon.arrowRight.classNames, + class: ThemeIcon.asClassName(Codicon.arrowRight),
+ style: [ + style: [
+ 'color: white', + 'color: white',
+ 'margin-left: 8px', + 'margin-left: 8px',
@@ -87,16 +87,56 @@ 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', {},);
@@ -775,13 +841,23 @@ export class GettingStartedPage extends
@@ -814,13 +880,23 @@ export class GettingStartedPage extends
const layoutLists = () => { const layoutLists = () => {
if (gettingStartedList.itemCount) { if (gettingStartedList.itemCount) {
this.container.classList.remove('noWalkthroughs'); this.container.classList.remove('noWalkthroughs');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement()); - 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('noWalkthroughs');
- 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);
};
@@ -828,13 +904,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()); - reset(rightColumn, gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) { + if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), gettingStartedList.getDomElement()); + reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), gettingStartedList.getDomElement());
@@ -105,20 +145,9 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement()); + reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, gettingStartedList.getDomElement()); + reset(rightColumn, gettingStartedList.getDomElement());
+ } + }
+
recentList.setLimit(5);
}
else {
this.container.classList.add('noWalkthroughs');
- reset(leftColumn, startList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement());
+ }
reset(rightColumn, recentList.getDomElement());
recentList.setLimit(10);
} }
setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50);
};
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
@@ -143,7 +172,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
@@ -276,6 +276,11 @@ export interface IWorkbenchConstructionO @@ -265,6 +265,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly isEnabledFileDownloads?: boolean readonly isEnabledFileDownloads?: boolean
@@ -154,7 +183,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+ +
//#endregion //#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts --- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -171,7 +200,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
} }
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -99,6 +104,13 @@ export class BrowserWorkbenchEnvironment @@ -113,6 +118,13 @@ export class BrowserWorkbenchEnvironment
return this.options.isEnabledFileDownloads; return this.options.isEnabledFileDownloads;
} }
@@ -201,7 +230,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
'auth'?: string 'auth'?: string
'disable-file-downloads'?: boolean; 'disable-file-downloads'?: boolean;
'locale'?: string 'locale'?: string
+ 'disable-getting-started-override'?: boolean; + 'disable-getting-started-override'?: boolean,
/* ----- server setup ----- */ /* ----- server setup ----- */
@@ -223,14 +252,14 @@ Index: code-server/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,7 +7,7 @@ import { Event } from 'vs/base/common/ev @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey } 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, 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 } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, 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 } 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, DEFAULT_EDITOR_ASSOCIATION } 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';
@@ -204,6 +204,7 @@ export class WorkbenchContextKeysHandler @@ -207,6 +207,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)
@@ -242,8 +271,8 @@ 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
@@ -33,6 +33,7 @@ export const IsFullscreenContext = new R @@ -36,6 +36,7 @@ export const HasWebFileSystemAccess = ne
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access) 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);
+export const IsEnabledCoderGettingStarted = new RawContextKey<boolean>('isEnabledCoderGettingStarted', true, true); +export const IsEnabledCoderGettingStarted = new RawContextKey<boolean>('isEnabledCoderGettingStarted', true, true);

View File

@@ -38,7 +38,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.main.ts
-const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History'); -const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History');
-const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine'); -const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
-args['user-data-dir'] = USER_DATA_PATH; -args['user-data-dir'] = USER_DATA_PATH;
-const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath); -const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath);
-const BUILTIN_EXTENSIONS_FOLDER_PATH = join(APP_ROOT, 'extensions'); -const BUILTIN_EXTENSIONS_FOLDER_PATH = join(APP_ROOT, 'extensions');
-args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH; -args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
-args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions'); -args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
@@ -58,7 +58,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.main.ts
+ const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History'); + const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History');
+ const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine'); + const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
+ args['user-data-dir'] = USER_DATA_PATH; + args['user-data-dir'] = USER_DATA_PATH;
+ const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath); + const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath);
+ const BUILTIN_EXTENSIONS_FOLDER_PATH = args['builtin-extensions-dir'] || join(APP_ROOT, 'extensions'); + const BUILTIN_EXTENSIONS_FOLDER_PATH = args['builtin-extensions-dir'] || join(APP_ROOT, 'extensions');
+ args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH; + args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
+ args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions'); + args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
@@ -95,7 +95,7 @@ Index: code-server/lib/vscode/src/vs/base/common/processes.ts
--- code-server.orig/lib/vscode/src/vs/base/common/processes.ts --- code-server.orig/lib/vscode/src/vs/base/common/processes.ts
+++ code-server/lib/vscode/src/vs/base/common/processes.ts +++ code-server/lib/vscode/src/vs/base/common/processes.ts
@@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme @@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme
/^VSCODE_(?!SHELL_LOGIN).+$/, /^VSCODE_(?!(PORTABLE|SHELL_LOGIN)).+$/,
/^SNAP(|_.*)$/, /^SNAP(|_.*)$/,
/^GDK_PIXBUF_.+$/, /^GDK_PIXBUF_.+$/,
+ /^CODE_SERVER_.+$/, + /^CODE_SERVER_.+$/,
@@ -107,7 +107,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandl
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts --- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
@@ -143,8 +143,11 @@ export class BrowserDialogHandler implem @@ -76,8 +76,11 @@ export class BrowserDialogHandler extend
async about(): Promise<void> { async about(): Promise<void> {
const detailString = (useAgo: boolean): string => { const detailString = (useAgo: boolean): string => {
@@ -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';
@@ -117,6 +118,9 @@ export class BrowserMain extends Disposa @@ -119,6 +120,9 @@ export class BrowserMain extends Disposa
// Startup // Startup
const instantiationService = workbench.startup(); const instantiationService = workbench.startup();
@@ -198,7 +198,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -31,6 +31,8 @@ export type ExtensionVirtualWorkspaceSup @@ -55,6 +55,8 @@ export type ExtensionVirtualWorkspaceSup
}; };
export interface IProductConfiguration { export interface IProductConfiguration {

View File

@@ -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
@@ -266,6 +266,11 @@ export interface IWorkbenchConstructionO @@ -255,6 +255,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly configurationDefaults?: Record<string, any>; readonly configurationDefaults?: Record<string, any>;
@@ -43,12 +43,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+ +
//#endregion //#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts --- code-server.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
@@ -78,7 +78,14 @@ export class BrowserWorkbenchEnvironment @@ -92,7 +92,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

@@ -8,7 +8,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -34,6 +34,7 @@ export interface IProductConfiguration { @@ -58,6 +58,7 @@ export interface IProductConfiguration {
readonly codeServerVersion?: string readonly codeServerVersion?: string
readonly rootEndpoint?: string readonly rootEndpoint?: string
readonly updateEndpoint?: string readonly updateEndpoint?: string

View File

@@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts --- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts
+++ code-server/lib/vscode/src/vs/platform/product/common/product.ts +++ code-server/lib/vscode/src/vs/platform/product/common/product.ts
@@ -53,6 +53,16 @@ else if (typeof require?.__$__nodeRequir @@ -47,6 +47,16 @@ else if (globalThis._VSCODE_PRODUCT_JSON
version: pkg.version version: pkg.version
}); });
} }
@@ -75,7 +75,7 @@ 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 { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -60,7 +59,7 @@ export abstract class AbstractExtensionR @@ -60,7 +59,7 @@ export abstract class AbstractExtensionR
private readonly _environmentService: IEnvironmentService, private readonly _environmentService: IEnvironmentService,

View File

@@ -6,24 +6,11 @@ https://github.com/microsoft/vscode-extension-samples/tree/ddae6c0c9ff203b4ed6f6
We also override isProposedApiEnabled in case an extension does not declare the We also override isProposedApiEnabled in case an extension does not declare the
APIs it needs correctly (the Jupyter extension had this issue). APIs it needs correctly (the Jupyter extension had this issue).
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
@@ -1482,7 +1482,7 @@ class ProposedApiController {
this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id)));
- this._envEnablesProposedApiForAll =
+ this._envEnablesProposedApiForAll = true ||
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts --- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts +++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
@@ -364,10 +364,7 @@ function extensionDescriptionArrayToMap( @@ -260,10 +260,7 @@ function extensionDescriptionArrayToMap(
} }
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
@@ -35,3 +22,16 @@ Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extens
} }
export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void { export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
@@ -24,7 +24,7 @@ export class ExtensionsProposedApi {
this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id)));
- this._envEnablesProposedApiForAll =
+ this._envEnablesProposedApiForAll = true ||
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID

View File

@@ -30,7 +30,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -35,6 +35,7 @@ export interface IProductConfiguration { @@ -59,6 +59,7 @@ export interface IProductConfiguration {
readonly rootEndpoint?: string readonly rootEndpoint?: string
readonly updateEndpoint?: string readonly updateEndpoint?: string
readonly logoutEndpoint?: string readonly logoutEndpoint?: string
@@ -42,31 +42,31 @@ 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
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/comm @@ -11,7 +11,7 @@ import { StopWatch } from 'vs/base/commo
import { RemoteAuthorities } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService'; import { IProductService } from 'vs/platform/product/common/productService';
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } 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'; import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts';
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
@@ -23,7 +23,7 @@ export class RemoteAuthorityResolverServ @@ -29,7 +29,7 @@ export class RemoteAuthorityResolverServ
private readonly _connectionToken: Promise<string> | string | undefined; constructor(
private readonly _connectionTokens: Map<string, string>; connectionToken: Promise<string> | string | undefined,
resourceUriProvider: ((uri: URI) => URI) | undefined,
- constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) { - @IProductService productService: IProductService,
+ constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, private readonly proxyEndpointTemplate?: string) { + @IProductService private readonly productService: IProductService,
@ILogService private readonly _logService: ILogService,
) {
super(); super();
this._connectionToken = connectionToken; @@ -75,9 +75,14 @@ export class RemoteAuthorityResolverServ
this._connectionTokens = new Map<string, string>();
@@ -61,9 +61,14 @@ export class RemoteAuthorityResolverServ
private async _doResolveAuthority(authority: string): Promise<ResolverResult> {
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}`);
this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`);
+ let options: ResolvedOptions | undefined; + let options: ResolvedOptions | undefined;
+ if (this.proxyEndpointTemplate) { + if (this.productService.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href); + const proxyUrl = new URL(this.productService.proxyEndpointTemplate, window.location.href);
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }} + options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
+ } + }
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80); const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
@@ -88,24 +88,11 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
embedderIdentifier: 'server-distro', embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery, extensionsGallery: this._productService.extensionsGallery,
}, },
Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
@@ -248,7 +248,7 @@ export class BrowserMain extends Disposa
// Remote
const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName);
- const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider);
+ const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider, this.configuration.productConfiguration?.proxyEndpointTemplate);
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
// Signing
Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts --- code-server.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
@@ -381,7 +381,7 @@ export async function createTerminalEnvi @@ -383,7 +383,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
@@ -170,7 +157,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExpl
=================================================================== ===================================================================
--- 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 @@ -73,7 +73,7 @@ export class ForwardedPortsView extends
this.contextKeyListener = undefined; this.contextKeyListener = undefined;
} }

View File

@@ -6,7 +6,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -36,6 +36,10 @@ export interface IProductConfiguration { @@ -60,6 +60,10 @@ export interface IProductConfiguration {
readonly updateEndpoint?: string readonly updateEndpoint?: string
readonly logoutEndpoint?: string readonly logoutEndpoint?: string
readonly proxyEndpointTemplate?: string readonly proxyEndpointTemplate?: string

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
@@ -192,8 +192,7 @@ function packageTask(type, platform, arc @@ -191,8 +191,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) => {
@@ -232,9 +231,9 @@ function packageTask(type, platform, arc @@ -231,9 +230,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 });

View File

@@ -1,7 +1,7 @@
Add support for telemetry endpoint Add support for telemetry endpoint
To test: To test:
1. Create a RequestBin - https://requestbin.io/ 1. Create a mock API using [RequestBin](https://requestbin.io/) or [Beeceptor](https://beeceptor.com/)
2. Run code-server with `CS_TELEMETRY_URL` set: 2. Run code-server with `CS_TELEMETRY_URL` set:
i.e. `CS_TELEMETRY_URL="https://requestbin.io/1ebub9z1" ./code-server-<version>-macos-amd64/bin/code-server` i.e. `CS_TELEMETRY_URL="https://requestbin.io/1ebub9z1" ./code-server-<version>-macos-amd64/bin/code-server`
NOTE: it has to be a production build. NOTE: it has to be a production build.
@@ -12,16 +12,16 @@ 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
@@ -71,6 +71,7 @@ import { IExtensionsScannerService } fro @@ -69,6 +69,7 @@ import { IExtensionsScannerService } fro
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
+import { TelemetryClient } from "vs/server/node/telemetryClient"; +import { TelemetryClient } from "vs/server/node/telemetryClient";
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';
@@ -139,10 +140,13 @@ export async function setupServerService @@ -150,10 +151,13 @@ export async function setupServerService
const machineId = await getMachineId(); 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) {

View File

@@ -93,7 +93,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts --- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -33,6 +33,7 @@ export type ExtensionVirtualWorkspaceSup @@ -57,6 +57,7 @@ export type ExtensionVirtualWorkspaceSup
export interface IProductConfiguration { export interface IProductConfiguration {
readonly codeServerVersion?: string readonly codeServerVersion?: string
readonly rootEndpoint?: string readonly rootEndpoint?: string

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
@@ -210,7 +210,7 @@ export class BrowserWorkbenchEnvironment @@ -215,7 +215,7 @@ export class BrowserWorkbenchEnvironment
@memoize @memoize
get webviewExternalEndpoint(): string { get webviewExternalEndpoint(): string {
@@ -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-lC8sxUeeYqUtmkCpPt/OX/HQdE0JbHG1Z3dzrilsRU0=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> - content="default-src 'none'; script-src 'sha256-4A7BC3A+gc8e1t5I0rfDNtBiGlA0/GTojKdfdDHAWYw=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-/9/YQU12wvTeVXCsIGB4shLwdWrMceCpKojfkloNjPU=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> + content="default-src 'none'; script-src 'sha256-8I6aYsGyWES1C043+SeEOyeZlC0Y0HN5WevEDh97c1Y=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming --> <!-- Disable pinch zooming -->
<meta name="viewport" <meta name="viewport"
@@ -331,6 +331,12 @@ @@ -323,6 +323,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
@@ -330,6 +330,12 @@ @@ -322,6 +322,12 @@
const hostname = location.hostname; const hostname = location.hostname;

View File

@@ -6,10 +6,16 @@
"matchUpdateTypes": ["minor", "patch", "digest"], "matchUpdateTypes": ["minor", "patch", "digest"],
"automerge": true, "automerge": true,
"groupName": "Minor dependency updates" "groupName": "Minor dependency updates"
},
{
"matchDepTypes": ["peerDependencies"],
"matchUpdateTypes": ["minor", "patch", "digest"],
"automerge": true,
"groupName": "Peer dependency updates"
} }
], ],
"vulnerabilityAlerts": { "vulnerabilityAlerts": {
"enabled": "true" "enabled": "true"
}, },
"ignoreDeps": ["express"] "ignoreDeps": ["express", "ansi-regex", "env-paths", "limiter", "node", "prettier"]
} }

View File

@@ -10,7 +10,7 @@
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;" content="style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/> />
<title>{{APP_NAME}} login</title> <title>{{I18N_LOGIN_TITLE}}</title>
<link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" /> <link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" />
<link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" /> <link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" />
<link rel="manifest" href="{{BASE}}/manifest.json" crossorigin="use-credentials" /> <link rel="manifest" href="{{BASE}}/manifest.json" crossorigin="use-credentials" />
@@ -25,7 +25,7 @@
<div class="card-box"> <div class="card-box">
<div class="header"> <div class="header">
<h1 class="main">{{WELCOME_TEXT}}</h1> <h1 class="main">{{WELCOME_TEXT}}</h1>
<div class="sub">Please log in below. {{PASSWORD_MSG}}</div> <div class="sub">{{I18N_LOGIN_BELOW}} {{PASSWORD_MSG}}</div>
</div> </div>
<div class="content"> <div class="content">
<form class="login-form" method="post"> <form class="login-form" method="post">
@@ -38,11 +38,11 @@
autofocus autofocus
class="password" class="password"
type="password" type="password"
placeholder="PASSWORD" placeholder="{{I18N_PASSWORD_PLACEHOLDER}}"
name="password" name="password"
autocomplete="current-password" autocomplete="current-password"
/> />
<input class="submit -button" value="SUBMIT" type="submit" /> <input class="submit -button" value="{{I18N_SUBMIT}}" type="submit" />
</div> </div>
{{ERROR}} {{ERROR}}
</form> </form>

View File

@@ -4,6 +4,7 @@ export enum HttpCode {
NotFound = 404, NotFound = 404,
BadRequest = 400, BadRequest = 400,
Unauthorized = 401, Unauthorized = 401,
Forbidden = 403,
LargePayload = 413, LargePayload = 413,
ServerError = 500, ServerError = 500,
} }

View File

@@ -3,7 +3,15 @@ import { promises as fs } from "fs"
import { load } from "js-yaml" import { load } from "js-yaml"
import * as os from "os" import * as os from "os"
import * as path from "path" import * as path from "path"
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util" import {
canConnect,
generateCertificate,
generatePassword,
humanPath,
paths,
isNodeJSErrnoException,
splitOnFirstEquals,
} from "./util"
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc") const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
@@ -84,7 +92,6 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"reuse-window"?: boolean "reuse-window"?: boolean
"new-window"?: boolean "new-window"?: boolean
"ignore-last-opened"?: boolean "ignore-last-opened"?: boolean
link?: OptionalString
verbose?: boolean verbose?: boolean
"app-name"?: string "app-name"?: string
"welcome-text"?: string "welcome-text"?: string
@@ -180,7 +187,14 @@ export const options: Options<Required<UserProvidedArgs>> = {
enable: { type: "string[]" }, enable: { type: "string[]" },
help: { type: "boolean", short: "h", description: "Show this output." }, help: { type: "boolean", short: "h", description: "Show this output." },
json: { type: "boolean" }, json: { type: "boolean" },
locale: { type: "string" }, // The preferred way to set the locale is via the UI. locale: {
// The preferred way to set the locale is via the UI.
type: "string",
description: `
Set vscode display language and language to show on the login page, more info see
https://en.wikipedia.org/wiki/IETF_language_tag
`,
},
open: { type: "boolean", description: "Open in browser on startup. Does not work remotely." }, open: { type: "boolean", description: "Open in browser on startup. Does not work remotely." },
"bind-addr": { "bind-addr": {
@@ -255,15 +269,6 @@ export const options: Options<Required<UserProvidedArgs>> = {
short: "w", short: "w",
description: "Text to show on login page", description: "Text to show on login page",
}, },
link: {
type: OptionalString,
description: `
Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.
`,
deprecated: true,
},
} }
export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => { export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => {
@@ -295,19 +300,6 @@ export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedAr
}) })
} }
export function splitOnFirstEquals(str: string): string[] {
// we use regex instead of "=" to ensure we split at the first
// "=" and return the following substring with it
// important for the hashed-password which looks like this
// $argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY
// 2 means return two items
// Source: https://stackoverflow.com/a/4607799/3015595
// We use the ? to say the the substr after the = is optional
const split = str.split(/=(.+)?/, 2)
return split
}
/** /**
* Parse arguments into UserProvidedArgs. This should not go beyond checking * Parse arguments into UserProvidedArgs. This should not go beyond checking
* that arguments are valid types and have values when required. * that arguments are valid types and have values when required.
@@ -540,17 +532,6 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args.host = addr.host args.host = addr.host
args.port = addr.port args.port = addr.port
// If we're being exposed to the cloud, we listen on a random address and
// disable auth.
if (args.link) {
args.host = "localhost"
args.port = 0
args.socket = undefined
args["socket-mode"] = undefined
args.cert = undefined
args.auth = AuthType.None
}
if (args.cert && !args.cert.value) { if (args.cert && !args.cert.value) {
const { cert, certKey } = await generateCertificate(args["cert-host"] || "localhost") const { cert, certKey } = await generateCertificate(args["cert-host"] || "localhost")
args.cert = { args.cert = {
@@ -590,6 +571,9 @@ 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) args["proxy-domain"] = Array.from(proxyDomains)
if (args["proxy-domain"].length > 0 && !process.env.VSCODE_PROXY_URI) {
process.env.VSCODE_PROXY_URI = `{{port}}.${args["proxy-domain"][0]}`
}
if (typeof args._ === "undefined") { if (typeof args._ === "undefined") {
args._ = [] args._ = []

View File

@@ -1,43 +0,0 @@
import { logger } from "@coder/logger"
import { spawn } from "child_process"
import path from "path"
import split2 from "split2"
// https://github.com/coder/coder-cloud
const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent")
function runAgent(...args: string[]): Promise<void> {
logger.debug(`running agent with ${args}`)
const agent = spawn(coderCloudAgent, args, {
stdio: ["inherit", "inherit", "pipe"],
})
agent.stderr.pipe(split2()).on("data", (line) => {
line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "")
logger.info(line)
})
return new Promise((res, rej) => {
agent.on("error", rej)
agent.on("close", (code) => {
if (code !== 0) {
rej({
message: `--link agent exited with ${code}`,
})
return
}
res()
})
})
}
export function coderCloudBind(address: URL | string, serverName = ""): Promise<void> {
if (typeof address === "string") {
throw new Error("Cannot link socket paths")
}
// Address needs to be in hostname:port format without the protocol.
return runAgent("bind", `--code-server-addr=${address.host}`, serverName)
}

View File

@@ -12,7 +12,15 @@ import { version as codeServerVersion } from "./constants"
import { Heart } from "./heart" import { Heart } from "./heart"
import { CoderSettings, SettingsProvider } from "./settings" import { CoderSettings, SettingsProvider } from "./settings"
import { UpdateProvider } from "./update" import { UpdateProvider } from "./update"
import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString, escapeHtml, escapeJSON } from "./util" import {
getPasswordMethod,
IsCookieValidArgs,
isCookieValid,
sanitizeString,
escapeHtml,
escapeJSON,
splitOnFirstEquals,
} from "./util"
/** /**
* Base options included on every page. * Base options included on every page.
@@ -308,3 +316,82 @@ export const getCookieOptions = (req: express.Request): express.CookieOptions =>
export const self = (req: express.Request): string => { export const self = (req: express.Request): string => {
return normalize(`${req.baseUrl}${req.originalUrl.endsWith("/") ? "/" : ""}`, true) return normalize(`${req.baseUrl}${req.originalUrl.endsWith("/") ? "/" : ""}`, true)
} }
function getFirstHeader(req: http.IncomingMessage, headerName: string): string | undefined {
const val = req.headers[headerName]
return Array.isArray(val) ? val[0] : val
}
/**
* Throw a forbidden error if origin checks fail. Call `next` if provided.
*/
export function ensureOrigin(req: express.Request, _?: express.Response, next?: express.NextFunction): void {
try {
authenticateOrigin(req)
if (next) {
next()
}
} catch (error) {
logger.debug(`${error instanceof Error ? error.message : error}; blocking request to ${req.originalUrl}`)
throw new HttpError("Forbidden", HttpCode.Forbidden)
}
}
/**
* Authenticate the request origin against the host. Throw if invalid.
*/
export function authenticateOrigin(req: express.Request): void {
// A missing origin probably means the source is non-browser. Not sure we
// have a use case for this but let it through.
const originRaw = getFirstHeader(req, "origin")
if (!originRaw) {
return
}
let origin: string
try {
origin = new URL(originRaw).host.trim().toLowerCase()
} catch (error) {
throw new Error(`unable to parse malformed origin "${originRaw}"`)
}
const host = getHost(req)
if (typeof host === "undefined") {
// A missing host likely means the reverse proxy has not been configured to
// forward the host which means we cannot perform the check. Emit an error
// so an admin can fix the issue.
logger.error("No host headers found")
logger.error("Are you behind a reverse proxy that does not forward the host?")
throw new Error("no host headers found")
}
if (host !== origin) {
throw new Error(`host "${host}" does not match origin "${origin}"`)
}
}
/**
* Get the host from headers. It will be trimmed and lowercased.
*/
function getHost(req: express.Request): string | undefined {
// Honor Forwarded if present.
const forwardedRaw = getFirstHeader(req, "forwarded")
if (forwardedRaw) {
const parts = forwardedRaw.split(/[;,]/)
for (let i = 0; i < parts.length; ++i) {
const [key, value] = splitOnFirstEquals(parts[i])
if (key.trim().toLowerCase() === "host" && value) {
return value.trim().toLowerCase()
}
}
}
// Honor X-Forwarded-Host if present.
const xHost = getFirstHeader(req, "x-forwarded-host")
if (xHost) {
return xHost.trim().toLowerCase()
}
const host = getFirstHeader(req, "host")
return host ? host.trim().toLowerCase() : undefined
}

24
src/node/i18n/index.ts Normal file
View File

@@ -0,0 +1,24 @@
import i18next, { init } from "i18next"
import * as en from "./locales/en.json"
import * as zhCn from "./locales/zh-cn.json"
import * as th from "./locales/th.json"
init({
lng: "en",
fallbackLng: "en", // language to use if translations in user language are not available.
returnNull: false,
lowerCaseLng: true,
debug: process.env.NODE_ENV === "development",
resources: {
en: {
translation: en,
},
"zh-cn": {
translation: zhCn,
},
th: {
translation: th,
},
},
})
export default i18next

View File

@@ -0,0 +1,13 @@
{
"LOGIN_TITLE": "{{app}} login",
"LOGIN_BELOW": "Please log in below.",
"WELCOME": "Welcome to {{app}}",
"LOGIN_PASSWORD": "Check the config file at {{configFile}} for the password.",
"LOGIN_USING_ENV_PASSWORD": "Password was set from $PASSWORD.",
"LOGIN_USING_HASHED_PASSWORD": "Password was set from $HASHED_PASSWORD.",
"SUBMIT": "SUBMIT",
"PASSWORD_PLACEHOLDER": "PASSWORD",
"LOGIN_RATE_LIMIT": "Login rate limited!",
"MISS_PASSWORD": "Missing password",
"INCORRECT_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

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

@@ -6,7 +6,6 @@ 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 { coderCloudBind } from "./coder_cloud"
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 { humanPath, isDirectory, loadAMDModule, open } from "./util"
@@ -127,11 +126,7 @@ export const runCodeServer = async (
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 ${humanPath(os.homedir(), args.config)}`)
logger.info( logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
`${protocol.toUpperCase()} server listening on ${serverAddress.toString()} ${
args.link ? "(randomized by --link)" : ""
}`,
)
if (args.auth === AuthType.Password) { if (args.auth === AuthType.Password) {
logger.info(" - Authentication is enabled") logger.info(" - Authentication is enabled")
@@ -143,13 +138,13 @@ export const runCodeServer = async (
logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`) logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`)
} }
} else { } else {
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`) 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: ${humanPath(os.homedir(), args.cert.value)}`)
} else { } else {
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`) logger.info(" - Not serving HTTPS")
} }
if (args["proxy-domain"].length > 0) { if (args["proxy-domain"].length > 0) {
@@ -157,11 +152,6 @@ export const runCodeServer = async (
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`)) args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
} }
if (args.link) {
await coderCloudBind(serverAddress, args.link.value)
logger.info(" - Connected to cloud agent")
}
if (args.enable && args.enable.length > 0) { if (args.enable && args.enable.length > 0) {
logger.info("Enabling the following experimental features:") logger.info("Enabling the following experimental features:")
args.enable.forEach((feature) => { args.enable.forEach((feature) => {

View File

@@ -3,9 +3,19 @@ import { HttpCode } from "../common/http"
export const proxy = proxyServer.createProxyServer({}) export const proxy = proxyServer.createProxyServer({})
// The error handler catches when the proxy fails to connect (for example when
// there is nothing running on the target port).
proxy.on("error", (error, _, res) => { proxy.on("error", (error, _, res) => {
res.writeHead(HttpCode.ServerError) // This could be for either a web socket or a regular request. Despite what
res.end(error.message) // the types say, writeHead() will not exist on web socket requests (nor will
// status() from Express). But writing out the code manually does not work
// for regular requests thus the branching behavior.
if (typeof res.writeHead !== "undefined") {
res.writeHead(HttpCode.ServerError)
res.end(error.message)
} else {
res.end(`HTTP/1.1 ${HttpCode.ServerError} ${error.message}\r\n\r\n`)
}
}) })
// Intercept the response to rewrite absolute redirects against the base path. // Intercept the response to rewrite absolute redirects against the base path.

View File

@@ -1,6 +1,6 @@
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, redirect, self } from "../http" import { 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"
@@ -78,10 +78,8 @@ wsRouter.ws("*", async (req, _, next) => {
if (!port) { if (!port) {
return next() return next()
} }
ensureOrigin(req)
// Must be authenticated to use the proxy.
await ensureAuthenticated(req) await ensureAuthenticated(req)
proxy.ws(req, req.ws, req.head, { proxy.ws(req, req.ws, req.head, {
ignorePath: true, ignorePath: true,
target: `http://0.0.0.0:${port}${req.originalUrl}`, target: `http://0.0.0.0:${port}${req.originalUrl}`,

View File

@@ -63,5 +63,11 @@ export const errorHandler: express.ErrorRequestHandler = async (err, req, res, n
export const wsErrorHandler: express.ErrorRequestHandler = async (err, req, res, next) => { export const wsErrorHandler: express.ErrorRequestHandler = async (err, req, res, next) => {
logger.error(`${err.message} ${err.stack}`) logger.error(`${err.message} ${err.stack}`)
;(req as WebsocketRequest).ws.end() let statusCode = 500
if (errorHasStatusCode(err)) {
statusCode = err.statusCode
} else if (errorHasCode(err) && notFoundCodes.includes(err.code)) {
statusCode = HttpCode.NotFound
}
;(req as WebsocketRequest).ws.end(`HTTP/1.1 ${statusCode} ${err.message}\r\n\r\n`)
} }

View File

@@ -33,7 +33,15 @@ import { CodeServerRouteWrapper } from "./vscode"
export const register = async (app: App, args: DefaultedArgs): Promise<Disposable["dispose"]> => { export const register = async (app: App, args: DefaultedArgs): Promise<Disposable["dispose"]> => {
const heart = new Heart(path.join(paths.data, "heartbeat"), async () => { const heart = new Heart(path.join(paths.data, "heartbeat"), async () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// getConnections appears to not call the callback when there are no more
// connections. Feels like it must be a bug? For now add a timer to make
// sure we eventually resolve.
const timer = setTimeout(() => {
logger.debug("Node failed to respond with connections; assuming zero")
resolve(false)
}, 5000)
app.server.getConnections((error, count) => { app.server.getConnections((error, count) => {
clearTimeout(timer)
if (error) { if (error) {
return reject(error) return reject(error)
} }

View File

@@ -7,12 +7,13 @@ 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 { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
import i18n from "../i18n"
// 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.
export class RateLimiter { export class RateLimiter {
private readonly minuteLimiter = new Limiter(2, "minute") private readonly minuteLimiter = new Limiter({ tokensPerInterval: 2, interval: "minute" })
private readonly hourLimiter = new Limiter(12, "hour") private readonly hourLimiter = new Limiter({ tokensPerInterval: 12, interval: "hour" })
public canTry(): boolean { public canTry(): boolean {
// Note: we must check using >= 1 because technically when there are no tokens left // Note: we must check using >= 1 because technically when there are no tokens left
@@ -28,21 +29,26 @@ export class RateLimiter {
const getRoot = async (req: Request, error?: Error): Promise<string> => { const getRoot = async (req: Request, error?: Error): Promise<string> => {
const content = await fs.readFile(path.join(rootPath, "src/browser/pages/login.html"), "utf8") const content = await fs.readFile(path.join(rootPath, "src/browser/pages/login.html"), "utf8")
const locale = req.args["locale"] || "en"
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"] || `Welcome to ${appName}` const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string)
let passwordMsg = `Check the config file at ${humanPath(os.homedir(), req.args.config)} for the password.` let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: humanPath(os.homedir(), req.args.config) })
if (req.args.usingEnvPassword) { if (req.args.usingEnvPassword) {
passwordMsg = "Password was set from $PASSWORD." passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD")
} else if (req.args.usingEnvHashedPassword) { } else if (req.args.usingEnvHashedPassword) {
passwordMsg = "Password was set from $HASHED_PASSWORD." passwordMsg = i18n.t("LOGIN_USING_HASHED_PASSWORD")
} }
return replaceTemplates( return replaceTemplates(
req, req,
content content
.replace(/{{APP_NAME}}/g, appName) .replace(/{{I18N_LOGIN_TITLE}}/g, i18n.t("LOGIN_TITLE", { app: appName }))
.replace(/{{WELCOME_TEXT}}/g, welcomeText) .replace(/{{WELCOME_TEXT}}/g, welcomeText)
.replace(/{{PASSWORD_MSG}}/g, passwordMsg) .replace(/{{PASSWORD_MSG}}/g, passwordMsg)
.replace(/{{I18N_LOGIN_BELOW}}/g, i18n.t("LOGIN_BELOW"))
.replace(/{{I18N_PASSWORD_PLACEHOLDER}}/g, i18n.t("PASSWORD_PLACEHOLDER"))
.replace(/{{I18N_SUBMIT}}/g, i18n.t("SUBMIT"))
.replace(/{{ERROR}}/, error ? `<div class="error">${escapeHtml(error.message)}</div>` : ""), .replace(/{{ERROR}}/, error ? `<div class="error">${escapeHtml(error.message)}</div>` : ""),
) )
} }
@@ -70,11 +76,11 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
try { try {
// Check to see if they exceeded their login attempts // Check to see if they exceeded their login attempts
if (!limiter.canTry()) { if (!limiter.canTry()) {
throw new Error("Login rate limited!") throw new Error(i18n.t("LOGIN_RATE_LIMIT") as string)
} }
if (!password) { if (!password) {
throw new Error("Missing password") throw new Error(i18n.t("MISS_PASSWORD") as string)
} }
const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) const passwordMethod = getPasswordMethod(hashedPasswordFromArgs)
@@ -108,7 +114,7 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
}), }),
) )
throw new Error("Incorrect password") throw new Error(i18n.t("INCORRECT_PASSWORD") as string)
} catch (error: any) { } catch (error: any) {
const renderedHtml = await getRoot(req, error) const renderedHtml = await getRoot(req, error)
res.send(renderedHtml) res.send(renderedHtml)

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, redirect, self } from "../http" import { 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 => {
@@ -50,6 +50,7 @@ export async function wsProxy(
passthroughPath?: boolean passthroughPath?: boolean
}, },
): Promise<void> { ): Promise<void> {
ensureOrigin(req)
await ensureAuthenticated(req) await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, { _proxy.ws(req, req.ws, req.head, {
ignorePath: true, ignorePath: true,

View File

@@ -7,7 +7,7 @@ import { WebsocketRequest } from "../../../typings/pluginapi"
import { logError } from "../../common/util" import { logError } from "../../common/util"
import { CodeArgs, toCodeArgs } from "../cli" import { CodeArgs, toCodeArgs } from "../cli"
import { isDevMode } from "../constants" import { isDevMode } from "../constants"
import { authenticated, ensureAuthenticated, redirect, replaceTemplates, self } from "../http" import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
import { SocketProxyProvider } from "../socket" import { SocketProxyProvider } from "../socket"
import { isFile, loadAMDModule } from "../util" import { isFile, loadAMDModule } from "../util"
import { Router as WsRouter } from "../wsRouter" import { Router as WsRouter } from "../wsRouter"
@@ -40,6 +40,7 @@ export class CodeServerRouteWrapper {
//#region Route Handlers //#region Route Handlers
private manifest: express.Handler = async (req, res, next) => { private manifest: express.Handler = async (req, res, next) => {
const appName = req.args["app-name"] || "code-server"
res.writeHead(200, { "Content-Type": "application/manifest+json" }) res.writeHead(200, { "Content-Type": "application/manifest+json" })
return res.end( return res.end(
@@ -47,8 +48,8 @@ export class CodeServerRouteWrapper {
req, req,
JSON.stringify( JSON.stringify(
{ {
name: "code-server", name: appName,
short_name: "code-server", short_name: appName,
start_url: ".", start_url: ".",
display: "fullscreen", display: "fullscreen",
description: "Run Code on a remote server.", description: "Run Code on a remote server.",
@@ -173,7 +174,7 @@ export class CodeServerRouteWrapper {
this.router.get("/", this.ensureCodeServerLoaded, this.$root) this.router.get("/", this.ensureCodeServerLoaded, this.$root)
this.router.get("/manifest.json", this.manifest) this.router.get("/manifest.json", this.manifest)
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest) this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
this._wsRouterWrapper.ws("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket) this._wsRouterWrapper.ws("*", ensureOrigin, ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
} }
dispose() { dispose() {

View File

@@ -541,3 +541,13 @@ export const loadAMDModule = async <T>(amdPath: string, exportName: string): Pro
return module[exportName] as T return module[exportName] as T
} }
/**
* Split a string on the first equals. The result will always be an array with
* two items regardless of how many equals there are. The second item will be
* undefined if empty or missing.
*/
export function splitOnFirstEquals(str: string): [string, string | undefined] {
const split = str.split(/=(.+)?/, 2)
return [split[0], split[1]]
}

View File

@@ -32,6 +32,9 @@ export class WebsocketRouter {
/** /**
* Handle a websocket at this route. Note that websockets are immediately * Handle a websocket at this route. Note that websockets are immediately
* paused when they come in. * paused when they come in.
*
* If the origin header exists it must match the host or the connection will
* be prevented.
*/ */
public ws(route: expressCore.PathParams, ...handlers: pluginapi.WebSocketHandler[]): void { public ws(route: expressCore.PathParams, ...handlers: pluginapi.WebSocketHandler[]): void {
this.router.get( this.router.get(

View File

@@ -128,6 +128,8 @@ export class CodeServer {
path.join(dir, "extensions"), path.join(dir, "extensions"),
"--auth", "--auth",
"none", "none",
// The workspace to open.
...(this.args.includes("--ignore-last-opened") ? [] : [dir]),
...this.args, ...this.args,
// Using port zero will spawn on a random port. // Using port zero will spawn on a random port.
"--bind-addr", "--bind-addr",
@@ -139,8 +141,6 @@ export class CodeServer {
path.join(dir, "config.yaml"), path.join(dir, "config.yaml"),
"--user-data-dir", "--user-data-dir",
dir, dir,
// The last argument is the workspace to open.
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, {

111
test/e2e/routes.test.ts Normal file
View File

@@ -0,0 +1,111 @@
import { describe, test, expect } from "./baseFixture"
import { clean, getMaybeProxiedPathname } from "../utils/helpers"
const routes = ["/", "/vscode", "/vscode/"]
describe("VS Code Routes", ["--disable-workspace-trust"], {}, async () => {
const testName = "vscode-routes-default"
test.beforeAll(async () => {
await clean(testName)
})
test("should load all route variations", async ({ codeServerPage }) => {
for (const route of routes) {
await codeServerPage.navigate(route)
// Check there were no redirections
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe(route)
// TODO@jsjoeio
// now that we are in a proper browser instead of scraping the HTML we
// could possibly intercept requests to make sure assets are loading from
// the right spot.
//
// Check that page loaded from correct route
const html = await codeServerPage.page.innerHTML("html")
switch (route) {
case "/":
case "/vscode/":
expect(html).toMatch(/src="\.\/[a-z]+-[0-9a-z]+\/static\//)
break
case "/vscode":
expect(html).toMatch(/src="\.\/vscode\/[a-z]+-[0-9a-z]+\/static\//)
break
}
}
})
})
const CODE_WORKSPACE_DIR = process.env.CODE_WORKSPACE_DIR || ""
describe("VS Code Routes with code-workspace", ["--disable-workspace-trust", CODE_WORKSPACE_DIR], {}, async () => {
test("should redirect to the passed in workspace using human-readable query", async ({ codeServerPage }) => {
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe(`?workspace=${CODE_WORKSPACE_DIR}`)
})
})
const CODE_FOLDER_DIR = process.env.CODE_FOLDER_DIR || ""
describe("VS Code Routes with code-workspace", ["--disable-workspace-trust", CODE_FOLDER_DIR], {}, async () => {
test("should redirect to the passed in folder using human-readable query", async ({ codeServerPage }) => {
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe(`?folder=${CODE_FOLDER_DIR}`)
})
})
describe(
"VS Code Routes with ignore-last-opened",
["--disable-workspace-trust", "--ignore-last-opened"],
{},
async () => {
test("should not redirect", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
await codeServerPage.navigate(`/?folder=${folder}`)
await codeServerPage.navigate(`/`)
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe("")
})
},
)
describe("VS Code Routes with no workspace or folder", ["--disable-workspace-trust"], {}, async () => {
test("should redirect to last query folder/workspace", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
const workspace = process.env.CODE_WORKSPACE_DIR
await codeServerPage.navigate(`/?folder=${folder}&workspace=${workspace}`)
// If you visit again without query parameters it will re-attach them by
// redirecting. It should always redirect to the same route.
for (const route of routes) {
await codeServerPage.navigate(route)
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe(route)
expect(url.search).toBe(`?folder=${folder}&workspace=${workspace}`)
}
})
})
describe("VS Code Routes with no workspace or folder", ["--disable-workspace-trust"], {}, async () => {
test("should not redirect if ew passed in", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
const workspace = process.env.CODE_WORKSPACE_DIR
await codeServerPage.navigate(`/?folder=${folder}&workspace=${workspace}`)
// Closing the folder should stop the redirecting.
await codeServerPage.navigate("/?ew=true")
let url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe("?ew=true")
})
})

View File

@@ -1,13 +0,0 @@
import { describe, test, expect } from "./baseFixture"
describe("Workspace trust (enabled)", [], {}, async () => {
test("should see the 'I Trust...' option", async ({ codeServerPage }) => {
expect(await codeServerPage.page.isVisible("text=Yes, I trust")).toBe(true)
})
})
describe("Workspace trust (disabled)", ["--disable-workspace-trust"], {}, async () => {
test("should not see the 'I Trust...' option", async ({ codeServerPage }) => {
expect(await codeServerPage.page.isVisible("text=Yes, I trust")).toBe(false)
})
})

View File

@@ -14,7 +14,7 @@
"jest": "^27.3.1", "jest": "^27.3.1",
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0", "jsdom": "^16.4.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.7",
"playwright": "^1.16.3", "playwright": "^1.16.3",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"ts-jest": "^27.0.7", "ts-jest": "^27.0.7",

View File

@@ -1,5 +1,6 @@
import { promises as fs } from "fs" import { promises as fs } from "fs"
import { clean, getAvailablePort, tmpdir, useEnv } from "../../test/utils/helpers" import { clean, getAvailablePort, getMaybeProxiedPathname, tmpdir, useEnv } from "../../test/utils/helpers"
import { REVERSE_PROXY_BASE_PATH } from "../utils/constants"
/** /**
* This file is for testing test helpers (not core code). * This file is for testing test helpers (not core code).
@@ -56,3 +57,22 @@ describe("getAvailablePort", () => {
expect(portOne).not.toEqual(portTwo) expect(portOne).not.toEqual(portTwo)
}) })
}) })
describe("getMaybeProxiedPathname", () => {
it("should return the route", () => {
const route = "/vscode"
const url = new URL(`http://localhost:3000${route}`)
const actual = getMaybeProxiedPathname(url)
expect(actual).toBe(route)
})
it("should strip proxy if env var set", () => {
const envKey = "USE_PROXY"
const [setValue, resetValue] = useEnv(envKey)
setValue("1")
const route = "/vscode"
const url = new URL(`http://localhost:3000/8000/${REVERSE_PROXY_BASE_PATH}${route}`)
const actual = getMaybeProxiedPathname(url)
expect(actual).toBe(route)
resetValue()
})
})

View File

@@ -11,7 +11,6 @@ import {
readSocketPath, readSocketPath,
setDefaults, setDefaults,
shouldOpenInExistingInstance, shouldOpenInExistingInstance,
splitOnFirstEquals,
toCodeArgs, toCodeArgs,
optionDescriptions, optionDescriptions,
options, options,
@@ -44,6 +43,7 @@ describe("parser", () => {
delete process.env.PASSWORD delete process.env.PASSWORD
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
console.log = jest.fn() console.log = jest.fn()
}) })
@@ -297,26 +297,6 @@ describe("parser", () => {
}) })
}) })
it("should override with --link", async () => {
const args = parse(
"--cert test --cert-key test --socket test --socket-mode 777 --host 0.0.0.0 --port 8888 --link test".split(" "),
)
const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
auth: "none",
host: "localhost",
link: {
value: "test",
},
port: 0,
cert: undefined,
"cert-key": path.resolve("test"),
socket: undefined,
"socket-mode": undefined,
})
})
it("should use env var password", async () => { it("should use env var password", async () => {
process.env.PASSWORD = "test" process.env.PASSWORD = "test"
const args = parse([]) const args = parse([])
@@ -478,6 +458,31 @@ describe("parser", () => {
port: 8082, port: 8082,
}) })
}) })
it("should not set proxy uri", async () => {
await setDefaults(parse([]))
expect(process.env.VSCODE_PROXY_URI).toBeUndefined()
})
it("should set proxy uri", async () => {
await setDefaults(parse(["--proxy-domain", "coder.org"]))
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.org")
})
it("should set proxy uri to first domain", async () => {
await setDefaults(
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
)
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.com")
})
it("should not override existing proxy uri", async () => {
process.env.VSCODE_PROXY_URI = "foo"
await setDefaults(
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
)
expect(process.env.VSCODE_PROXY_URI).toEqual("foo")
})
}) })
describe("cli", () => { describe("cli", () => {
@@ -555,31 +560,6 @@ describe("cli", () => {
}) })
}) })
describe("splitOnFirstEquals", () => {
it("should split on the first equals", () => {
const testStr = "enabled-proposed-api=test=value"
const actual = splitOnFirstEquals(testStr)
const expected = ["enabled-proposed-api", "test=value"]
expect(actual).toEqual(expect.arrayContaining(expected))
})
it("should split on first equals regardless of multiple equals signs", () => {
const testStr =
"hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY"
const actual = splitOnFirstEquals(testStr)
const expected = [
"hashed-password",
"$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY",
]
expect(actual).toEqual(expect.arrayContaining(expected))
})
it("should always return the first element before an equals", () => {
const testStr = "auth="
const actual = splitOnFirstEquals(testStr)
const expected = ["auth"]
expect(actual).toEqual(expect.arrayContaining(expected))
})
})
describe("shouldSpawnCliProcess", () => { describe("shouldSpawnCliProcess", () => {
it("should return false if no 'extension' related args passed in", async () => { it("should return false if no 'extension' related args passed in", async () => {
const args = {} const args = {}
@@ -881,21 +861,13 @@ describe("optionDescriptions", () => {
it("should show if an option is deprecated", () => { it("should show if an option is deprecated", () => {
const opts: Partial<Options<Required<UserProvidedArgs>>> = { const opts: Partial<Options<Required<UserProvidedArgs>>> = {
link: { cert: {
type: OptionalString, type: OptionalString,
description: ` description: "foo",
Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.
`,
deprecated: true, deprecated: true,
}, },
} }
expect(optionDescriptions(opts)).toStrictEqual([ expect(optionDescriptions(opts)).toStrictEqual([" --cert (deprecated) foo"])
` --link (deprecated) Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.`,
])
}) })
it("should show newlines in description", () => { it("should show newlines in description", () => {

View File

@@ -1,55 +1,125 @@
import { getMockReq } from "@jest-mock/express" import { getMockReq } from "@jest-mock/express"
import { constructRedirectPath, relativeRoot } from "../../../src/node/http" import * as http from "../../../src/node/http"
import { mockLogger } from "../../utils/helpers"
describe("http", () => { describe("http", () => {
it("should construct a relative path to the root", () => { beforeEach(() => {
expect(relativeRoot("/")).toStrictEqual(".") mockLogger()
expect(relativeRoot("/foo")).toStrictEqual(".")
expect(relativeRoot("/foo/")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar ")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar/")).toStrictEqual("./../..")
}) })
})
describe("constructRedirectPath", () => { afterEach(() => {
it("should preserve slashes in queryString so they are human-readable", () => { jest.clearAllMocks()
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = ""
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
}) })
it("should use an empty string if no query params", () => {
const mockReq = getMockReq({ it("should construct a relative path to the root", () => {
originalUrl: "localhost:8080", expect(http.relativeRoot("/")).toStrictEqual(".")
}) expect(http.relativeRoot("/foo")).toStrictEqual(".")
const mockQueryParams = {} expect(http.relativeRoot("/foo/")).toStrictEqual("./..")
const mockTo = "" expect(http.relativeRoot("/foo/bar ")).toStrictEqual("./..")
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo) expect(http.relativeRoot("/foo/bar/")).toStrictEqual("./../..")
const expected = "./"
expect(actual).toBe(expected)
}) })
it("should append the 'to' path relative to the originalUrl", () => {
const mockReq = getMockReq({ describe("origin", () => {
originalUrl: "localhost:8080", ;[
{
origin: "",
host: "",
},
{
origin: "http://localhost:8080",
host: "",
expected: "no host headers",
},
{
origin: "http://localhost:8080",
host: " ",
expected: "does not match",
},
{
origin: "http://localhost:8080",
host: "localhost:8080",
},
{
origin: "http://localhost:8080",
host: "localhost:8081",
expected: "does not match",
},
{
origin: "localhost:8080",
host: "localhost:8080",
expected: "does not match", // Gets parsed as host: localhost and path: 8080.
},
{
origin: "test.org",
host: "localhost:8080",
expected: "malformed", // Parsing fails completely.
},
].forEach((test) => {
;[
["host", test.host],
["x-forwarded-host", test.host],
["forwarded", `for=127.0.0.1, host=${test.host}, proto=http`],
["forwarded", `for=127.0.0.1;proto=http;host=${test.host}`],
["forwarded", `proto=http;host=${test.host}, for=127.0.0.1`],
].forEach(([key, value]) => {
it(`${test.origin} -> [${key}: ${value}]`, () => {
const req = getMockReq({
originalUrl: "localhost:8080",
headers: {
origin: test.origin,
[key]: value,
},
})
if (typeof test.expected === "string") {
expect(() => http.authenticateOrigin(req)).toThrow(test.expected)
} else {
expect(() => http.authenticateOrigin(req)).not.toThrow()
}
})
})
}) })
const mockQueryParams = {}
const mockTo = "vscode"
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode"
expect(actual).toBe(expected)
}) })
it("should append append queryParams after 'to' path", () => {
const mockReq = getMockReq({ describe("constructRedirectPath", () => {
originalUrl: "localhost:8080", it("should preserve slashes in queryString so they are human-readable", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = ""
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
})
it("should use an empty string if no query params", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = ""
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./"
expect(actual).toBe(expected)
})
it("should append the 'to' path relative to the originalUrl", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = "vscode"
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode"
expect(actual).toBe(expected)
})
it("should append append queryParams after 'to' path", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = "vscode"
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
}) })
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = "vscode"
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
}) })
}) })

View File

@@ -4,21 +4,26 @@ import * as http from "http"
import nodeFetch from "node-fetch" import nodeFetch from "node-fetch"
import { HttpCode } from "../../../src/common/http" import { HttpCode } from "../../../src/common/http"
import { proxy } from "../../../src/node/proxy" import { proxy } from "../../../src/node/proxy"
import { getAvailablePort } from "../../utils/helpers" import { wss, Router as WsRouter } from "../../../src/node/wsRouter"
import { getAvailablePort, 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"
describe("proxy", () => { describe("proxy", () => {
const nhooyrDevServer = new httpserver.HttpServer() const nhooyrDevServer = new httpserver.HttpServer()
const wsApp = express.default()
const wsRouter = WsRouter()
let codeServer: httpserver.HttpServer | undefined let codeServer: httpserver.HttpServer | undefined
let proxyPath: string let proxyPath: string
let absProxyPath: string let absProxyPath: string
let e: express.Express let e: express.Express
beforeAll(async () => { beforeAll(async () => {
wsApp.use("/", wsRouter.router)
await nhooyrDevServer.listen((req, res) => { await nhooyrDevServer.listen((req, res) => {
e(req, res) e(req, res)
}) })
nhooyrDevServer.listenUpgrade(wsApp)
proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup` proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup`
absProxyPath = proxyPath.replace("/proxy/", "/absproxy/") absProxyPath = proxyPath.replace("/proxy/", "/absproxy/")
}) })
@@ -29,6 +34,7 @@ describe("proxy", () => {
beforeEach(() => { beforeEach(() => {
e = express.default() e = express.default()
mockLogger()
}) })
afterEach(async () => { afterEach(async () => {
@@ -36,6 +42,7 @@ describe("proxy", () => {
await codeServer.dispose() await codeServer.dispose()
codeServer = undefined codeServer = undefined
} }
jest.clearAllMocks()
}) })
it("should rewrite the base path", async () => { it("should rewrite the base path", async () => {
@@ -151,6 +158,35 @@ describe("proxy", () => {
expect(resp.status).toBe(500) expect(resp.status).toBe(500)
expect(resp.statusText).toMatch("Internal Server Error") expect(resp.statusText).toMatch("Internal Server Error")
}) })
it("should pass origin check", async () => {
wsRouter.ws("/wsup", async (req) => {
wss.handleUpgrade(req, req.ws, req.head, (ws) => {
ws.send("hello")
req.ws.resume()
})
})
codeServer = await integration.setup(["--auth=none"], "")
const ws = await codeServer.wsWait(proxyPath, {
headers: {
host: "localhost:8080",
origin: "https://localhost:8080",
},
})
ws.terminate()
})
it("should fail origin check", async () => {
await expect(async () => {
codeServer = await integration.setup(["--auth=none"], "")
await codeServer.wsWait(proxyPath, {
headers: {
host: "localhost:8080",
origin: "https://evil.org",
},
})
}).rejects.toThrow()
})
}) })
// NOTE@jsjoeio // NOTE@jsjoeio
@@ -190,18 +226,18 @@ describe("proxy (standalone)", () => {
}) })
// Start both servers // Start both servers
await proxyTarget.listen(PROXY_PORT) proxyTarget.listen(PROXY_PORT)
await testServer.listen(PORT) testServer.listen(PORT)
}) })
afterEach(async () => { afterEach(async () => {
await testServer.close() testServer.close()
await proxyTarget.close() proxyTarget.close()
}) })
it("should return a 500 when proxy target errors ", async () => { it("should return a 500 when proxy target errors ", async () => {
// Close the proxy target so that proxy errors // Close the proxy target so that proxy errors
await proxyTarget.close() proxyTarget.close()
const errorResp = await nodeFetch(`${URL}/error`) const errorResp = await nodeFetch(`${URL}/error`)
expect(errorResp.status).toBe(HttpCode.ServerError) expect(errorResp.status).toBe(HttpCode.ServerError)
expect(errorResp.statusText).toBe("Internal Server Error") expect(errorResp.statusText).toBe("Internal Server Error")

View File

@@ -23,7 +23,9 @@ describe("health", () => {
codeServer = await integration.setup(["--auth=none"], "") codeServer = await integration.setup(["--auth=none"], "")
const ws = codeServer.ws("/healthz") const ws = codeServer.ws("/healthz")
const message = await new Promise((resolve, reject) => { const message = await new Promise((resolve, reject) => {
ws.on("error", console.error) ws.on("error", (err) => {
console.error("[healthz]", err)
})
ws.on("message", (message) => { ws.on("message", (message) => {
try { try {
const j = JSON.parse(message.toString()) const j = JSON.parse(message.toString())

View File

@@ -138,5 +138,16 @@ describe("login", () => {
expect(resp.status).toBe(200) expect(resp.status).toBe(200)
expect(htmlContent).toContain(`Welcome to ${appName}`) expect(htmlContent).toContain(`Welcome to ${appName}`)
}) })
it("should return correct welcome text when locale is set to non-English", async () => {
process.env.PASSWORD = previousEnvPassword
const locale = "zh-cn"
const codeServer = await integration.setup([`--locale=${locale}`], "")
const resp = await codeServer.fetch("/login", { method: "GET" })
const htmlContent = await resp.text()
expect(resp.status).toBe(200)
expect(htmlContent).toContain(`欢迎来到 code-server`)
})
}) })
}) })

View File

@@ -1,17 +1,11 @@
import { promises as fs } from "fs"
import * as path from "path"
import { clean, tmpdir } 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"
// TODO@jsjoeio - move these to integration tests since they rely on Code
// to be built
describe("vscode", () => { describe("vscode", () => {
let codeServer: httpserver.HttpServer | undefined let codeServer: httpserver.HttpServer | undefined
beforeEach(() => {
const testName = "vscode" mockLogger()
beforeAll(async () => {
await clean(testName)
}) })
afterEach(async () => { afterEach(async () => {
@@ -19,119 +13,18 @@ describe("vscode", () => {
await codeServer.dispose() await codeServer.dispose()
codeServer = undefined codeServer = undefined
} }
jest.clearAllMocks()
}) })
const routes = ["/", "/vscode", "/vscode/"] it("should fail origin check", async () => {
await expect(async () => {
it("should load all route variations", async () => { codeServer = await integration.setup(["--auth=none"], "")
codeServer = await integration.setup(["--auth=none"], "") await codeServer.wsWait("/vscode", {
headers: {
for (const route of routes) { host: "localhost:8080",
const resp = await codeServer.fetch(route) origin: "https://evil.org",
expect(resp.status).toBe(200) },
const html = await resp.text() })
const url = new URL(resp.url) // Check there were no redirections. }).rejects.toThrow()
expect(url.pathname + url.search).toBe(route)
switch (route) {
case "/":
case "/vscode/":
expect(html).toMatch(/src="\.\/[a-z]+-[0-9a-z]+\/static\//)
break
case "/vscode":
expect(html).toMatch(/src="\.\/vscode\/[a-z]+-[0-9a-z]+\/static\//)
break
}
}
})
it("should redirect to the passed in workspace using human-readable query", async () => {
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
codeServer = await integration.setup(["--auth=none", workspace], "")
const resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe(`?workspace=${workspace}`)
})
it("should redirect to the passed in folder using human-readable query", async () => {
const folder = await tmpdir(testName)
codeServer = await integration.setup(["--auth=none", folder], "")
const resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe(`?folder=${folder}`)
})
it("should redirect to last query folder/workspace", async () => {
codeServer = await integration.setup(["--auth=none"], "")
const folder = await tmpdir(testName)
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
let resp = await codeServer.fetch("/", undefined, {
folder,
workspace,
})
expect(resp.status).toBe(200)
await resp.text()
// If you visit again without query parameters it will re-attach them by
// redirecting. It should always redirect to the same route.
for (const route of routes) {
resp = await codeServer.fetch(route)
const url = new URL(resp.url)
expect(url.pathname).toBe(route)
expect(url.search).toBe(`?folder=${folder}&workspace=${workspace}`)
await resp.text()
}
// Closing the folder should stop the redirecting.
resp = await codeServer.fetch("/", undefined, { ew: "true" })
let url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("?ew=true")
await resp.text()
resp = await codeServer.fetch("/")
url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("")
await resp.text()
})
it("should do nothing when nothing is passed in", async () => {
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch("/", undefined)
expect(resp.status).toBe(200)
const url = new URL(resp.url)
expect(url.search).toBe("")
await resp.text()
})
it("should not redirect when last opened is ignored", async () => {
codeServer = await integration.setup(["--auth=none", "--ignore-last-opened"], "")
const folder = await tmpdir(testName)
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
let resp = await codeServer.fetch("/", undefined, {
folder,
workspace,
})
expect(resp.status).toBe(200)
await resp.text()
// No redirections.
resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("")
await resp.text()
}) })
}) })

View File

@@ -601,3 +601,41 @@ describe("constructOpenOptions", () => {
expect(urlSearch).toBe("?q=^&test") expect(urlSearch).toBe("?q=^&test")
}) })
}) })
describe("splitOnFirstEquals", () => {
const tests = [
{
name: "empty",
key: "",
value: "",
},
{
name: "split on first equals",
key: "foo",
value: "bar",
},
{
name: "split on first equals even with multiple equals",
key: "foo",
value: "bar=baz",
},
{
name: "split with empty value",
key: "foo",
value: "",
},
{
name: "split with no value",
key: "foo",
value: undefined,
},
]
tests.forEach((test) => {
it("should ${test.name}", () => {
const input = test.key && typeof test.value !== "undefined" ? `${test.key}=${test.value}` : test.key
const [key, value] = util.splitOnFirstEquals(input)
expect(key).toStrictEqual(test.key)
expect(value).toStrictEqual(test.value || undefined)
})
})
})

View File

@@ -1,6 +1,8 @@
import { workspaceDir } from "./constants" import { workspaceDir } from "./constants"
import { clean } 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
@@ -17,5 +19,14 @@ export default async function () {
wtfnode.setup() wtfnode.setup()
} }
// Create dummy code-workspace for routes.test.ts
const codeWorkspace = path.join(await tmpdir(workspaceDir), "test.code-workspace")
await fs.writeFile(codeWorkspace, "")
process.env.CODE_WORKSPACE_DIR = codeWorkspace
// Create dummy folder for routes.test.ts
const folder = await tmpdir(workspaceDir)
process.env.CODE_FOLDER_DIR = folder
console.log("✅ Global Setup for Playwright End-to-End Tests is now complete.") console.log("✅ Global Setup for Playwright End-to-End Tests is now complete.")
} }

View File

@@ -136,3 +136,17 @@ export async function getMaybeProxiedCodeServer(codeServer: CodeServerPage | Cod
return address return address
} }
/**
* Stripes proxy base from url.pathname
* i.e. /<port>/ide + route returns just route
*/
export function getMaybeProxiedPathname(url: URL): string {
if (process.env.USE_PROXY === "1") {
// Behind proxy, path will be /<port>/ide + route
const pathWithoutProxy = url.pathname.split(`/${REVERSE_PROXY_BASE_PATH}`)[1]
return pathWithoutProxy
}
return url.pathname
}

View File

@@ -7,7 +7,6 @@ import { Disposable } from "../../src/common/emitter"
import * as util from "../../src/common/util" import * as util from "../../src/common/util"
import { ensureAddress } from "../../src/node/app" import { ensureAddress } from "../../src/node/app"
import { disposer } from "../../src/node/http" import { disposer } from "../../src/node/http"
import { handleUpgrade } from "../../src/node/wsRouter" import { handleUpgrade } from "../../src/node/wsRouter"
// Perhaps an abstraction similar to this should be used in app.ts as well. // Perhaps an abstraction similar to this should be used in app.ts as well.
@@ -76,14 +75,25 @@ export class HttpServer {
/** /**
* Open a websocket against the request path. * Open a websocket against the request path.
*/ */
public ws(requestPath: string): Websocket { public ws(requestPath: string, options?: Websocket.ClientOptions): Websocket {
const address = ensureAddress(this.hs, "ws") const address = ensureAddress(this.hs, "ws")
if (typeof address === "string") { if (typeof address === "string") {
throw new Error("Cannot open websocket to socket path") throw new Error("Cannot open websocket to socket path")
} }
address.pathname = requestPath address.pathname = requestPath
return new Websocket(address.toString()) return new Websocket(address.toString(), options)
}
/**
* Open a websocket and wait for it to fully open.
*/
public wsWait(requestPath: string, options?: Websocket.ClientOptions): Promise<Websocket> {
const ws = this.ws(requestPath, options)
return new Promise<Websocket>((resolve, reject) => {
ws.on("error", (err) => reject(err))
ws.on("open", () => resolve(ws))
})
} }
public port(): number { public port(): number {

View File

@@ -1285,16 +1285,16 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
safe-buffer "~5.1.1" safe-buffer "~5.1.1"
cookiejar@^2.1.3: cookiejar@^2.1.3:
version "2.1.3" version "2.1.4"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
cross-fetch@^3.0.4: cross-fetch@^3.0.4:
version "3.1.4" version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies: dependencies:
node-fetch "2.6.1" node-fetch "2.6.7"
cross-spawn@^7.0.3: cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
@@ -2334,9 +2334,9 @@ jest@^27.3.1:
jest-cli "^27.4.7" jest-cli "^27.4.7"
jpeg-js@^0.4.2: jpeg-js@^0.4.2:
version "0.4.3" version "0.4.4"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
js-tokens@^4.0.0: js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
@@ -2395,11 +2395,9 @@ json-schema@^0.4.0:
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
json5@2.x, json5@^2.1.2: json5@2.x, json5@^2.1.2:
version "2.2.0" version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
dependencies:
minimist "^1.2.5"
kleur@^3.0.3: kleur@^3.0.3:
version "3.0.3" version "3.0.3"
@@ -2503,17 +2501,12 @@ mimic-fn@^2.1.0:
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@^3.0.3, minimatch@^3.0.4: minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^3.0.0: minipass@^3.0.0:
version "3.1.6" version "3.1.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
@@ -2554,19 +2547,7 @@ node-addon-api@^4.3.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-fetch@2.6.1: node-fetch@2.6.7, node-fetch@^2.6.5, node-fetch@^2.6.7:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^2.6.1:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.5:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==

View File

@@ -22,7 +22,8 @@
"./test/node_modules/@types", "./test/node_modules/@types",
"./lib/vscode/src/vs/server/@types" "./lib/vscode/src/vs/server/@types"
], ],
"downlevelIteration": true "downlevelIteration": true,
"resolveJsonModule": true
}, },
"include": ["./src/**/*"], "include": ["./src/**/*"],
"exclude": ["/test", "/lib", "/ci", "/doc"] "exclude": ["/test", "/lib", "/ci", "/doc"]

624
yarn.lock

File diff suppressed because it is too large Load Diff