Compare commits

...

142 Commits

Author SHA1 Message Date
dependabot[bot]
daac46b3cf chore: bump i18next from 22.5.1 to 23.2.11 (#6326)
Bumps [i18next](https://github.com/i18next/i18next) from 22.5.1 to 23.2.11.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v22.5.1...v23.2.11)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* Set session socket into environment variable

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

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

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

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

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

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

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

* Update proxy-agent usage

---------

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

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

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

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

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

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 12:45:49 -05:00
Jonas
ee024f3f2d docs: mention Termux extension workarounds (#6227) 2023-05-31 15:00:58 -08:00
Simon Merschjohann
0703ef008c Allow {{host}} and {{port}} in domain proxy (#6225) 2023-05-31 16:31:30 -05:00
Asher
2109d1cf6a Add new unreleased section to changelog 2023-05-22 12:21:30 -08:00
Asher
74af05dfbe Release v4.13.0 2023-05-19 13:14:29 -08:00
dependabot[bot]
2798322b03 chore: bump vm2 from 3.9.17 to 3.9.19 (#6216)
Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.17 to 3.9.19.
- [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.17...3.9.19)

---
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-05-17 12:38:38 -08:00
Asher
b3b971480f Support X-Forwarded-Host with multiple hosts
Closes #6215.
2023-05-17 11:51:05 -08:00
Asher
6745a46034 Fix extension install test
This is not a valid ID (to install a specific version you use @) and,
quite strangely, Code is now returning an "extension not found" error if
the ID is invalid, breaking the test since we expect it to error about
the marketplace not existing.
2023-05-15 20:54:37 -08:00
Asher
43ef50b404 Update to 1.78.2 (#6201)
* Update to 1.78.1

No changes needed in the patches other than moving some lines around and
updating the CSP hash as usual.

The flake had to be updated as it was using Node 16.16 and 16.17 is
required at minimum now.  Also python seems to install python2 which is
marked as deprecated so explicitly install python3.

* Update to 1.78.2

Patches applied without any conflicts.

* Update commit environment variable

This was causing the commit not to be set.  It broke display languages
since that has a hard dependency on the commit for directory names.
Possibly broke other things.
2023-05-15 15:44:03 -08:00
dependabot[bot]
45c89856fd chore: bump eslint-config-prettier from 8.6.0 to 8.8.0 (#6179)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.6.0 to 8.8.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.6.0...v8.8.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-05-08 12:43:37 -08:00
Jim van Abkoude
5f4ae75431 Add nix-on-droid as an Android installation method (#6196) 2023-05-08 12:42:43 -08:00
易良
521ff44aed fix: relative path in markdown (#6188)
Co-authored-by: jinjing.zzj <jinjing.zzj@alibaba-inc.com>
2023-05-08 12:38:52 -08:00
Asher
5708e6ce32 Format redacted arg lines
Pushing straight to main again.
2023-05-05 14:44:40 -08:00
Asher
3f7db15fde Redact sensitive args from handshake debug log 2023-05-04 10:54:41 -08:00
Asher
8c99f41b90 Fix parent process log level 2023-05-04 10:48:32 -08:00
Asher
e02d56dbfd Do not trace child process messages
All this does is add a stack trace which is not helpful here.
2023-05-04 10:48:31 -08:00
Asher
aac5efa046 Do not log < 500 on web sockets as errors
For example if someone spams a web socket without authentication we
should not log "forbidden".  Forbidden is normal/expected operation, not
an error.
2023-05-04 10:48:30 -08:00
dependabot[bot]
ff2764f7b1 chore: bump rotating-file-stream from 3.0.0 to 3.1.0 (#6183)
Bumps [rotating-file-stream](https://github.com/iccicci/rotating-file-stream) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/iccicci/rotating-file-stream/releases)
- [Changelog](https://github.com/iccicci/rotating-file-stream/blob/master/CHANGELOG.md)
- [Commits](https://github.com/iccicci/rotating-file-stream/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: rotating-file-stream
  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-05-03 08:33:32 -08:00
dependabot[bot]
bda6b631ab chore: bump @typescript-eslint/eslint-plugin from 5.57.1 to 5.59.1 (#6180)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.57.1 to 5.59.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.59.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-05-01 16:49:48 -08:00
dependabot[bot]
9bac70ea0d chore: bump eslint from 8.26.0 to 8.39.0 (#6181)
Bumps [eslint](https://github.com/eslint/eslint) from 8.26.0 to 8.39.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.26.0...v8.39.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 16:49:18 -08:00
dependabot[bot]
7722ef1437 chore: bump aquasecurity/trivy-action from 0.9.2 to 0.10.0 (#6178)
Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.9.2 to 0.10.0.
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](1f0aa582c8...e5f43133f6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 11:17:06 -05:00
smalllady
951d8ac45e Fix proxying non-ASCII (#6154)
This only affects the path proxy since `req.originalUrl` is in escaped format.
2023-04-25 11:41:33 -08:00
Asher
2e17735795 Release v4.12.0 (#6157) 2023-04-21 14:04:07 -05:00
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
Joe Previte
87f606db2a chore(ci): update linux cross-compile runner 20.04 (#5896) 2022-12-16 08:54:23 -07:00
Joe Previte
06483bc59d refactor: rm node_mods caching in release workflow (#5895) 2022-12-15 14:24:59 -07:00
Joe Previte
9012ddfe79 refactor: use separate cache key in release, run standalone after caching (#5859)
* refactor: remove keytar dep in cross-compile

* refactor: try other keytar package

* refactor: remove keytar step in cross-compile

* fix: manually remove keytar

* try this first

* I think this is it

* Revert "I think this is it"

This reverts commit 5c566b0c01.

* okay this is it

* fixup

* try legacy peer

* remove keytar before standalone

* wrong path

* maybe

* revert: change *npm* back to npm*

* revert: don't uninstall keytar

* fix: use npm run standalone-release

* fixup formatting

* Revert "refactor: remove yarn.lock steps (#5850)"

This reverts commit 907747d394.

* fixup: remove the --exclude

* refactor: remove yarn.lock check

* try ddd in postinstall

* refactor: cache before release:standalone

* refactor: add os to cache key in release

* chore: formatting

* Update ci/build/npm-postinstall.sh

* fixup: formatting
2022-12-12 22:03:07 +00:00
Joe Previte
1efc5f104e fix: use npm and yarn consistently in build and release (#5852)
* refactor: remove keytar dep in cross-compile

* refactor: try other keytar package

* refactor: remove keytar step in cross-compile

* fix: manually remove keytar

* try this first

* I think this is it

* Revert "I think this is it"

This reverts commit 5c566b0c01.

* okay this is it

* fixup

* try legacy peer

* remove keytar before standalone

* wrong path

* maybe

* revert: change *npm* back to npm*

* revert: don't uninstall keytar

* fix: use npm run standalone-release

* fixup formatting

* Revert "refactor: remove yarn.lock steps (#5850)"

This reverts commit 907747d394.

* fixup: remove the --exclude

* refactor: remove yarn.lock check

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2022-12-12 21:41:29 +00:00
Joe Previte
ef5865f506 fix: add VERSION to env in publish.yaml (#5868)
When I did the last release, `VERSION` wasn't defined which lead to a
blank string in the PR title and the commit message here:

https://github.com/coder/code-server-aur/pull/24

This should fix that.
2022-12-12 21:24:06 +00:00
Joe Previte
48588ee542 chore: update download artifacts in release (#5841) 2022-12-12 17:47:27 +00:00
Geoffrey Huntley
ac6b5bd4e6 chore(nfpm): bump to latest version (#5860) 2022-12-12 08:52:33 -07:00
Joe Previte
0ca6620ee6 fix: add missing keytar deps (#5851)
* fix: add missing keytar deps

* fixup
2022-12-08 11:30:08 -07:00
Joe Previte
907747d394 refactor: remove yarn.lock steps (#5850) 2022-12-08 10:44:27 -07:00
Joe Previte
d8995ec0b8 refactor: use npm in build.yaml (#5849) 2022-12-08 10:37:42 -07:00
Joe Previte
c088e73063 fix: use proper npm casing postinstall (#5848)
* fix: use proper npm casing postinstall

* chore: add log for npm config user agent

* fixup
2022-12-08 10:22:40 -07:00
Joe Previte
83c3453f50 fix: quote VERSION in jq command in release (#5845) 2022-12-07 22:19:20 +00:00
Joe Previte
3182be634e refactor: use npm in build steps and postinstall.sh (#5844)
* refactor: default to npm in postinstall.sh

yarn has a bug where it will try to update dependencies even if
`yarn.lock` is present. Therefore we're defaulting to `npm` to prevent
further issues.

* refactor: exclude yarn.lock in standalone

By excluding `yarn.lock`, we prevent issues where the user must use
`yarn` instead of `npm` to install code-server.
2022-12-07 15:04:50 -07:00
Joe Previte
1297e9ac88 chore: update qs version (#5840) 2022-12-07 14:57:08 -06:00
Joe Previte
1484bee621 release: 4.9.0 (#5772)
* wip: changelog

* fixup

* fix: add +x to product.json in build-vscode

While testing a pre-release, there seems to be a bug with the file
permissions for `product.json`. Adding `chmod +x` to see if that fixes
it.

* chore: increase timeout

* fix: keep product.json file permissions in release

When we added the change to modify the `package.json` version using `mv`
and `jq` we didn't account for lost file permissions.

This caused a bug only happening in CI.

This should fix it by giving it 755 via `chmod`.

* trigger ci

* chore: update package.json bust cache

* fixup!: fix: keep product.json file permissions in release

* Revert "fix: add +x to product.json in build-vscode"

This reverts commit fc4d2b532f.

* chore: pin ubuntu runner in build code-server

* chore: update prettierignore

* chore: add notes to changelog

* chore: use ubuntu-22.04 for e2e

* chore: pin all jobs in build to ubuntu 20.04

* feat(wrapper): add tests for isChild

* fixup: include description ts-expect-error comment

* chore: update CHANGELOG

* chore: update Helm chart

* fixup: use our childProcess

* Update CHANGELOG.md

Co-authored-by: Asher <ash@coder.com>

Co-authored-by: Asher <ash@coder.com>
2022-12-06 13:28:27 -07:00
107 changed files with 3426 additions and 1942 deletions

2
.github/CODEOWNERS vendored
View File

@@ -3,3 +3,5 @@
ci/helm-chart/ @Matthew-Beckett @alexgorbatchev
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

@@ -24,7 +24,7 @@ concurrency:
jobs:
prettier:
name: Format with Prettier
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -37,7 +37,7 @@ jobs:
doctoc:
name: Doctoc markdown files
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -45,7 +45,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v26.1
uses: tj-actions/changed-files@v37
with:
files: |
docs/**
@@ -66,7 +66,7 @@ jobs:
lint-helm:
name: Lint Helm chart
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -76,14 +76,14 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v26.1
uses: tj-actions/changed-files@v37
with:
files: |
ci/helm-chart/**
- name: Install helm
if: steps.changed-files.outputs.any_changed == 'true'
uses: azure/setup-helm@v3.4
uses: azure/setup-helm@v3.5
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -97,7 +97,7 @@ jobs:
lint-ts:
name: Lint TypeScript files
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -107,7 +107,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v26.1
uses: tj-actions/changed-files@v37
with:
files: |
**/*.ts
@@ -139,10 +139,71 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true'
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@v37
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:
name: Build code-server
runs-on: ubuntu-latest
timeout-minutes: 30
runs-on: ubuntu-20.04
timeout-minutes: 60
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
@@ -170,22 +231,24 @@ jobs:
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
key: yarn-build-code-server-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
yarn-build-code-server-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Build code-server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn build
# Get Code's git hash. When this changes it means the content is
# different and we need to rebuild.
- name: Get latest lib/vscode rev
id: vscode-rev
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)"
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
# the patches changed, or when the code-server version changes (since
@@ -204,19 +267,6 @@ jobs:
if: steps.cache-vscode.outputs.cache-hit != 'true'
run: yarn build:vscode
# Our code imports code from VS Code's `out` directory meaning VS Code
# 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
# and is neutral to architecture/os/libc version.
- name: Create release package
@@ -233,125 +283,10 @@ jobs:
name: npm-package
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-latest
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:
name: Run e2e tests
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Node.js v16
uses: actions/setup-node@v3
with:
node-version: "16"
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Download npm package
uses: actions/download-artifact@v3
with:
name: npm-package
- name: Decompress npm package
run: tar -xzf package.tar.gz
- name: Install release package dependencies
run: cd release && yarn install
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Install Playwright OS dependencies
run: |
./test/node_modules/.bin/playwright install-deps
./test/node_modules/.bin/playwright install
- name: Run end-to-end tests
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e --global-timeout 840000
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: failed-test-videos
path: ./test/test-results
- name: Remove release packages and test artifacts
run: rm -rf ./release ./test/test-results
test-e2e-proxy:
name: Run e2e tests behind proxy
needs: build
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
timeout-minutes: 25
steps:
- name: Checkout repo
@@ -380,7 +315,63 @@ jobs:
run: tar -xzf package.tar.gz
- name: Install release package dependencies
run: cd release && yarn install
run: cd release && npm install --unsafe-perm --omit=dev
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Install Playwright OS dependencies
run: |
./test/node_modules/.bin/playwright install-deps
./test/node_modules/.bin/playwright install
- name: Run end-to-end tests
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: failed-test-videos
path: ./test/test-results
- name: Remove release packages and test artifacts
run: rm -rf ./release ./test/test-results
test-e2e-proxy:
name: Run e2e tests behind proxy
needs: build
runs-on: ubuntu-20.04
timeout-minutes: 25
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Node.js v16
uses: actions/setup-node@v3
with:
node-version: "16"
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Download npm package
uses: actions/download-artifact@v3
with:
name: npm-package
- name: Decompress npm package
run: tar -xzf package.tar.gz
- name: Install release package dependencies
run: cd release && npm install --unsafe-perm --omit=dev
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
@@ -392,7 +383,7 @@ jobs:
./test/node_modules/.bin/playwright install
- name: Cache Caddy
uses: actions/cache@v2
uses: actions/cache@v3
id: caddy-cache
with:
path: |
@@ -405,7 +396,7 @@ jobs:
if: steps.caddy-cache.outputs.cache-hit != 'true'
run: |
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
- 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
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
uses: robinraju/release-downloader@v1.6
uses: robinraju/release-downloader@v1.8
with:
repository: "coder/code-server"
tag: ${{ github.event.inputs.version || github.ref_name }}
@@ -127,6 +133,8 @@ jobs:
- name: Validate package
uses: hapakaien/archlinux-package-action@v2
env:
VERSION: ${{ env.VERSION }}
with:
pkgver: ${{ env.VERSION }}
updpkgsums: true
@@ -135,12 +143,14 @@ jobs:
- name: Open PR
# We need to git push -u otherwise gh will prompt
# asking where to push the branch.
env:
VERSION: ${{ env.VERSION }}
run: |
git checkout -b update-version-${{ steps.version.outputs.version }}
git add .
git commit -m "chore: updating version to ${{ steps.version.outputs.version }}"
git checkout -b update-version-${{ env.VERSION }}
git add .
git commit -m "chore: updating version to ${{ env.VERSION }}"
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:
runs-on: ubuntu-20.04
steps:
@@ -174,7 +184,7 @@ jobs:
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Download release artifacts
uses: robinraju/release-downloader@v1.6
uses: robinraju/release-downloader@v1.8
with:
repository: "coder/code-server"
tag: v${{ env.VERSION }}
@@ -182,7 +192,7 @@ jobs:
out-file-path: "release-packages"
- name: Publish to Docker
run: yarn publish:docker
run: ./ci/steps/docker-buildx-push.sh
env:
VERSION: ${{ env.VERSION }}
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -44,11 +44,13 @@ jobs:
run: |
yum install -y epel-release centos-release-scl make
yum install -y devtoolset-9-{make,gcc,gcc-c++} jq rsync python3
# for keytar
yum install -y libsecret-devel
- name: Install nfpm and envsubst
run: |
mkdir -p ~/.local/bin
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.22.2/nfpm_2.22.2_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
curl -sSfL https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst
chmod +x envsubst
mv envsubst ~/.local/bin
@@ -68,19 +70,9 @@ jobs:
# NOTE: && here is deliberate - GitHub puts each line in its own `.sh`
# file when running inside a docker container.
- name: Build standalone release
run: source scl_source enable devtoolset-9 && yarn release:standalone
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
run: source scl_source enable devtoolset-9 && npm run release:standalone
- name: Install test dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Run integration tests on standalone release
@@ -127,11 +119,11 @@ jobs:
# but this means we don't need to maintain a self-hosted runner!
# NOTE@jsjoeio:
# We used to use 16.04 until GitHub deprecated it on September 20, 2021
# See here: https://github.com/actions/virtual-environments/pull/3862/files
# We used to use 18.04 until GitHub browned it out on December 15, 2022
# See here: https://github.com/actions/runner-images/issues/6002
package-linux-cross:
name: Linux cross-compile builds
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
timeout-minutes: 15
needs: npm-version
strategy:
@@ -178,6 +170,8 @@ jobs:
- name: Decompress npm package
run: tar -xzf package.tar.gz
# NOTE@jsjoeio - npm fails here
# so use yarn
- name: Build standalone release
run: yarn release:standalone
@@ -234,19 +228,9 @@ jobs:
run: tar -xzf package.tar.gz
- name: Build standalone release
run: yarn release:standalone
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
run: npm run release:standalone
- name: Install test dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn install
- name: Run native module tests on standalone release
@@ -299,8 +283,9 @@ jobs:
branch: ${{ github.ref }}
workflow: build.yaml
workflow_conclusion: completed
check_artifacts: true
name: npm-package
check_artifacts: false
if_no_artifact_found: fail
- name: Decompress npm package
run: tar -xzf package.tar.gz
@@ -321,7 +306,9 @@ jobs:
echo "Updating version in lib/vscode/product.json"
tmp=$(mktemp)
jq '.codeServerVersion = "$VERSION"' release/lib/vscode/product.json > "$tmp" && mv "$tmp" release/lib/vscode/product.json
jq ".codeServerVersion = \"$VERSION\"" release/lib/vscode/product.json > "$tmp" && mv "$tmp" release/lib/vscode/product.json
# Ensure it has the same permissions as before
chmod 644 release/lib/vscode/product.json
- name: Compress release package
run: tar -czf package.tar.gz release

View File

@@ -12,9 +12,8 @@ on:
# Runs every Monday morning PST
- cron: "17 15 * * 1"
# Cancel in-progress runs for pull requests when developers push
# additional 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
# Cancel in-progress runs for pull requests when developers push additional
# changes, and serialize builds in branches.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
@@ -65,7 +64,7 @@ jobs:
fetch-depth: 0
- name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5
uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54
with:
scan-type: "fs"
scan-ref: "."

View File

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

View File

@@ -1,6 +1,7 @@
lib/vscode
lib/vscode-reh-web-linux-x64
release-standalone
release-packages
release
helm-chart
test/scripts

View File

@@ -20,6 +20,143 @@ Code v99.99.999
-->
## Unreleased
Code v1.79.2
## [4.14.1](https://github.com/coder/code-server/releases/tag/v4.14.1) - 2023-06-26
Code v1.79.2
### Security
- Remove extra write permissions on the Node binary bundled with the linux-amd64
tarball. If you extract the tar without a umask this could mean the Node
binary would be unexpectedly writable.
### Fixed
- Inability to launch multiple instances of code-server for different users.
### Added
- `--session-socket` CLI flag to configure the location of the session socket.
By default it will be placed in `<user data dir>/code-server-ipc.sock`.
## [4.14.0](https://github.com/coder/code-server/releases/tag/v4.14.0) - 2023-06-16
Code v1.79.2
### Added
- `--domain-proxy` now supports `{{port}}` and `{{host}}` template variables.
### Changed
- Updated to Code 1.79.2
- Files opened from an external terminal will now open in the most closely
related window rather than in the last opened window.
## [4.13.0](https://github.com/coder/code-server/releases/tag/v4.13.0) - 2023-05-19
Code v1.78.2
### Changed
- Updated to Code 1.78.2.
### Fixed
- Proxying files that contain non-ASCII characters.
- Origin check when X-Forwarded-Host contains comma-separated hosts.
## [4.12.0](https://github.com/coder/code-server/releases/tag/v4.12.0) - 2023-04-21
Code v1.77.3
### Changed
- Updated to Code 1.77.3
- Ports panel will use domain-based proxy (instead of the default path-based
proxy) when set via --proxy-domain.
- Apply --app-name to the PWA title.
### Added
- Thai translation for login page.
- Debug logs around the origin security check. If you are getting forbidden
errors on web sockets please run code-server with `--log debug` to see why the
requests are being blocked.
## [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
Code v1.73.1
### Changed
- Upgraded to Code 1.73.1
### Added
- `/security.txt` added as a route with info on our security policy information thanks to @ghuntley
### Fixed
- Installing on majaro images should now work thanks to @MrPeacockNLB for
adding the `--noconfirm` flag in `install.sh`
### Known Issues
- `--cert` on Ubuntu 22.04: OpenSSL v3 is used which breaks `pem` meaning the
`--cert` feature will not work. [Reference](https://github.com/adobe/fetch/pull/318#issuecomment-1306070259)
## [4.8.3](https://github.com/coder/code-server/releases/tag/v4.8.3) - 2022-11-07
Code v1.72.1

View File

@@ -29,7 +29,7 @@ This directory contains scripts used for the development of code-server.
- [./ci/dev/watch.ts](./dev/watch.ts) (`yarn watch`)
- Starts a process to build and launch code-server and restart on any code changes.
- Example usage in [./docs/CONTRIBUTING.md](../docs/CONTRIBUTING.md).
- [./ci/dev/gen_icons.sh](./ci/dev/gen_icons.sh) (`yarn icons`)
- [./ci/dev/gen_icons.sh](./dev/gen_icons.sh) (`yarn icons`)
- Generates the various icons from a single `.svg` favicon in
`src/browser/media/favicon.svg`.
- Requires [imagemagick](https://imagemagick.org/index.php)
@@ -75,7 +75,7 @@ You can disable minification by setting `MINIFY=`.
This directory contains the release docker container image.
- [./ci/steps/build-docker-buildx-push.sh](./ci/steps/docker-buildx-push.sh)
- [./ci/steps/build-docker-buildx-push.sh](./steps/docker-buildx-push.sh)
- Builds the release containers with tags `codercom/code-server-$ARCH:$VERSION` for amd64 and arm64 with `docker buildx` and pushes them.
- Assumes debian releases are ready in `./release-packages`.

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
chmod +x out/node/entry.js
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 "$@"

View File

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

View File

@@ -24,6 +24,8 @@ main() {
rsync ./ci/build/code-server.sh "$RELEASE_PATH/bin/code-server"
rsync "$node_path" "$RELEASE_PATH/lib/node"
chmod 755 "$RELEASE_PATH/lib/node"
pushd "$RELEASE_PATH"
npm install --unsafe-perm --omit=dev
popd

View File

@@ -15,7 +15,7 @@ copy-bin-script() {
local dest="lib/vscode-reh-web-linux-x64/bin/$script"
cp "lib/vscode/resources/server/bin/$script" "$dest"
sed -i.bak "s/@@VERSION@@/$(vscode_version)/g" "$dest"
sed -i.bak "s/@@COMMIT@@/$VSCODE_DISTRO_COMMIT/g" "$dest"
sed -i.bak "s/@@COMMIT@@/$BUILD_SOURCEVERSION/g" "$dest"
sed -i.bak "s/@@APPNAME@@/code-server/g" "$dest"
# Fix Node path on Darwin and Linux.
@@ -51,8 +51,8 @@ main() {
# Set the commit Code will embed into the product.json. We need to do this
# since Code tries to get the commit from the `.git` directory which will fail
# as it is a submodule.
export VSCODE_DISTRO_COMMIT
VSCODE_DISTRO_COMMIT=$(git rev-parse HEAD)
export BUILD_SOURCEVERSION
BUILD_SOURCEVERSION=$(git rev-parse HEAD)
# Add the date, our name, links, and enable telemetry (this just makes
# telemetry available; telemetry can still be disabled by flag or setting).
@@ -109,6 +109,15 @@ EOF
popd
pushd lib/vscode-reh-web-linux-x64
# Make sure Code took the version we set in the environment variable. Not
# having a version will break display languages.
if ! jq -e .commit product.json; then
echo "'commit' is missing from product.json"
exit 1
fi
popd
# These provide a `code-server` command in the integrated terminal to open
# files in the current instance.
delete-bin-script remote-cli/code-server

View File

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

View File

@@ -1,18 +1,8 @@
#!/usr/bin/env sh
set -eu
# Copied from ../lib.sh.
arch() {
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.
# Copied from ../lib.sh except we do not rename Darwin and we do not need to
# detect Alpine.
os() {
osname=$(uname | tr '[:upper:]' '[:lower:]')
case $osname in
@@ -61,7 +51,6 @@ symlink_bin_script() {
cd "$oldpwd"
}
ARCH="${NPM_CONFIG_ARCH:-$(arch)}"
OS="$(os)"
# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2
@@ -102,14 +91,6 @@ main() {
;;
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
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"
@@ -124,27 +105,18 @@ main() {
}
install_with_yarn_or_npm() {
echo "User agent: ${npm_config_user_agent-none}"
# NOTE@edvincent: We want to keep using the package manager that the end-user was using to install the package.
# This also ensures that when *we* run `yarn` in the development process, the yarn.lock file is used.
case "${npm_config_user_agent-}" in
yarn*)
if [ -f "yarn.lock" ]; then
yarn --production --frozen-lockfile --no-default-rc
else
echo "yarn.lock file not present, not running in development mode. use npm to install code-server!"
exit 1
fi
;;
npm*)
if [ -f "yarn.lock" ]; then
echo "yarn.lock file present, running in development mode. use yarn to install code-server!"
exit 1
else
# HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases.
# The legacy behavior doesn't complain about pre-releases being used, falling back to that for now.
# See https://github.com//pull/5071
npm install --unsafe-perm --legacy-peer-deps --omit=dev
fi
# HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases.
# The legacy behavior doesn't complain about pre-releases being used, falling back to that for now.
# See https://github.com//pull/5071
npm install --unsafe-perm --legacy-peer-deps --omit=dev
;;
yarn*)
yarn --production --frozen-lockfile --no-default-rc
;;
*)
echo "Could not determine which package manager is being used to install code-server"

View File

@@ -11,22 +11,6 @@ main() {
make -s out/index.js
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
# information. We must also run it from the root otherwise coverage will not
# 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 { onLine, OnLineCallback } from "../../src/node/util"
@@ -30,12 +30,13 @@ class Watcher {
// Pass CLI args, save for `node` and the initial script name.
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
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
@@ -82,10 +83,10 @@ class Watcher {
private parseVSCodeLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return
console.log("[VS Code]", originalLine)
console.log("[Code OSS]", originalLine)
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()
}
}
@@ -93,10 +94,10 @@ class Watcher {
private parseCodeServerLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return
console.log("[Compiler][Code Server]", originalLine)
console.log("[Compiler][code-server]", originalLine)
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()
}
}

View File

@@ -15,9 +15,9 @@ type: application
# 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.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 3.3.3
version: 3.10.1
# 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
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 4.8.3
appVersion: 4.14.1

View File

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

View File

@@ -14,6 +14,12 @@ spec:
targetPort: http
protocol: TCP
name: http
{{- range .Values.extraPorts }}
- port: {{ .port }}
targetPort: {{ .port }}
protocol: {{ .protocol }}
name: {{ .name }}
{{- end }}
selector:
app.kubernetes.io/name: {{ include "code-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -6,7 +6,7 @@ replicaCount: 1
image:
repository: codercom/code-server
tag: '4.8.3'
tag: '4.14.1'
pullPolicy: Always
# Specifies one or more secrets to be used when pulling images from a
@@ -179,6 +179,7 @@ extraInitContainers: |
extraSecretMounts: []
# - name: secret-files
# mountPath: /etc/secrets
# subPath: private.key # (optional)
# secretName: code-server-secret-files
# readOnly: true
@@ -196,3 +197,8 @@ extraConfigmapMounts: []
# subPath: certificates.crt # (optional)
# configMap: certs-configmap
# readOnly: true
extraPorts: []
# - name: minecraft
# port: 25565
# protocol: tcp

View File

@@ -11,9 +11,10 @@
- [Version updates to Code](#version-updates-to-code)
- [Patching Code](#patching-code)
- [Build](#build)
- [Creating a Standalone Release](#creating-a-standalone-release)
- [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)
- ["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)
- [Test](#test)
- [Unit tests](#unit-tests)
@@ -170,6 +171,22 @@ yarn package
> If you need your builds to support older distros, run the build commands
> 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
#### 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`.
### "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`.

View File

@@ -26,6 +26,7 @@
- [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)
- [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 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)
@@ -33,6 +34,7 @@
- [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 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 -->
<!-- prettier-ignore-end -->
@@ -86,6 +88,12 @@ app (PWA):
1. Start the editor
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.
## 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.
## 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?
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?
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
leverage modern VS Code APIs and stay up to date with the upsteam version.
browser. OpenVSCode-Server is a direct fork of VS Code with changes comitted
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.
code-server includes some other features:
- 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).
However, OpenVSCode-Server is scoped at only making VS Code available as-is in
the web browser. code-server contains additional changes to make the self-hosted
experience better (see the next section for details).
## 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
GitHub and Microsoft.
On the other hand, code-server is self-hosted, free, open-source, and
can be run on any machine with few limitations.
On the other hand, code-server is self-hosted, free, open-source, and can be run
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?
@@ -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 set the environment variable `CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or
`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
1. Go to GitHub Actions > Draft release > Run workflow off commit you want to
release. CI will automatically upload the artifacts to the release. Make sure CI
has finished on that commit.
1. CI will automatically grab the
artifacts, publish the NPM package from `npm-package`, and publish the Docker
Hub image from `release-images`.
1. Publish release.
1. After, create a new branch called `release/v0.0.0` (replace 0s with actual version aka v4.5.0)
1. Summarize the major changes in the `CHANGELOG.md`
1. Bump chart version in `Chart.yaml`.
1. Go to GitHub Actions > Draft release > Run workflow on the commit you want to
release. Make sure CI has finished the build workflow on that commit or this
will fail.
2. CI will automatically grab the build artifact on that commit, inject the
version into the `package.json`, put together platform-specific packages, and
upload those packages to a draft release.
3. Summarize the major changes in the `CHANGELOG.md`.
4. Copy the relevant changelog section to the release then publish it.
5. CI will automatically publish the NPM package, Docker image, and update
Homebrew using the published release assets.
6. Bump the chart version in `Chart.yaml` and merge in the changelog updates.
#### 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. use `0.0.0-rc.0`
1. When you publish the release, select "pre-release"
1. Add a `-rc.<number>` suffix to the version.
2. When you publish the release select "pre-release". CI will not automatically
publish pre-releases.
3. Do not update the chart version or merge in the changelog until the final
release.
#### AUR

View File

@@ -16,7 +16,7 @@ access it in the browser.
## 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.
**TL;DR:** Linux machine with WebSockets enabled, 1 GB RAM, and 2 vCPUs

View File

@@ -21,3 +21,11 @@ nvm use 16
8. Install code-server globally on device with: `npm install --global code-server --unsafe-perm`
9. Run code-server with `code-server`
10. Access on localhost:8080 in your browser
# Running code-server using Nix-on-Droid
1. Install Nix-on-Droid from [F-Droid](https://f-droid.org/packages/com.termux.nix/)
2. Start app
3. Spawn a shell with code-server by running `nix-shell -p code-server`
4. Run code-server with `code-server`
5. Access on localhost:8080 in your browser

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,10 +14,12 @@
- [Accessing web services](#accessing-web-services)
- [Using a subdomain](#using-a-subdomain)
- [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)
- [Proxying to create a React app](#proxying-to-create-a-react-app)
- [Proxying to a Vue app](#proxying-to-a-vue-app)
- [Proxying to an Angular app](#proxying-to-an-angular-app)
- [Proxying to a Svelte app](#proxying-to-a-svelte-app)
- [SSH into code-server on VS Code](#ssh-into-code-server-on-vs-code)
- [Option 1: cloudflared tunnel](#option-1-cloudflared-tunnel)
- [Option 2: ngrok tunnel](#option-2-ngrok-tunnel)
@@ -316,12 +318,32 @@ To set your domain, start code-server with the `--proxy-domain` flag:
code-server --proxy-domain <domain>
```
Now you can browse to `<port>.<domain>`. Note that this uses the host header, so
ensure your reverse proxy (if you're using one) forwards that information.
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 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
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
@@ -393,6 +415,27 @@ In order to use code-server's built-in proxy with Angular, you need to make the
For additional context, see [this GitHub Discussion](https://github.com/coder/code-server/discussions/5439#discussioncomment-3371983).
### Proxying to a Svelte app
In order to use code-server's built-in proxy with Svelte, you need to make the following changes in your app:
1. Add `svelte.config.js` if you don't already have one
2. Update the values to match this (you can use any free port):
```js
const config = {
kit: {
paths: {
base: "/absproxy/5173",
},
},
}
```
3. Access app at `<code-server-root>/absproxy/5173/` e.g. `http://localhost:8080/absproxy/5173/
For additional context, see [this Github Issue](https://github.com/sveltejs/kit/issues/2958)
## SSH into code-server on VS Code
[![SSH](https://img.shields.io/badge/SSH-363636?style=for-the-badge&logo=GNU+Bash&logoColor=ffffff)](https://ohmyz.sh/) [![Terminal](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff)](https://img.shields.io/badge/Terminal-2E2E2E?style=for-the-badge&logo=Windows+Terminal&logoColor=ffffff) [![Visual Studio Code](https://img.shields.io/badge/Visual_Studio_Code-007ACC?style=for-the-badge&logo=Visual+Studio+Code&logoColor=ffffff)](vscode:extension/ms-vscode-remote.remote-ssh)

View File

@@ -297,9 +297,9 @@ You can install code-server using the [Helm package manager](https://coder.com/d
## 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).
> 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.
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).
## 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",
"children": [
{
"title": "--link",
"description": "How to run code-server --link",
"path": "./link.md"
"title": "Coder",
"description": "How to run code-server in Coder",
"path": "./coder.md"
},
{
"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
```
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:

View File

@@ -8,11 +8,11 @@
- [Upgrade](#upgrade)
- [Known Issues](#known-issues)
- [Git won't work in `/sdcard`](#git-wont-work-in-sdcard)
- [Many extensions including language packs fail to install](#many-extensions-including-language-packs-fail-to-install)
- [Extra](#extra)
- [Create a new user](#create-a-new-user)
- [Install Go](#install-go)
- [Install Python](#install-python)
- [Working with PRoot](#working-with-proot)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- prettier-ignore-end -->
@@ -20,53 +20,9 @@
## Install
1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**.
2. Install Debian by running the following:
- Run `termux-setup-storage` to allow storage access, or else code-server won't be able to read from `/sdcard`.\
> The following command is from [proot-distro](https://github.com/termux/proot-distro), but you can also use [Andronix](https://andronix.app/).
> 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).
2. Run `pkg install tur-repo`
3. Run `pkg install code-server`
4. You can now start code server by simply running `code-server`.
## NPM Installation
@@ -132,6 +88,37 @@ Potential Workaround :
1. Create a soft-link from the debian-fs to your folder in `/sdcard`
2. Use git from termux (preferred)
### Many extensions including language packs fail to install
Issue: Android is not seen as a Linux environment but as a separate, unsupported platform, so code-server only allows [Web Extensions](https://code.visualstudio.com/api/extension-guides/web-extensions), refusing to download extensions that run on the server.\
Fix: None\
Potential workarounds :
Either
- Manually download extensions as `.vsix` file and install them via `Extensions: Install from VSIX...` in the Command Palette.
- Use an override to pretend the platform is Linux:
Create a JS script that patches `process.platform`:
```js
// android-as-linux.js
Object.defineProperty(process, "platform", {
get() {
return "linux"
},
})
```
Then use Node's `--require` option to make sure it is loaded before `code-server` starts:
```sh
NODE_OPTIONS="--require /path/to/android-as-linux.js" code-server
```
⚠️ Note that Android and Linux are not 100% compatible, so use these workarounds at your own risk. Extensions that have native dependencies other than Node or that directly interact with the OS might cause issues.
## Extra
### Create a new user
@@ -202,35 +189,3 @@ eval "$(pyenv virtualenv-init -)"
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.
> 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).

30
flake.lock generated
View File

@@ -1,12 +1,15 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
@@ -17,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1660639432,
"narHash": "sha256-2WDiboOCfB0LhvnDVMXOAr8ZLDfm3WdO54CkoDPwN1A=",
"lastModified": 1683594133,
"narHash": "sha256-iUhLhEAgOCnexSGDsYT2ouydis09uDoNzM7UC685XGE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6c6409e965a6c883677be7b9d87a95fab6c3472e",
"rev": "8d447c5626cfefb9b129d5b30103344377fe09bc",
"type": "github"
},
"original": {
@@ -34,6 +37,21 @@
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@@ -12,11 +12,11 @@
in {
devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
nodejs yarn' python pkg-config git rsync jq moreutils quilt bats
nodejs yarn' python3 pkg-config git rsync jq moreutils quilt bats
];
buildInputs = with pkgs; (lib.optionals (!stdenv.isDarwin) [ libsecret ]
++ (with xorg; [ libX11 libxkbfile ])
++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
AppKit Cocoa CoreServices Security xcbuild
]));
};

View File

@@ -46,7 +46,7 @@ Usage:
Sets the prefix used by standalone release archives. Defaults to ~/.local
The release is unarchived into ~/.local/lib/code-server-X.X.X
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>
Specifies the remote shell for remote installation. Defaults to ssh.

View File

@@ -49,7 +49,6 @@
"@types/proxy-from-env": "^1.0.1",
"@types/safe-compare": "^1.1.0",
"@types/semver": "^7.1.0",
"@types/split2": "^3.2.0",
"@types/trusted-types": "^2.0.2",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.41.0",
@@ -64,7 +63,7 @@
"prettier": "2.8.0",
"prettier-plugin-sh": "^0.12.8",
"ts-node": "^10.0.0",
"typescript": "^4.6.2"
"typescript": "^5.0.4"
},
"resolutions": {
"ansi-regex": "^5.0.1",
@@ -83,27 +82,28 @@
"nanoid": "^3.1.31",
"minimist": "npm:minimist-lite@2.2.1",
"glob-parent": "^6.0.1",
"@types/node": "^16.0.0"
"@types/node": "^16.0.0",
"qs": "^6.7.3"
},
"dependencies": {
"@coder/logger": "^3.0.0",
"argon2": "0.30.2",
"argon2": "0.30.3",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"env-paths": "^2.2.0",
"express": "5.0.0-alpha.8",
"http-proxy": "^1.18.0",
"httpolyglot": "^0.1.2",
"i18next": "^23.2.11",
"js-yaml": "^4.0.0",
"limiter": "^1.1.5",
"limiter": "^2.1.0",
"pem": "^1.14.2",
"proxy-agent": "^5.0.0",
"proxy-agent": "^6.2.1",
"qs": "6.11.0",
"rotating-file-stream": "^3.0.0",
"safe-buffer": "^5.1.1",
"safe-compare": "^1.1.4",
"semver": "^7.1.3",
"split2": "^4.0.0",
"ws": "^8.0.0",
"xdg-basedir": "^4.0.0"
},
@@ -116,7 +116,8 @@
"ide",
"coder",
"vscode-remote",
"browser-ide"
"browser-ide",
"remote-development"
],
"engines": {
"node": "16"

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/lib/vscode/src/vs/base/common/network.ts
@@ -162,7 +162,9 @@ class RemoteAuthoritiesImpl {
@@ -168,7 +168,9 @@ class RemoteAuthoritiesImpl {
return URI.from({
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
authority: `${host}:${port}`,
@@ -99,26 +99,19 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
+++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -274,6 +274,7 @@ export class BrowserSocketFactory implem
connect(host: string, port: number, path: string, query: string, debugLabel: string, callback: IConnectCallback): void {
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
const errorListener = socket.onError((err) => callback(err, undefined));
socket.onOpen(() => {
@@ -282,6 +283,3 @@ export class BrowserSocketFactory implem
});
}
}
-
-
-
@@ -280,6 +280,7 @@ export class BrowserSocketFactory implem
connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> {
return new Promise<ISocket>((resolve, reject) => {
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
const errorListener = socket.onError(reject);
socket.onOpen(() => {
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -267,12 +267,11 @@ export class WebClientServer {
@@ -268,16 +268,15 @@ export class WebClientServer {
return void res.end();
}
@@ -127,33 +120,33 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
- return Array.isArray(val) ? val[0] : val;
- };
-
- const remoteAuthority = getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host;
const useTestResolver = (!this._environmentService.isBuilt && this._environmentService.args['use-test-resolver']);
+ // For now we are getting the remote authority from the client to avoid
+ // needing specific configuration for reverse proxies to work. Set this to
+ // something invalid to make sure we catch code that is using this value
+ // from the backend when it should not.
+ const remoteAuthority = 'remote';
const remoteAuthority = (
useTestResolver
? 'test+test'
- : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host)
+ : 'remote'
);
if (!remoteAuthority) {
return serveError(req, res, 400, `Bad request.`);
}
@@ -298,6 +297,8 @@ export class WebClientServer {
@@ -304,8 +303,12 @@ export class WebClientServer {
scopes: [['user:email'], ['repo']]
} : undefined;
+ const base = relativeRoot(getOriginalUrl(req))
+ const vscodeBase = relativePath(getOriginalUrl(req))
const workbenchWebConfiguration = {
remoteAuthority,
@@ -309,6 +310,7 @@ export class WebClientServer {
workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']),
productConfiguration: <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion,
+ rootEndpoint: base,
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery,
@@ -326,8 +328,10 @@ export class WebClientServer {
+
const productConfiguration = <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion,
+ rootEndpoint: base,
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery,
@@ -340,8 +343,10 @@ export class WebClientServer {
const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
@@ -165,17 +158,17 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
+ VS_BASE: vscodeBase,
};
@@ -344,7 +348,7 @@ export class WebClientServer {
if (useTestResolver) {
@@ -366,7 +371,7 @@ export class WebClientServer {
'default-src \'self\';',
'img-src \'self\' https: data: blob:;',
'media-src \'self\';',
- `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' http://${remoteAuthority};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
+ `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=';`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
- `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
+ `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' ${useTestResolver ? '' : ''};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
'child-src \'self\';',
`frame-src 'self' https://*.vscode-cdn.net data:;`,
'worker-src \'self\' data:;',
@@ -417,3 +421,70 @@ export class WebClientServer {
'worker-src \'self\' data: blob:;',
@@ -439,3 +444,70 @@ export class WebClientServer {
return void res.end(data);
}
}
@@ -250,7 +243,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/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 {
readonly codeServerVersion?: string
@@ -262,7 +255,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
@@ -489,6 +489,7 @@ function doCreateUri(path: string, query
@@ -484,6 +484,7 @@ function doCreateUri(path: string, query
});
}
@@ -270,7 +263,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
return URI.parse(window.location.href).with({ path, query });
}
@@ -500,7 +501,7 @@ function doCreateUri(path: string, query
@@ -495,7 +496,7 @@ function doCreateUri(path: string, query
if (!configElement || !configElementAttribute) {
throw new Error('Missing web configuration element');
}
@@ -289,12 +282,12 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { RemoteAuthorities } from 'vs/base/common/network';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -75,7 +74,7 @@ export abstract class AbstractExtensionR
public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined {
if (this._extensionGalleryResourceUrlTemplate) {
const uri = URI.parse(format2(this._extensionGalleryResourceUrlTemplate, { publisher: galleryExtension.publisher, name: galleryExtension.name, version: galleryExtension.version, path: 'extension' }));
@@ -102,7 +101,7 @@ export abstract class AbstractExtensionR
: version,
path: 'extension'
}));
- return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri;
+ return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri;
}

View File

@@ -8,7 +8,7 @@ To test:
2. Open code-server
3. Open terminal
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
terminal.
@@ -17,7 +17,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
@@ -100,10 +100,14 @@ class RemoteTerminalBackend extends Base
@@ -103,10 +103,14 @@ class RemoteTerminalBackend extends Base
}
const reqId = e.reqId;
const commandId = e.commandId;

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/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') {
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) {
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/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -271,6 +271,11 @@ export interface IWorkbenchConstructionO
@@ -288,6 +288,11 @@ export interface IWorkbenchConstructionO
*/
readonly userDataPath?: string
@@ -23,24 +23,24 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -32,6 +32,11 @@ export interface IBrowserWorkbenchEnviro
* Options used to configure the workbench.
*/
@@ -34,6 +34,11 @@ export interface IBrowserWorkbenchEnviro
readonly options?: IWorkbenchConstructionOptions;
+
+ /**
/**
+ * Enable downloading files via menu actions.
+ */
+ readonly isEnabledFileDownloads?: boolean;
}
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -87,6 +92,13 @@ export class BrowserWorkbenchEnvironment
+
+ /**
* Gets whether a resolver extension is expected for the environment.
*/
readonly expectsResolverExtension: boolean;
@@ -111,6 +116,13 @@ export class BrowserWorkbenchEnvironment
return this.options.userDataPath;
}
@@ -66,7 +66,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */
@@ -94,6 +95,7 @@ export interface ServerParsedArgs {
@@ -95,6 +96,7 @@ export interface ServerParsedArgs {
/* ----- code-server ----- */
'disable-update-check'?: boolean;
'auth'?: string
@@ -78,7 +78,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -304,6 +304,7 @@ export class WebClientServer {
@@ -331,6 +331,7 @@ export class WebClientServer {
remoteAuthority,
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
userDataPath: this._environmentService.userDataPath,
@@ -90,29 +90,22 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@@ -7,12 +7,11 @@ import { Event } from 'vs/base/common/ev
@@ -7,12 +7,12 @@ import { Event } from 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle';
import { 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 { 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, 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 { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService';
@@ -25,6 +24,7 @@ import { IPaneCompositePartService } fro
import { Schemas } from 'vs/base/common/network';
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
import { IProductService } from 'vs/platform/product/common/productService';
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
export class WorkbenchContextKeysHandler extends Disposable {
private inputFocusedContext: IContextKey<boolean>;
@@ -77,7 +77,7 @@ export class WorkbenchContextKeysHandler
@@ -80,7 +80,7 @@ export class WorkbenchContextKeysHandler
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -121,7 +114,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@IProductService private readonly productService: IProductService,
@IEditorService private readonly editorService: IEditorService,
@IEditorResolverService private readonly editorResolverService: IEditorResolverService,
@@ -202,6 +202,9 @@ export class WorkbenchContextKeysHandler
@@ -210,6 +210,9 @@ export class WorkbenchContextKeysHandler
this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
@@ -143,8 +136,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 { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
@@ -483,13 +483,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
import { ThemeIcon } from 'vs/base/common/themables';
@@ -489,13 +489,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
id: DOWNLOAD_COMMAND_ID,
title: DOWNLOAD_LABEL
},
@@ -172,9 +165,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/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);
+

View File

@@ -14,20 +14,22 @@ We can remove this once upstream supports all language packs.
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
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
===================================================================
--- code-server.orig/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
@@ -230,6 +230,9 @@ export async function setupServerService
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel);
+ const languagePackChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ILanguagePackService));
+ socketServer.registerChannel('languagePacks', languagePackChannel);
+
const encryptionChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(IEncryptionMainService));
socketServer.registerChannel('encryption', encryptionChannel);
const credentialsChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ICredentialsMainService));
socketServer.registerChannel('credentials', credentialsChannel);
Index: code-server/lib/vscode/src/vs/base/common/platform.ts
===================================================================
@@ -42,7 +44,7 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
export const LANGUAGE_DEFAULT = 'en';
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;
_isWeb = true;
@@ -56,16 +58,18 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
-
- _locale = configuredLocale || LANGUAGE_DEFAULT;
+ _locale = LANGUAGE_DEFAULT;
_language = _locale;
_platformLocale = navigator.language;
+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
+ const rawNlsConfig = el && el.getAttribute('data-settings');
+ if (rawNlsConfig) {
+ try {
+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
+ const resolved = nlsConfig.availableLanguages['*'];
+ _locale = nlsConfig.locale;
+ _platformLocale = nlsConfig.osLocale;
+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
+ _translationsConfigFile = nlsConfig._translationsConfigFile;
+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT;
+ } 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/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'));
}
@@ -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/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)) {
value._languagePackSupport = true;
}
@@ -151,7 +155,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
return value;
});
_cache.set(key, result);
@@ -44,3 +50,43 @@ export namespace InternalNLSConfiguratio
@@ -46,3 +52,43 @@ export namespace InternalNLSConfiguratio
return candidate && typeof candidate._languagePackId === 'string';
}
}
@@ -206,24 +210,24 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
+import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
import { CharCode } from 'vs/base/common/charCode';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@@ -343,6 +344,8 @@ export class WebClientServer {
callbackRoute: this._callbackRoute
};
@@ -299,6 +300,8 @@ export class WebClientServer {
const base = relativeRoot(getOriginalUrl(req))
const vscodeBase = relativePath(getOriginalUrl(req))
+ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
+ const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
const workbenchWebConfiguration = {
remoteAuthority,
@@ -336,6 +339,7 @@ export class WebClientServer {
const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
@@ -351,6 +354,7 @@ export class WebClientServer {
WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
BASE: base,
VS_BASE: vscodeBase,
+ NLS_CONFIGURATION: asJSON(nlsConfiguration),
};
if (useTestResolver) {
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -236,7 +240,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */
@@ -96,6 +97,7 @@ export interface ServerParsedArgs {
@@ -97,6 +98,7 @@ export interface ServerParsedArgs {
'disable-update-check'?: boolean;
'auth'?: string
'disable-file-downloads'?: boolean;
@@ -248,9 +252,18 @@ Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
@@ -119,8 +119,9 @@ import 'vs/workbench/contrib/logs/browse
// Explorer
import 'vs/workbench/contrib/files/browser/files.web.contribution';
@@ -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';
@@ -117,8 +117,9 @@ registerSingleton(ILanguagePackService,
// Logs
import 'vs/workbench/contrib/logs/browser/logs.contribution';
-// Localization
-import 'vs/workbench/contrib/localization/browser/localization.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/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 { Language } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
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'))
}
async getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined> {
@@ -73,6 +79,6 @@ export class WebLanguagePacksService ext
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
@@ -72,6 +78,6 @@ export class WebLanguagePacksService ext
// Web doesn't have a concept of language packs, so we just return an empty array
getInstalledLanguages(): Promise<ILanguagePackItem[]> {
@@ -298,11 +311,11 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack
+ 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/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts
@@ -41,7 +41,8 @@ export class NativeLocaleService impleme
--- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
@@ -51,7 +51,8 @@ class NativeLocaleService implements ILo
@IProductService private readonly productService: IProductService
) { }
@@ -312,7 +325,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-san
try {
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> {
@@ -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);
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
@@ -337,9 +337,6 @@ export class InstallAction extends Exten
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();
@@ -607,7 +604,7 @@ export abstract class InstallInOtherServ
}
if (isLanguagePackExtension(this.extension.local.manifest)) {
- return true;
+ return false;
}
// Prefers to run on UI
@@ -1695,17 +1692,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> {
@@ -1722,7 +1708,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);
@@ -1732,17 +1717,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,16 +10,25 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
@@ -62,7 +62,7 @@ import { GettingStartedIndexList } from
@@ -10,7 +10,7 @@ import { IInstantiationService } from 'v
import { IEditorSerializer, IEditorOpenContext } from 'vs/workbench/common/editor';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { assertIsDefined } from 'vs/base/common/types';
-import { $, addDisposableListener, append, clearNode, Dimension, reset } from 'vs/base/browser/dom';
+import { $, addDisposableListener, append, clearNode, Dimension, reset, prepend } from 'vs/base/browser/dom';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IProductService } from 'vs/platform/product/common/productService';
import { hiddenEntriesConfigurationKey, IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService';
@@ -59,7 +59,7 @@ import { GettingStartedIndexList } from
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils';
-import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
+import { IsEnabledCoderGettingStarted, WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
import { OpenFolderAction, OpenFileFolderAction, OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions';
import { Toggle } from 'vs/base/browser/ui/toggle/toggle';
@@ -758,6 +758,72 @@ export class GettingStartedPage extends
@@ -782,6 +782,72 @@ export class GettingStartedPage extends
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
);
@@ -72,7 +81,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ 'align-items: center',
+ ].join(';'),
+ }, 'Get started ', $('span', {
+ class: Codicon.arrowRight.classNames,
+ class: ThemeIcon.asClassName(Codicon.arrowRight),
+ style: [
+ 'color: white',
+ 'margin-left: 8px',
@@ -87,38 +96,21 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ ),
+ ),
+ );
+ }
+ }
+
const leftColumn = $('.categories-column.categories-column-left', {},);
const rightColumn = $('.categories-column.categories-column-right', {},);
@@ -775,13 +841,23 @@ export class GettingStartedPage extends
const layoutLists = () => {
if (gettingStartedList.itemCount) {
this.container.classList.remove('noWalkthroughs');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, gettingStartedList.getDomElement());
+ }
+
@@ -831,6 +897,9 @@ export class GettingStartedPage extends
recentList.setLimit(5);
reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
}
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);
}
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ prepend(rightColumn, gettingStartedCoder)
+ }
};
featuredExtensionList.onDidChange(layoutFeaturedExtension);
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
@@ -143,7 +135,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -276,6 +276,11 @@ export interface IWorkbenchConstructionO
@@ -293,6 +293,11 @@ export interface IWorkbenchConstructionO
*/
readonly isEnabledFileDownloads?: boolean
@@ -154,24 +146,24 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -37,6 +37,11 @@ export interface IBrowserWorkbenchEnviro
* Enable downloading files via menu actions.
*/
@@ -39,6 +39,11 @@ export interface IBrowserWorkbenchEnviro
readonly isEnabledFileDownloads?: boolean;
+
+ /**
/**
+ * Enable Coder's custom getting started text.
+ */
+ readonly isEnabledCoderGettingStarted?: boolean;
}
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -99,6 +104,13 @@ export class BrowserWorkbenchEnvironment
+
+ /**
* Gets whether a resolver extension is expected for the environment.
*/
readonly expectsResolverExtension: boolean;
@@ -123,6 +128,13 @@ export class BrowserWorkbenchEnvironment
return this.options.isEnabledFileDownloads;
}
@@ -197,11 +189,11 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */
@@ -98,6 +99,7 @@ export interface ServerParsedArgs {
@@ -99,6 +100,7 @@ export interface ServerParsedArgs {
'auth'?: string
'disable-file-downloads'?: boolean;
'locale'?: string
+ 'disable-getting-started-override'?: boolean;
+ 'disable-getting-started-override'?: boolean,
/* ----- server setup ----- */
@@ -209,7 +201,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -308,6 +308,7 @@ export class WebClientServer {
@@ -334,6 +334,7 @@ export class WebClientServer {
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
userDataPath: this._environmentService.userDataPath,
isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'],
@@ -223,14 +215,14 @@ Index: 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
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 { 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, 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 { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -204,6 +204,7 @@ export class WorkbenchContextKeysHandler
@@ -212,6 +212,7 @@ export class WorkbenchContextKeysHandler
// code-server
IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true)
@@ -242,8 +234,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/lib/vscode/src/vs/workbench/common/contextkeys.ts
@@ -33,6 +33,7 @@ export const IsFullscreenContext = new R
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
@@ -36,6 +36,7 @@ export const HasWebFileSystemAccess = ne
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));
export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', 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 MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
-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');
-args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
-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 MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
+ 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');
+ args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
+ 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/lib/vscode/src/vs/base/common/processes.ts
@@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme
/^VSCODE_(?!SHELL_LOGIN).+$/,
/^VSCODE_(?!(PORTABLE|SHELL_LOGIN|ENV_REPLACE|ENV_APPEND|ENV_PREPEND)).+$/,
/^SNAP(|_.*)$/,
/^GDK_PIXBUF_.+$/,
+ /^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/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
@@ -143,8 +143,11 @@ export class BrowserDialogHandler implem
@@ -77,8 +77,11 @@ export class BrowserDialogHandler extend
async about(): Promise<void> {
const detailString = (useAgo: boolean): string => {
@@ -176,7 +176,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
@@ -69,6 +69,7 @@ import { IndexedDB } from 'vs/base/brows
@@ -67,6 +67,7 @@ import { IndexedDB } from 'vs/base/brows
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
@@ -184,7 +184,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel';
@@ -117,6 +118,9 @@ export class BrowserMain extends Disposa
@@ -127,6 +128,9 @@ export class BrowserMain extends Disposa
// 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/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 {
@@ -264,11 +264,11 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -308,6 +308,7 @@ export class WebClientServer {
folderUri: resolveWorkspaceURI(this._environmentService.args['default-folder']),
workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']),
productConfiguration: <Partial<IProductConfiguration>>{
+ codeServerVersion: this._productService.codeServerVersion,
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery,
@@ -305,6 +305,7 @@ export class WebClientServer {
} : undefined;
const productConfiguration = <Partial<IProductConfiguration>>{
+ codeServerVersion: this._productService.codeServerVersion,
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery,

View File

@@ -20,7 +20,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -303,6 +303,7 @@ export class WebClientServer {
@@ -326,6 +326,7 @@ export class WebClientServer {
const workbenchWebConfiguration = {
remoteAuthority,
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
@@ -32,7 +32,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -266,6 +266,11 @@ export interface IWorkbenchConstructionO
@@ -283,6 +283,11 @@ export interface IWorkbenchConstructionO
*/
readonly configurationDefaults?: Record<string, any>;
@@ -43,12 +43,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -78,7 +78,14 @@ export class BrowserWorkbenchEnvironment
@@ -102,7 +102,14 @@ export class BrowserWorkbenchEnvironment
get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }
@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/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 rootEndpoint?: string
readonly updateEndpoint?: string
@@ -28,7 +28,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */
@@ -92,6 +93,7 @@ export const serverOptions: OptionDescri
@@ -93,6 +94,7 @@ export const serverOptions: OptionDescri
export interface ServerParsedArgs {
/* ----- code-server ----- */
'disable-update-check'?: boolean;
@@ -40,14 +40,14 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -313,6 +313,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
+ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
},
@@ -310,6 +310,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
+ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
};
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts

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/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
});
}
@@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -111,7 +111,7 @@ export class WebClientServer {
@@ -112,7 +112,7 @@ export class WebClientServer {
const serverRootPath = getRemoteServerRootPath(_productService);
this._staticRoute = `${serverRootPath}/static`;
this._callbackRoute = `${serverRootPath}/callback`;
@@ -49,22 +49,22 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
}
/**
@@ -312,14 +312,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
embedderIdentifier: 'server-distro',
- extensionsGallery: this._webExtensionResourceUrlTemplate ? {
- ...this._productService.extensionsGallery,
- 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
- scheme: 'http',
- authority: remoteAuthority,
- path: `${this._webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}`
- }).toString(true)
- } : undefined
+ extensionsGallery: this._productService.extensionsGallery,
},
callbackRoute: this._callbackRoute
@@ -310,14 +310,7 @@ export class WebClientServer {
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
embedderIdentifier: 'server-distro',
- extensionsGallery: this._webExtensionResourceUrlTemplate ? {
- ...this._productService.extensionsGallery,
- 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
- scheme: 'http',
- authority: remoteAuthority,
- path: `${this._webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}`
- }).toString(true)
- } : undefined
+ extensionsGallery: this._productService.extensionsGallery,
};
if (!this._environmentService.isBuilt) {
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
@@ -74,10 +74,10 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -60,7 +59,7 @@ export abstract class AbstractExtensionR
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -77,7 +76,7 @@ export abstract class AbstractExtensionR
private readonly _environmentService: IEnvironmentService,
private readonly _configurationService: IConfigurationService,
) {

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
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
===================================================================
--- 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
@@ -364,10 +364,7 @@ function extensionDescriptionArrayToMap(
@@ -260,10 +260,7 @@ function extensionDescriptionArrayToMap(
}
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 {
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/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 updateEndpoint?: string
readonly logoutEndpoint?: string
@@ -42,70 +42,48 @@ 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/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/comm
import { RemoteAuthorities } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IProductService } from 'vs/platform/product/common/productService';
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts';
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
@@ -23,7 +23,7 @@ export class RemoteAuthorityResolverServ
private readonly _connectionToken: Promise<string> | string | undefined;
private readonly _connectionTokens: Map<string, string>;
- constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) {
+ constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, private readonly proxyEndpointTemplate?: string) {
@@ -33,7 +33,7 @@ export class RemoteAuthorityResolverServ
isWorkbenchOptionsBasedResolution: boolean,
connectionToken: Promise<string> | string | undefined,
resourceUriProvider: ((uri: URI) => URI) | undefined,
- @IProductService productService: IProductService,
+ @IProductService private readonly productService: IProductService,
@ILogService private readonly _logService: ILogService,
) {
super();
this._connectionToken = connectionToken;
this._connectionTokens = new Map<string, string>();
@@ -61,9 +61,14 @@ export class RemoteAuthorityResolverServ
private async _doResolveAuthority(authority: string): Promise<ResolverResult> {
@@ -84,9 +84,14 @@ export class RemoteAuthorityResolverServ
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;
+ if (this.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href);
+ if (this.productService.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.productService.proxyEndpointTemplate, window.location.href);
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
+ }
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort);
- const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken } };
+ const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken }, options };
RemoteAuthorities.set(authority, result.authority.host, result.authority.port);
- const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken } };
+ const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken }, options };
RemoteAuthorities.set(authority, host, port);
this._cache.set(authority, result);
this._onDidChangeConnectionData.fire();
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -314,6 +314,7 @@ export class WebClientServer {
rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
embedderIdentifier: 'server-distro',
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
@@ -311,6 +311,7 @@ export class WebClientServer {
rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
};
Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
@@ -381,7 +381,7 @@ export async function createTerminalEnvi
@@ -271,7 +271,7 @@ export async function createTerminalEnvi
// Sanitize the environment, removing any undesirable VS Code and Electron environment
// variables
@@ -126,25 +104,26 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
interface ICredential {
service: string;
@@ -511,6 +512,38 @@ function doCreateUri(path: string, query
} : undefined,
@@ -505,6 +506,39 @@ function doCreateUri(path: string, query
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
- credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider() // with a remote, we don't use a local credentials provider
+ credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider(), // with a remote, we don't use a local credentials provider
- credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider()
+ credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider(),
+ resolveExternalUri: (uri: URI): Promise<URI> => {
+ let resolvedUri = uri
+ const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
+
+ if (localhostMatch && resolvedUri.authority !== location.host) {
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
+ resolvedUri = URI.parse(new URL(config.productConfiguration.proxyEndpointTemplate.replace('{{port}}', localhostMatch.port.toString()), window.location.href).toString())
+ const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
+ .replace('{{port}}', localhostMatch.port.toString())
+ .replace('{{host}}', window.location.host)
+ resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
+ } else {
+ throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
+ }
+ }
+
+ // If not localhost, return unmodified
+ // If not localhost, return unmodified.
+ return Promise.resolve(resolvedUri)
+ },
+ tunnelProvider: {
@@ -170,7 +149,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/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
@@ -73,7 +73,7 @@ export class ForwardedPortsView extends
@@ -74,7 +74,7 @@ export class ForwardedPortsView extends
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/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 logoutEndpoint?: string
readonly proxyEndpointTemplate?: string
@@ -54,14 +54,14 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -316,6 +316,10 @@ export class WebClientServer {
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
+ serviceWorker: {
+ scope: vscodeBase + '/',
+ path: base + '/_static/out/browser/serviceWorker.js',
+ },
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
},
@@ -312,6 +312,10 @@ export class WebClientServer {
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
+ serviceWorker: {
+ scope: vscodeBase + '/',
+ path: base + '/_static/out/browser/serviceWorker.js',
+ },
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
};

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/lib/vscode/build/gulpfile.reh.js
@@ -192,8 +192,7 @@ function packageTask(type, platform, arc
@@ -242,8 +242,7 @@ function packageTask(type, platform, arc
const src = gulp.src(sourceFolderName + '/**', { base: '.' })
.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 isUIExtension = (manifest) => {
@@ -232,9 +231,9 @@ function packageTask(type, platform, arc
@@ -282,9 +281,9 @@ function packageTask(type, platform, arc
.map(name => `.build/extensions/${name}/**`);
const extensions = gulp.src(extensionPaths, { base: '.build', dot: true });
@@ -32,7 +32,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
let version = packageJson.version;
const quality = product.quality;
@@ -388,7 +387,7 @@ function tweakProductForServerWeb(produc
@@ -431,7 +430,7 @@ function tweakProductForServerWeb(produc
const minifyTask = task.define(`minify-vscode-${type}`, task.series(
optimizeTask,
util.rimraf(`out-vscode-${type}-min`),

View File

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

View File

@@ -1,7 +1,7 @@
Add support for telemetry endpoint
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:
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.
@@ -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/lib/vscode/src/vs/server/node/serverServices.ts
@@ -71,6 +71,7 @@ import { IExtensionsScannerService } fro
@@ -67,6 +67,7 @@ import { IExtensionsScannerService } fro
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
+import { TelemetryClient } from "vs/server/node/telemetryClient";
import { NullPolicyService } from 'vs/platform/policy/common/policy';
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
import { LoggerService } from 'vs/platform/log/node/loggerService';
@@ -139,10 +140,13 @@ export async function setupServerService
const machineId = await getMachineId();
@@ -149,10 +150,13 @@ export async function setupServerService
let oneDsAppender: ITelemetryAppender = NullAppender;
const isInternal = isInternalTelemetry(productService, configurationService);
if (supportsTelemetry(productService, environmentService)) {
- if (productService.aiConfig && productService.aiConfig.ariaKey) {
@@ -94,11 +94,11 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -321,6 +321,7 @@ export class WebClientServer {
scope: vscodeBase + '/',
path: base + '/_static/out/browser/serviceWorker.js',
},
+ enableTelemetry: this._productService.enableTelemetry,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
@@ -316,6 +316,7 @@ export class WebClientServer {
scope: vscodeBase + '/',
path: base + '/_static/out/browser/serviceWorker.js',
},
+ enableTelemetry: this._productService.enableTelemetry,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
};

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/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 {
readonly codeServerVersion?: string
readonly rootEndpoint?: string
@@ -105,14 +105,14 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -312,6 +312,7 @@ export class WebClientServer {
productConfiguration: <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
},
@@ -309,6 +309,7 @@ export class WebClientServer {
const productConfiguration = <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion,
rootEndpoint: base,
+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
};
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -126,7 +126,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
/* ----- server setup ----- */
@@ -88,6 +90,8 @@ export const serverOptions: OptionDescri
@@ -89,6 +91,8 @@ export const serverOptions: OptionDescri
};
export interface ServerParsedArgs {

View File

@@ -41,7 +41,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -210,7 +210,7 @@ export class BrowserWorkbenchEnvironment
@@ -225,7 +225,7 @@ export class BrowserWorkbenchEnvironment
@memoize
get webviewExternalEndpoint(): string {
@@ -54,7 +54,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -302,6 +302,7 @@ export class WebClientServer {
@@ -322,6 +322,7 @@ export class WebClientServer {
const workbenchWebConfiguration = {
remoteAuthority,
@@ -70,12 +70,12 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index
<meta charset="UTF-8">
<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-/9/YQU12wvTeVXCsIGB4shLwdWrMceCpKojfkloNjPU=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
- content="default-src 'none'; script-src 'sha256-7Y08cqii1UgeZbSST9r8UPSownOSMa3/PiKe77avh7I=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-h057XKGlOvyFweE9mchggtfdly92LQuQ9vKfYvNy7us=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming -->
<meta name="viewport"
@@ -331,6 +331,12 @@
@@ -334,6 +334,12 @@
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/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
@@ -330,6 +330,12 @@
@@ -333,6 +333,12 @@
const hostname = location.hostname;

View File

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

View File

@@ -10,7 +10,7 @@
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:;"
/>
<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="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" />
<link rel="manifest" href="{{BASE}}/manifest.json" crossorigin="use-credentials" />
@@ -25,7 +25,7 @@
<div class="card-box">
<div class="header">
<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 class="content">
<form class="login-form" method="post">
@@ -38,11 +38,11 @@
autofocus
class="password"
type="password"
placeholder="PASSWORD"
placeholder="{{I18N_PASSWORD_PLACEHOLDER}}"
name="password"
autocomplete="current-password"
/>
<input class="submit -button" value="SUBMIT" type="submit" />
<input class="submit -button" value="{{I18N_SUBMIT}}" type="submit" />
</div>
{{ERROR}}
</form>

View File

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

View File

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

View File

@@ -3,9 +3,8 @@ import { promises as fs } from "fs"
import { load } from "js-yaml"
import * as os from "os"
import * as path from "path"
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util"
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
import { generateCertificate, generatePassword, humanPath, paths, splitOnFirstEquals } from "./util"
import { EditorSessionManagerClient } from "./vscodeSocket"
export enum Feature {
// No current experimental features!
@@ -52,6 +51,7 @@ export interface UserProvidedCodeArgs {
"disable-file-downloads"?: boolean
"disable-workspace-trust"?: boolean
"disable-getting-started-override"?: boolean
"session-socket"?: string
}
/**
@@ -79,12 +79,12 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"bind-addr"?: string
socket?: string
"socket-mode"?: string
"trusted-origins"?: string[]
version?: boolean
"proxy-domain"?: string[]
"reuse-window"?: boolean
"new-window"?: boolean
"ignore-last-opened"?: boolean
link?: OptionalString
verbose?: boolean
"app-name"?: string
"welcome-text"?: string
@@ -162,6 +162,9 @@ export const options: Options<Required<UserProvidedArgs>> = {
"Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" +
"then notifies you once every week that a new release is available.",
},
"session-socket": {
type: "string",
},
"disable-file-downloads": {
type: "boolean",
description:
@@ -180,7 +183,14 @@ export const options: Options<Required<UserProvidedArgs>> = {
enable: { type: "string[]" },
help: { type: "boolean", short: "h", description: "Show this output." },
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." },
"bind-addr": {
@@ -199,6 +209,11 @@ export const options: Options<Required<UserProvidedArgs>> = {
socket: { type: "string", path: true, description: "Path to a socket (bind-addr will be ignored)." },
"socket-mode": { type: "string", description: "File mode of the socket." },
"trusted-origins": {
type: "string[]",
description:
"Disables authenticate origin check for trusted origin. Useful if not able to access reverse proxy configuration.",
},
version: { type: "boolean", short: "v", description: "Display version information." },
_: { type: "string[]" },
@@ -255,15 +270,6 @@ export const options: Options<Required<UserProvidedArgs>> = {
short: "w",
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[] => {
@@ -295,19 +301,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
* that arguments are valid types and have values when required.
@@ -441,19 +434,23 @@ export const parse = (
throw new Error("--cert-key is missing")
}
logger.debug(() => [
`parsed ${opts?.configFile ? "config" : "command line"}`,
field("args", {
...args,
password: args.password ? "<redacted>" : undefined,
"hashed-password": args["hashed-password"] ? "<redacted>" : undefined,
"github-auth": args["github-auth"] ? "<redacted>" : undefined,
}),
])
logger.debug(() => [`parsed ${opts?.configFile ? "config" : "command line"}`, field("args", redactArgs(args))])
return args
}
/**
* Redact sensitive information from arguments for logging.
*/
export const redactArgs = (args: UserProvidedArgs): UserProvidedArgs => {
return {
...args,
password: args.password ? "<redacted>" : undefined,
"hashed-password": args["hashed-password"] ? "<redacted>" : undefined,
"github-auth": args["github-auth"] ? "<redacted>" : undefined,
}
}
/**
* User-provided arguments with defaults. The distinction between user-provided
* args and defaulted args exists so we can tell the difference between end
@@ -472,6 +469,7 @@ export interface DefaultedArgs extends ConfigArgs {
usingEnvHashedPassword: boolean
"extensions-dir": string
"user-data-dir": string
"session-socket": string
/* Positional arguments. */
_: string[]
}
@@ -492,6 +490,11 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
}
if (!args["session-socket"]) {
args["session-socket"] = path.join(args["user-data-dir"], "code-server-ipc.sock")
}
process.env.CODE_SERVER_SESSION_SOCKET = args["session-socket"]
// --verbose takes priority over --log and --log takes priority over the
// environment variable.
if (args.verbose) {
@@ -540,17 +543,6 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args.host = addr.host
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) {
const { cert, certKey } = await generateCertificate(args["cert-host"] || "localhost")
args.cert = {
@@ -589,12 +581,25 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
// Filter duplicate proxy domains and remove any leading `*.`.
const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
args["proxy-domain"] = Array.from(proxyDomains)
const finalProxies = []
if (typeof args._ === "undefined") {
args._ = []
for (const proxyDomain of proxyDomains) {
if (!proxyDomain.includes("{{port}}")) {
finalProxies.push("{{port}}." + proxyDomain)
} else {
finalProxies.push(proxyDomain)
}
}
// all proxies are of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
// e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}
if (finalProxies.length > 0 && !process.env.VSCODE_PROXY_URI) {
process.env.VSCODE_PROXY_URI = `//${finalProxies[0]}`
}
args["proxy-domain"] = finalProxies
args._ = getResolvedPathsFromArgs(args)
return {
...args,
usingEnvPassword,
@@ -602,6 +607,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
}
export function getResolvedPathsFromArgs(args: UserProvidedArgs): string[] {
return (args._ ?? []).map((p) => path.resolve(p))
}
/**
* Helper function to return the default config file.
*
@@ -741,47 +750,38 @@ function bindAddrFromAllSources(...argsConfig: UserProvidedArgs[]): Addr {
return addr
}
/**
* Reads the socketPath based on path passed in.
*
* The one usually passed in is the DEFAULT_SOCKET_PATH.
*
* If it can't read the path, it throws an error and returns undefined.
*/
export async function readSocketPath(path: string): Promise<string | undefined> {
try {
return await fs.readFile(path, "utf8")
} catch (error) {
// If it doesn't exist, we don't care.
// But if it fails for some reason, we should throw.
// We want to surface that to the user.
if (!isNodeJSErrnoException(error) || error.code !== "ENOENT") {
throw error
}
}
return undefined
}
/**
* Determine if it looks like the user is trying to open a file or folder in an
* existing instance. The arguments here should be the arguments the user
* explicitly passed on the command line, *NOT DEFAULTS* or the configuration.
*/
export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Promise<string | undefined> => {
export const shouldOpenInExistingInstance = async (
args: UserProvidedArgs,
sessionSocket: string,
): Promise<string | undefined> => {
// Always use the existing instance if we're running from VS Code's terminal.
if (process.env.VSCODE_IPC_HOOK_CLI) {
logger.debug("Found VSCODE_IPC_HOOK_CLI")
return process.env.VSCODE_IPC_HOOK_CLI
}
const paths = getResolvedPathsFromArgs(args)
const client = new EditorSessionManagerClient(sessionSocket)
// If these flags are set then assume the user is trying to open in an
// existing instance since these flags have no effect otherwise.
// existing instance since these flags have no effect otherwise. That means
// if there is no existing instance we should error rather than falling back
// to spawning code-server normally.
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
return args[cur as keyof UserProvidedArgs] ? prev + 1 : prev
}, 0)
if (openInFlagCount > 0) {
logger.debug("Found --reuse-window or --new-window")
return readSocketPath(DEFAULT_SOCKET_PATH)
const socketPath = await client.getConnectedSocketPath(paths[0])
if (!socketPath) {
throw new Error(`No opened code-server instances found to handle ${paths[0]}`)
}
return socketPath
}
// It's possible the user is trying to spawn another instance of code-server.
@@ -789,9 +789,13 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
// code-server is invoked exactly like this: `code-server my-file`).
// 2. That a file or directory was passed.
// 3. That the socket is active.
// 4. That an instance exists to handle the path (implied by #3).
if (Object.keys(args).length === 1 && typeof args._ !== "undefined" && args._.length > 0) {
const socketPath = await readSocketPath(DEFAULT_SOCKET_PATH)
if (socketPath && (await canConnect(socketPath))) {
if (!(await client.canConnect())) {
return undefined
}
const socketPath = await client.getConnectedSocketPath(paths[0])
if (socketPath) {
logger.debug("Found existing code-server socket")
return socketPath
}

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

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

View File

@@ -12,7 +12,15 @@ import { version as codeServerVersion } from "./constants"
import { Heart } from "./heart"
import { CoderSettings, SettingsProvider } from "./settings"
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.
@@ -308,3 +316,91 @@ export const getCookieOptions = (req: express.Request): express.CookieOptions =>
export const self = (req: express.Request): string => {
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 trustedOrigins = req.args["trusted-origins"] || []
if (trustedOrigins.includes(origin) || trustedOrigins.includes("*")) {
return
}
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.
*/
export 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. Some reverse proxies will set multiple
// comma-separated hosts.
const xHost = getFirstHeader(req, "x-forwarded-host")
if (xHost) {
const firstXHost = xHost.split(",")[0]
if (firstXHost) {
return firstXHost.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

@@ -1,12 +1,10 @@
import { field, logger } from "@coder/logger"
import http from "http"
import * as os from "os"
import path from "path"
import { Disposable } from "../common/emitter"
import { plural } from "../common/util"
import { createApp, ensureAddress } from "./app"
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
import { coderCloudBind } from "./coder_cloud"
import { commit, version } from "./constants"
import { register } from "./routes"
import { humanPath, isDirectory, loadAMDModule, open } from "./util"
@@ -71,9 +69,8 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
forceNewWindow: args["new-window"],
gotoLineMode: true,
}
const paths = args._ || []
for (let i = 0; i < paths.length; i++) {
const fp = path.resolve(paths[i])
for (let i = 0; i < args._.length; i++) {
const fp = args._[i]
if (await isDirectory(fp)) {
pipeArgs.folderURIs.push(fp)
} else {
@@ -127,12 +124,7 @@ export const runCodeServer = async (
const disposeRoutes = await register(app, args)
logger.info(`Using config file ${humanPath(os.homedir(), args.config)}`)
logger.info(
`${protocol.toUpperCase()} server listening on ${serverAddress.toString()} ${
args.link ? "(randomized by --link)" : ""
}`,
)
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
if (args.auth === AuthType.Password) {
logger.info(" - Authentication is enabled")
if (args.usingEnvPassword) {
@@ -143,23 +135,26 @@ export const runCodeServer = async (
logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`)
}
} else {
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`)
logger.info(" - Authentication is disabled")
}
if (args.cert) {
logger.info(` - Using certificate for HTTPS: ${humanPath(os.homedir(), args.cert.value)}`)
} else {
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`)
logger.info(" - Not serving HTTPS")
}
if (args["proxy-domain"].length > 0) {
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`))
}
if (process.env.VSCODE_PROXY_URI) {
logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`)
}
if (args.link) {
await coderCloudBind(serverAddress, args.link.value)
logger.info(" - Connected to cloud agent")
const sessionServerAddress = app.editorSessionManagerServer.address()
if (sessionServerAddress) {
logger.info(`Session server listening on ${sessionServerAddress.toString()}`)
}
if (args.enable && args.enable.length > 0) {

View File

@@ -3,9 +3,19 @@ import { HttpCode } from "../common/http"
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) => {
res.writeHead(HttpCode.ServerError)
res.end(error.message)
// This could be for either a web socket or a regular request. Despite what
// 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.

View File

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

View File

@@ -62,6 +62,16 @@ export const errorHandler: express.ErrorRequestHandler = async (err, req, res, n
}
export const wsErrorHandler: express.ErrorRequestHandler = async (err, req, res, next) => {
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
}
if (statusCode >= 500) {
logger.error(`${err.message} ${err.stack}`)
} else {
logger.debug(`${err.message} ${err.stack}`)
}
;(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"]> => {
const heart = new Heart(path.join(paths.data, "heartbeat"), async () => {
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) => {
clearTimeout(timer)
if (error) {
return reject(error)
}

View File

@@ -7,12 +7,13 @@ import { CookieKeys } from "../../common/http"
import { rootPath } from "../constants"
import { authenticated, getCookieOptions, redirect, replaceTemplates } from "../http"
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
import i18n from "../i18n"
// RateLimiter wraps around the limiter library for logins.
// It allows 2 logins every minute plus 12 logins every hour.
export class RateLimiter {
private readonly minuteLimiter = new Limiter(2, "minute")
private readonly hourLimiter = new Limiter(12, "hour")
private readonly minuteLimiter = new Limiter({ tokensPerInterval: 2, interval: "minute" })
private readonly hourLimiter = new Limiter({ tokensPerInterval: 12, interval: "hour" })
public canTry(): boolean {
// 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 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 welcomeText = req.args["welcome-text"] || `Welcome to ${appName}`
let passwordMsg = `Check the config file at ${humanPath(os.homedir(), req.args.config)} for the password.`
const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string)
let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: humanPath(os.homedir(), req.args.config) })
if (req.args.usingEnvPassword) {
passwordMsg = "Password was set from $PASSWORD."
passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD")
} else if (req.args.usingEnvHashedPassword) {
passwordMsg = "Password was set from $HASHED_PASSWORD."
passwordMsg = i18n.t("LOGIN_USING_HASHED_PASSWORD")
}
return replaceTemplates(
req,
content
.replace(/{{APP_NAME}}/g, appName)
.replace(/{{I18N_LOGIN_TITLE}}/g, i18n.t("LOGIN_TITLE", { app: appName }))
.replace(/{{WELCOME_TEXT}}/g, welcomeText)
.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>` : ""),
)
}
@@ -70,11 +76,11 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
try {
// Check to see if they exceeded their login attempts
if (!limiter.canTry()) {
throw new Error("Login rate limited!")
throw new Error(i18n.t("LOGIN_RATE_LIMIT") as string)
}
if (!password) {
throw new Error("Missing password")
throw new Error(i18n.t("MISS_PASSWORD") as string)
}
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) {
const renderedHtml = await getRoot(req, error)
res.send(renderedHtml)

View File

@@ -3,7 +3,7 @@ import * as path from "path"
import * as qs from "qs"
import * as pluginapi from "../../../typings/pluginapi"
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"
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
@@ -11,7 +11,7 @@ const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
return `http://0.0.0.0:${req.params.port}/${req.originalUrl}`
}
const query = qs.stringify(req.query)
return `http://0.0.0.0:${req.params.port}/${req.params[0] || ""}${query ? `?${query}` : ""}`
return encodeURI(`http://0.0.0.0:${req.params.port}${req.params[0] || ""}${query ? `?${query}` : ""}`)
}
export async function proxy(
@@ -50,6 +50,7 @@ export async function wsProxy(
passthroughPath?: boolean
},
): Promise<void> {
ensureOrigin(req)
await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, {
ignorePath: true,

View File

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

View File

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

View File

@@ -541,3 +541,13 @@ export const loadAMDModule = async <T>(amdPath: string, exportName: string): Pro
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]]
}

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

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

View File

@@ -3,7 +3,7 @@ import * as cp from "child_process"
import * as path from "path"
import * as rfs from "rotating-file-stream"
import { Emitter } from "../common/emitter"
import { DefaultedArgs } from "./cli"
import { DefaultedArgs, redactArgs } from "./cli"
import { paths } from "./util"
const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
@@ -44,10 +44,11 @@ export function onMessage<M, T extends M>(
}
const onMessage = (message: M) => {
;(customLogger || logger).trace("got message", field("message", message))
if (fn(message)) {
cleanup()
resolve(message)
} else {
;(customLogger || logger).debug("got unhandled message", field("message", message))
}
}
@@ -147,7 +148,7 @@ abstract class Process {
* Child process that will clean up after itself if the parent goes away and can
* perform a handshake with the parent and ask it to relaunch.
*/
class ChildProcess extends Process {
export class ChildProcess extends Process {
public logger = logger.named(`child:${process.pid}`)
public constructor(private readonly parentPid: number) {
@@ -172,6 +173,7 @@ class ChildProcess extends Process {
* Initiate the handshake and wait for a response from the parent.
*/
public async handshake(): Promise<DefaultedArgs> {
this.logger.debug("initiating handshake")
this.send({ type: "handshake" })
const message = await onMessage<ParentMessage, ParentHandshakeMessage>(
process,
@@ -180,6 +182,13 @@ class ChildProcess extends Process {
},
this.logger,
)
this.logger.debug(
"got message",
field("message", {
type: message.type,
args: redactArgs(message.args),
}),
)
return message.args
}
@@ -280,6 +289,10 @@ export class ParentProcess extends Process {
}
public start(args: DefaultedArgs): Promise<void> {
// Our logger was created before we parsed CLI arguments so update the level
// in case it has changed.
this.logger.level = logger.level
// Store for relaunches.
this.args = args
if (!this.started) {
@@ -306,7 +319,7 @@ export class ParentProcess extends Process {
})
}
this.logger.debug(`spawned inner process ${child.pid}`)
this.logger.debug(`spawned child process ${child.pid}`)
await this.handshake(child)
@@ -334,13 +347,14 @@ export class ParentProcess extends Process {
if (!this.args) {
throw new Error("started without args")
}
await onMessage<ChildMessage, ChildHandshakeMessage>(
const message = await onMessage<ChildMessage, ChildHandshakeMessage>(
child,
(message): message is ChildHandshakeMessage => {
return message.type === "handshake"
},
this.logger,
)
this.logger.debug("got message", field("message", message))
this.send(child, { type: "handshake", args: this.args })
}

View File

@@ -32,6 +32,9 @@ export class WebsocketRouter {
/**
* Handle a websocket at this route. Note that websockets are immediately
* 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 {
this.router.get(

View File

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

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

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

View File

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

@@ -12,7 +12,7 @@ describe("--install-extension", () => {
setupFlags = ["--extensions-dir", tempDir]
})
it("should use EXTENSIONS_GALLERY when set", async () => {
const extName = `author.extension-1.0.0`
const extName = "author.extension"
const { stderr } = await runCodeServerCommand([...setupFlags, "--install-extension", extName], {
EXTENSIONS_GALLERY: "{}",
})

View File

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

View File

@@ -1,5 +1,6 @@
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).
@@ -56,3 +57,22 @@ describe("getAvailablePort", () => {
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

@@ -17,7 +17,7 @@ describe("createApp", () => {
beforeAll(async () => {
mockLogger()
const testName = "unlink-socket"
const testName = "app"
await clean(testName)
tmpDirPath = await tmpdir(testName)
tmpFilePath = path.join(tmpDirPath, "unlink-socket-file")
@@ -103,7 +103,7 @@ describe("createApp", () => {
const app = await createApp(defaultArgs)
expect(unlinkSpy).toHaveBeenCalledTimes(1)
expect(unlinkSpy).toHaveBeenCalledWith(tmpFilePath)
app.dispose()
})

View File

@@ -1,17 +1,13 @@
import { Level, logger } from "@coder/logger"
import { promises as fs } from "fs"
import * as net from "net"
import * as os from "os"
import * as path from "path"
import {
UserProvidedArgs,
bindAddrFromArgs,
defaultConfigFile,
parse,
readSocketPath,
setDefaults,
shouldOpenInExistingInstance,
splitOnFirstEquals,
toCodeArgs,
optionDescriptions,
options,
@@ -21,7 +17,12 @@ import {
} from "../../../src/node/cli"
import { shouldSpawnCliProcess } from "../../../src/node/main"
import { generatePassword, paths } from "../../../src/node/util"
import { clean, useEnv, tmpdir } from "../../utils/helpers"
import {
EditorSessionManager,
EditorSessionManagerClient,
makeEditorSessionManagerServer,
} from "../../../src/node/vscodeSocket"
import { clean, useEnv, tmpdir, listenOn } from "../../utils/helpers"
// The parser should not set any defaults so the caller can determine what
// values the user actually set. These are only set after explicitly calling
@@ -35,6 +36,7 @@ const defaults = {
usingEnvHashedPassword: false,
"extensions-dir": path.join(paths.data, "extensions"),
"user-data-dir": paths.data,
"session-socket": path.join(paths.data, "code-server-ipc.sock"),
_: [],
}
@@ -44,6 +46,7 @@ describe("parser", () => {
delete process.env.PASSWORD
delete process.env.CS_DISABLE_FILE_DOWNLOADS
delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
delete process.env.VSCODE_PROXY_URI
console.log = jest.fn()
})
@@ -100,6 +103,8 @@ describe("parser", () => {
"--disable-getting-started-override",
["--session-socket", "/tmp/override-code-server-ipc-socket"],
["--host", "0.0.0.0"],
"4",
"--",
@@ -133,6 +138,7 @@ describe("parser", () => {
"welcome-text": "welcome to code",
version: true,
"bind-addr": "192.169.0.1:8080",
"session-socket": "/tmp/override-code-server-ipc-socket",
})
})
@@ -297,26 +303,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 () => {
process.env.PASSWORD = "test"
const args = parse([])
@@ -433,7 +419,7 @@ describe("parser", () => {
const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
"proxy-domain": ["coder.com", "coder.org"],
"proxy-domain": ["{{port}}.coder.com", "{{port}}.coder.org"],
})
})
it("should allow '=,$/' in strings", async () => {
@@ -478,11 +464,36 @@ describe("parser", () => {
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", () => {
const testName = "cli"
const vscodeIpcPath = path.join(os.tmpdir(), "vscode-ipc")
let tmpDirPath: string
beforeAll(async () => {
await clean(testName)
@@ -490,93 +501,170 @@ describe("cli", () => {
beforeEach(async () => {
delete process.env.VSCODE_IPC_HOOK_CLI
await fs.rm(vscodeIpcPath, { force: true, recursive: true })
tmpDirPath = await tmpdir(testName)
})
it("should use existing if inside code-server", async () => {
process.env.VSCODE_IPC_HOOK_CLI = "test"
const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")
args.port = 8081
args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")
})
it("should use existing if --reuse-window is set", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {}
args["reuse-window"] = true
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(undefined)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).rejects.toThrow()
await fs.writeFile(vscodeIpcPath, "test")
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual("test")
const socketPath = path.join(tmpDirPath, "socket")
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath,
},
})
const vscodeSockets = listenOn(socketPath)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)
args.port = 8081
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual("test")
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)
server.close()
vscodeSockets.close()
})
it("should use existing if --new-window is set", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {}
args["new-window"] = true
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).rejects.toThrow()
await fs.writeFile(vscodeIpcPath, "test")
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
const socketPath = path.join(tmpDirPath, "socket")
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath,
},
})
const vscodeSockets = listenOn(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
server.close()
vscodeSockets.close()
})
it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
const testDir = await tmpdir(testName)
const socketPath = path.join(testDir, "socket")
await fs.writeFile(vscodeIpcPath, socketPath)
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
await new Promise((resolve) => {
const server = net.createServer(() => {
// Close after getting the first connection.
server.close()
})
server.once("listening", () => resolve(server))
server.listen(socketPath)
const client = new EditorSessionManagerClient(sessionSocket)
const socketPath = path.join(tmpDirPath, "socket")
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath,
},
})
const vscodeSockets = listenOn(socketPath)
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)
args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
})
})
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)
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))
server.close()
vscodeSockets.close()
})
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))
it("should prefer matching sessions for only the first path", async () => {
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
id: "aaa",
folders: [
{
uri: {
path: "/aaa",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
},
})
await client.addSession({
entry: {
workspace: {
id: "bbb",
folders: [
{
uri: {
path: "/bbb",
},
},
],
},
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
},
})
listenOn(`${tmpDirPath}/vscode-ipc-aaa.sock`, `${tmpDirPath}/vscode-ipc-bbb.sock`)
const args: UserProvidedArgs = {}
args._ = ["/aaa/file", "/bbb/file"]
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(`${tmpDirPath}/vscode-ipc-aaa.sock`)
server.close()
})
})
@@ -749,44 +837,6 @@ cert: false`)
})
})
describe("readSocketPath", () => {
const fileContents = "readSocketPath file contents"
let tmpDirPath: string
let tmpFilePath: string
const testName = "readSocketPath"
beforeAll(async () => {
await clean(testName)
})
beforeEach(async () => {
tmpDirPath = await tmpdir(testName)
tmpFilePath = path.join(tmpDirPath, "readSocketPath.txt")
await fs.writeFile(tmpFilePath, fileContents)
})
it("should throw an error if it can't read the file", async () => {
// TODO@jsjoeio - implement
// Test it on a directory.... ESDIR
// TODO@jsjoeio - implement
expect(() => readSocketPath(tmpDirPath)).rejects.toThrow("EISDIR")
})
it("should return undefined if it can't read the file", async () => {
// TODO@jsjoeio - implement
const socketPath = await readSocketPath(path.join(tmpDirPath, "not-a-file"))
expect(socketPath).toBeUndefined()
})
it("should return the file contents", async () => {
const contents = await readSocketPath(tmpFilePath)
expect(contents).toBe(fileContents)
})
it("should return the same file contents for two different calls", async () => {
const contents1 = await readSocketPath(tmpFilePath)
const contents2 = await readSocketPath(tmpFilePath)
expect(contents2).toBe(contents1)
})
})
describe("toCodeArgs", () => {
const vscodeDefaults = {
...defaults,
@@ -881,21 +931,13 @@ describe("optionDescriptions", () => {
it("should show if an option is deprecated", () => {
const opts: Partial<Options<Required<UserProvidedArgs>>> = {
link: {
cert: {
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.
`,
description: "foo",
deprecated: true,
},
}
expect(optionDescriptions(opts)).toStrictEqual([
` --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.`,
])
expect(optionDescriptions(opts)).toStrictEqual([" --cert (deprecated) foo"])
})
it("should show newlines in description", () => {

View File

@@ -1,55 +1,127 @@
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", () => {
it("should construct a relative path to the root", () => {
expect(relativeRoot("/")).toStrictEqual(".")
expect(relativeRoot("/foo")).toStrictEqual(".")
expect(relativeRoot("/foo/")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar ")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar/")).toStrictEqual("./../..")
beforeEach(() => {
mockLogger()
})
})
describe("constructRedirectPath", () => {
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 = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
afterEach(() => {
jest.clearAllMocks()
})
it("should use an empty string if no query params", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = ""
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./"
expect(actual).toBe(expected)
it("should construct a relative path to the root", () => {
expect(http.relativeRoot("/")).toStrictEqual(".")
expect(http.relativeRoot("/foo")).toStrictEqual(".")
expect(http.relativeRoot("/foo/")).toStrictEqual("./..")
expect(http.relativeRoot("/foo/bar ")).toStrictEqual("./..")
expect(http.relativeRoot("/foo/bar/")).toStrictEqual("./../..")
})
it("should append the 'to' path relative to the originalUrl", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
describe("origin", () => {
;[
{
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],
["x-forwarded-host", `${test.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,
},
args: {},
})
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({
originalUrl: "localhost:8080",
describe("constructRedirectPath", () => {
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

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

View File

@@ -4,21 +4,26 @@ import * as http from "http"
import nodeFetch from "node-fetch"
import { HttpCode } from "../../../src/common/http"
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 integration from "../../utils/integration"
describe("proxy", () => {
const nhooyrDevServer = new httpserver.HttpServer()
const wsApp = express.default()
const wsRouter = WsRouter()
let codeServer: httpserver.HttpServer | undefined
let proxyPath: string
let absProxyPath: string
let e: express.Express
beforeAll(async () => {
wsApp.use("/", wsRouter.router)
await nhooyrDevServer.listen((req, res) => {
e(req, res)
})
nhooyrDevServer.listenUpgrade(wsApp)
proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup`
absProxyPath = proxyPath.replace("/proxy/", "/absproxy/")
})
@@ -29,6 +34,7 @@ describe("proxy", () => {
beforeEach(() => {
e = express.default()
mockLogger()
})
afterEach(async () => {
@@ -36,6 +42,7 @@ describe("proxy", () => {
await codeServer.dispose()
codeServer = undefined
}
jest.clearAllMocks()
})
it("should rewrite the base path", async () => {
@@ -151,6 +158,46 @@ describe("proxy", () => {
expect(resp.status).toBe(500)
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()
})
it("should proxy non-ASCII", async () => {
e.get("*", (req, res) => {
res.json("ほげ")
})
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch(proxyPath.replace("wsup", "ほげ"))
expect(resp.status).toBe(200)
const json = await resp.json()
expect(json).toBe("ほげ")
})
})
// NOTE@jsjoeio
@@ -190,18 +237,18 @@ describe("proxy (standalone)", () => {
})
// Start both servers
await proxyTarget.listen(PROXY_PORT)
await testServer.listen(PORT)
proxyTarget.listen(PROXY_PORT)
testServer.listen(PORT)
})
afterEach(async () => {
await testServer.close()
await proxyTarget.close()
testServer.close()
proxyTarget.close()
})
it("should return a 500 when proxy target errors ", async () => {
// Close the proxy target so that proxy errors
await proxyTarget.close()
proxyTarget.close()
const errorResp = await nodeFetch(`${URL}/error`)
expect(errorResp.status).toBe(HttpCode.ServerError)
expect(errorResp.statusText).toBe("Internal Server Error")

View File

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

View File

@@ -138,5 +138,16 @@ describe("login", () => {
expect(resp.status).toBe(200)
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 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", () => {
let codeServer: httpserver.HttpServer | undefined
const testName = "vscode"
beforeAll(async () => {
await clean(testName)
beforeEach(() => {
mockLogger()
})
afterEach(async () => {
@@ -19,119 +13,18 @@ describe("vscode", () => {
await codeServer.dispose()
codeServer = undefined
}
jest.clearAllMocks()
})
const routes = ["/", "/vscode", "/vscode/"]
it("should load all route variations", async () => {
codeServer = await integration.setup(["--auth=none"], "")
for (const route of routes) {
const resp = await codeServer.fetch(route)
expect(resp.status).toBe(200)
const html = await resp.text()
const url = new URL(resp.url) // Check there were no redirections.
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()
it("should fail origin check", async () => {
await expect(async () => {
codeServer = await integration.setup(["--auth=none"], "")
await codeServer.wsWait("/vscode", {
headers: {
host: "localhost:8080",
origin: "https://evil.org",
},
})
}).rejects.toThrow()
})
})

View File

@@ -601,3 +601,41 @@ describe("constructOpenOptions", () => {
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

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

Some files were not shown because too many files have changed in this diff Show More