Compare commits

...

371 Commits

Author SHA1 Message Date
Asher
fd36a99a4c Update vscode patch notes and bump version 2020-04-29 15:22:11 -05:00
Asher
4b09746c37 Merge pull request #1574 from cdr/transformer
Remove transformer file
2020-04-29 12:45:07 -05:00
Asher
870cf4f3fe Fix yarn.lock
Things got really out of whack when trying to update dependencies
earlier.
2020-04-29 12:39:42 -05:00
Asher
1ff35f177d Remove transformer file
Also remove some unused imports that were causing build errors (they
were left over from the fix that allowed installing any extension kind).
2020-04-29 12:13:44 -05:00
Asher
f3edb1cc5f Update node to latest lts (12.16.3) and update deps 2020-04-29 11:43:13 -05:00
Asher
86dc38e69f Allow extensions of any kind
This enables vscode-icons among others.
2020-04-28 17:57:56 -05:00
Asher
a2b69c8f3f Fix inconsistencies in log flags and env var
- Fix priority to match the commented behavior.
- Ignore bogus LOG_LEVEL values.
2020-04-28 17:57:55 -05:00
Asher
4cfd7c50ad Remove unused class
I managed to lose this deletion in a merge.
2020-04-28 17:57:54 -05:00
Anmol Sethi
a96606e589 Fix mention of host/port in docs 2020-04-28 18:29:25 -04:00
Anmol Sethi
30aefe19b5 Update issue template to mention check against regular VS Code 2020-04-28 14:50:08 -04:00
Anmol Sethi
37184f456c Merge pull request #1562 from cdr/bindaddr
Deprecate --host and --port in favour of --bind-addr
2020-04-28 14:33:38 -04:00
Anmol Sethi
05456024c4 Merge pull request #1561 from cdr/ratelimit
Add basic rate limiting to login endpoint
2020-04-28 14:33:18 -04:00
Anmol Sethi
5accf3fe5f Add basic rate limiting to login endpoint
Closes #1320
2020-04-28 14:21:08 -04:00
Anmol Sethi
2dd27b4cb8 gitignore release-upload 2020-04-28 14:19:25 -04:00
Anmol Sethi
af28885ea6 Deprecate --host and --port in favour of --bind-addr 2020-04-28 14:19:24 -04:00
Anmol Sethi
f21ba53609 Merge pull request #1563 from cdr/remove-ssh
Remove SSH server
2020-04-28 14:15:54 -04:00
Anmol Sethi
181e0ea6c8 Remove ssh2 dep 2020-04-28 14:04:56 -04:00
Asher
6074ca275b Fill out some missing browser environment values
Pass the user data dir to the browser environment service then derive
all the paths we can based off that path like the global storage path
which the vim extension uses to store history (otherwise it gets stored
in the working directory from when code-server was spawned).

Arguably the better solution is to use the userdata scheme but that
won't work because the vim extension ignores the VS Code API.

Fixes #1551.
2020-04-27 17:15:37 -05:00
Anmol Sethi
d0d5461a67 Remove SSH server
Closes #1502
2020-04-27 09:27:45 -04:00
Anmol Sethi
8608ae2f08 Merge pull request #1546 from cdr/readlink-mac
Fix code-server.sh script on macOS
2020-04-22 18:01:25 -04:00
Anmol Sethi
401f08db63 Fix code-server.sh script on macOS 2020-04-22 17:49:02 -04:00
Asher
caa299b60d Update VS Code to 1.44.2 2020-04-21 14:25:27 -05:00
Asher
dcde596002 Document debugging process
Closes #1465.
2020-04-20 18:55:14 -05:00
Asher
ee14db20f1 Allow data: in CSP for font-src
Closes #1530.
2020-04-20 18:10:07 -05:00
Asher
27ba64c7e4 Improve request error handling
See #1532 for more context.

- Errored JSON requests will get back the error in JSON instead of using
  the status text. This seems better to me because it seems more correct
  to utilize the response body over hijacking the status text. The
  caller is expecting JSON anyway. Worst of all I never actually set the
  status text like I thought I did so it wasn't working to begin with.
- Allow the update error to propagate for JSON update requests. It was
  caught to show the error inline instead of an error page when using
  the update page but for JSON requests it meant there was no error and
  no error code so it looked like it succeeded.
- Make errors for failed requests to GitHub less incomprehensible.
  Previously they would just be the code which is no context at all.
2020-04-17 15:16:10 -05:00
Anmol Sethi
c7753f2cf9 Update docker one liner to forward UID/GID
Closes #1425
2020-04-16 14:57:41 -04:00
Asher
974d4cb8fc Allow specifying a workspace on the command line
Fixes #1535.
2020-04-16 11:56:46 -05:00
Charles Moog
29b6115c77 Adds dev container and docs (#1499) 2020-04-14 17:22:52 -05:00
Asher
28e91ba70c Fix domain issues when setting the cookie
Fixes #1507.
2020-04-13 16:14:40 -05:00
Asher
5aded14b87 Merge pull request #1453 from cdr/proxy
HTTP proxy
2020-04-08 12:44:29 -05:00
Asher
a288351ad4 Respond when proxy errors
Otherwise the request will just hang.
2020-04-08 11:54:18 -05:00
Asher
3b39482420 Document workspace and folder behavior
Also fixed a type issue.
2020-04-07 17:49:50 -05:00
Asher
a5c35af81b Fix encoding issues with folder and workspace params
The raw value is now passed back to VS Code so it can do the parsing
with its own URI class rather than trying to parse using Node's url
module first since that has no guarantee of working the same way. It
also lets us keep the vscode-remote bit internal to VS Code.

Removed the logic that keeps trying paths until it finds a valid one
because it seems confusing to open a path and silently get some other
path instead of an error for the one you tried to open. Now it'll just
use exactly what you specified or fail trying.

Fixes #1488. The problem here was that url.parse was encoding the spaces
then the validation failed looking for a literal %20.
2020-04-07 15:18:19 -05:00
Charles Moog
b78bdaf46e Merge pull request #1496 from cdr/report-issue-url
Send report issues to code-server repo
2020-04-06 17:29:53 -05:00
cmoog
aefef5b0e8 Send report issues to code-server repo 2020-04-06 22:23:14 +00:00
Abin Simon
ca998240a0 Fix typo in FAQ (#1489) 2020-04-03 13:09:32 -05:00
Asher
d2a31477c7 Merge pull request #1486 from cdr/update-backup
Back up old directory when updating
2020-04-02 17:28:27 -05:00
Asher
9c6581273e Show proper error when an update fails 2020-04-02 17:20:25 -05:00
Asher
d1445a8135 Back up code-server directory when updating 2020-04-02 16:21:48 -05:00
Asher
5fc00acc39 Fix incorrect reporting that an update failed 2020-04-02 14:48:15 -05:00
Asher
363cdd02df Improve proxy documentation 2020-04-02 13:40:30 -05:00
Asher
a5d1d3b90e Move proxy logic into main HTTP server
This makes the code much more internally consistent (providers just
return payloads, include the proxy provider).
2020-04-02 13:40:29 -05:00
Asher
aaa6c279a1 Use Set for proxy domains 2020-04-02 13:40:28 -05:00
Asher
498becd11f Use route.fullPath when adding trailing slash
There's no need to specially construct the path.
2020-04-02 13:40:27 -05:00
Asher
411c61fb02 Create helper for determining if route is the root 2020-04-02 13:40:26 -05:00
Asher
74a0bacdcf Rename hxxp to isHttp 2020-04-02 13:40:25 -05:00
Asher
e7e7b0ffb7 Fix redirects through subpath proxy 2020-04-02 13:40:25 -05:00
Asher
fd339a7433 Include query parameters when proxying 2020-04-02 13:40:24 -05:00
Asher
561b6343c8 Ensure a trailing slash on subpath proxy 2020-04-02 13:40:23 -05:00
Asher
e68d72c4d6 Add documentation for proxying 2020-04-02 13:40:22 -05:00
Asher
737a8f5965 Catch proxy errors
Otherwise they'll crash code-server.
2020-04-02 13:40:21 -05:00
Asher
c0dd29c591 Fix domains with ports & localhost subdomains 2020-04-02 13:40:20 -05:00
Asher
8aa5675ba2 Implement the actual proxy 2020-04-02 13:40:19 -05:00
Asher
2086648c87 Only handle exact domain matches
This simplifies the logic a bit.
2020-04-02 13:40:18 -05:00
Asher
3a98d856a5 Handle authentication with proxy
The cookie will be set for the proxy domain so it'll work for all of its
subdomains.
2020-04-02 13:40:17 -05:00
Asher
90fd1f7dd1 Add proxy provider
It'll be able to handle /proxy requests as well as subdomains.
2020-04-02 13:40:16 -05:00
Asher
77ad73d579 Set domain on cookie
This allows it to be used in subdomains.
2020-04-02 13:40:15 -05:00
Asher
13534fa0c0 Add proxy-domain flag
This will be used for proxying ports.
2020-04-02 13:40:14 -05:00
Asher
37299abcc9 Minor startup code improvements
- Add type to HTTP options.
- Fix certificate message always saying it was generated.
- Dedent output not directly related to the HTTP server.
- Remove unnecessary comma.
2020-04-02 13:40:13 -05:00
Asher
e480f6527e Update VS Code to 1.43.2 2020-04-01 15:27:28 -05:00
Asher
26584f2060 Strip protocol from remote authority
In Google cloud shell the host header is 127.0.0.1:8080 instead of the
actual URL. This is what we write out to the HTML so VS Code can pick it
up. However cloud shell rewrites this string when found in the HTML
before serving it so it becomes https://8080-[...].appspot.com,
resulting in an extra unexpected https:// in the
URI (vscode-remote://https://8080[...]). The resulting malformed URI
causes the extension host to exit.

- Fixes #1471
- Fixes #1468
- Fixes #1440 (most likely).
2020-04-01 13:41:05 -05:00
Asher
a4c0fd1fdc Run ssh server listen after http
That way if they happen to conflict code-server doesn't crash.
2020-03-30 17:43:11 -05:00
Asher
6c104c016e Prevent exiting when an exception is uncaught 2020-03-30 17:43:10 -05:00
Asher
599670136d Output commit along with the version 2020-03-30 17:43:09 -05:00
Asher
ce637d318d Add descriptions to SSH flags 2020-03-30 17:43:08 -05:00
Anmol Sethi
d8654b5a19 Merge pull request #1460 from mjgallag/peg-yarn-version
Peg yarn version to ensure deterministic builds
2020-03-30 01:52:14 -04:00
Michael Gallagher
12c3ccd6c7 Peg yarn version to ensure deterministic builds
"Yarn is fully deterministic as long as all your teammates are using the same Yarn version." (https://classic.yarnpkg.com/blog/2017/05/31/determinism/)
2020-03-28 14:29:04 -07:00
Asher
7954656610 Set background color using VS Code theme 2020-03-27 16:58:50 -05:00
Asher
87ebf03eb7 Skip vscode dependencies for test phase
They aren't used so we can skip them.
2020-03-27 13:40:42 -05:00
Asher
df1c34e291 Overwrite GitHub releases again
I was under the impression this was causing existing releases to become
drafts again but that happens without this flag.
2020-03-27 12:03:01 -05:00
Asher
4a65b58772 Fix arm builds 2020-03-27 12:02:56 -05:00
Asher
11fdb8854b Skip unused dependencies 2020-03-26 15:12:17 -05:00
Asher
0a92bb1607 Fix node version mismatch 2020-03-26 13:54:41 -05:00
Asher
5bac2cbdb8 Add build test 2020-03-26 13:54:40 -05:00
Asher
511c3e95b2 Remove npm rebuild 2020-03-25 17:07:26 -05:00
Asher
0a5687bacf Fix crash when unable to request an update 2020-03-25 15:00:35 -05:00
Asher
27320465b7 Merge pull request #1443 from maksimr/fix-duplication
Remove duplication in dependencies
2020-03-25 14:13:34 -05:00
Asher
6df454e006 Merge pull request #1445 from maksimr/fix-doc
Fix documentation for build process
2020-03-25 14:13:15 -05:00
Asher
216652fb31 Merge pull request #1446 from maksimr/fix-apple-touch-icon
Returns back apple touch icon
2020-03-25 14:12:44 -05:00
Asher
0f066d30b4 Add data-cfasync="false" to script tags
This prevents Cloudflare's Rocket Loader from acting on them.

Fixes #1451.
2020-03-25 14:04:36 -05:00
Asher
d1687c1533 Catch error when SSH server fails to start 2020-03-24 17:38:46 -05:00
Asher
f5f29c0120 Set GitHub token when installing VS Code deps
Should help with the ripgrep ratelimit issues.
2020-03-24 16:05:07 -05:00
Maksim Ryzhikov
8a6faa39c9 Remove duplication in dependencies 2020-03-24 09:43:14 +03:00
Maksim Ryzhikov
5887c1d339 Returns back apple touch icon 2020-03-23 14:44:31 +03:00
Maksim Ryzhikov
664ef17af8 Fix documentation for build process 2020-03-23 12:26:54 +03:00
Anmol Sethi
004004c047 Merge pull request #1432 from onilton/patch-1
Add link to contributing
2020-03-19 20:47:10 -04:00
Onilton Maciel
09db0ffad5 Add link to contributing 2020-03-19 18:53:31 -03:00
Asher
a349ea8ff9 Merge pull request #1421 from cdr/dependabot/npm_and_yarn/acorn-5.7.4
Bump acorn from 5.7.3 to 5.7.4
2020-03-19 13:17:37 -05:00
Asher
cfebf2c67f Removed unused CSS 2020-03-17 10:36:19 -05:00
dependabot[bot]
ddd44999c6 Bump acorn from 5.7.3 to 5.7.4
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-16 22:34:51 +00:00
Asher
89d78a5921 Encode query params from open dialog
Fixes #1424.
2020-03-16 15:19:06 -05:00
Asher
99dd2db97c Remove open in desktop button 2020-03-16 15:14:55 -05:00
Asher
b52fbb4cb9 Catch error when openssl isn't installed 2020-03-16 15:14:54 -05:00
Will O'Beirne
3463d56114 SSH server & endpoint 2020-03-16 15:14:53 -05:00
Asher
5f63d2b822 Fix custom socket path 2020-03-16 15:14:52 -05:00
Asher
db4a4f0f50 Don't ignore scripts for code-server prod yarn 2020-03-16 15:14:51 -05:00
Asher
d192726e80 Simplify dashboard 2020-03-16 15:14:50 -05:00
Asher
d832f61d5b Make client-side extensions work at any base 2020-03-16 12:04:09 -05:00
Asher
88f4b986c5 Remove our env vars from the shell
This enables developing code-server in code-server.
2020-03-16 11:01:46 -05:00
Asher
aeb6261189 Update VS Code to 1.43.0 2020-03-13 17:42:10 -05:00
Asher
6cb228037b Add base path to update endpoint from VS Code
This will make it work regardless of what the current URL happens to be.

Also move the telemetry setting into the options since we might as well
make use of it seeing as how we have to parse it for the base path
anyway.
2020-03-13 16:44:56 -05:00
Asher
a00fa85d77 Qualify extensions in help output as VS Code extensions
Also add a description for uninstall-extension and force.
2020-03-13 13:23:30 -05:00
Asher
57de78e12a Add show-versions flag and add help for list-extensions
Closes #1417.
2020-03-13 13:17:59 -05:00
Asher
2342443368 Set telemetry setting based on disable-telemetry flag
By design the disable-telemetry flag does not affect extension
telemetry, only the setting does, so disabling the setting when the flag
is set should cause extensions to also stop sending telemetry.

Fixes #1116.
2020-03-13 12:36:57 -05:00
Asher
26647c54c9 Restore old folder query parameter behavior
Fixes #1351.
2020-03-11 16:06:32 -05:00
Asher
253cf1c438 Remove unnecessary return types 2020-03-11 16:06:32 -05:00
Anmol Sethi
f6a5eaa965 Merge pull request #1406 from allanbowe/patch-1
chore: update README link (as no longer redirects)
2020-03-09 22:04:23 -04:00
Allan Bowe
f83a57a010 chore: update README link (as no longer redirects) 2020-03-09 20:40:41 +01:00
Anmol Sethi
92cec80b0e Merge pull request #1400 from zuchka/patch-1
verb agreement typo in FAQ
2020-03-07 22:10:21 -05:00
Anmol Sethi
1f601f27a2 Merge pull request #1399 from SuperSandro2000/docker
Optimize release Docker Image
2020-03-07 21:04:30 -05:00
Matt Abrams
71f1291623 verb agreement typo in FAQ
dear owners,

this is a simple copy-editing pull request. The issue regards verb-agreement. Thank you for your time,
best,

Dr. Matthew Jeffrey Abrams
2020-03-06 20:29:09 -05:00
Sandro Jäckel
9b07078b47 Combine two RUNs 2020-03-07 00:06:42 +01:00
Sandro Jäckel
8433a3d081 Combine all apt-get commands to really delete the cache from all layers 2020-03-07 00:00:58 +01:00
Asher
c8269fb54d Add exec to startup script
Removes an extra process. See #1388.
2020-03-06 10:31:16 -06:00
Asher
0b9a478289 Add connection type to websocket query parameters
This allows external services to distinguish between them.
2020-03-05 15:49:37 -06:00
Asher
c7e6e58387 Output newlines in CI immediately
Perhaps this is causing the output buffering issue with the arm builds.
2020-03-05 11:39:08 -06:00
Asher
8c515029fd Don't overwrite releases
This makes them drafts again which is not what I wanted.
2020-03-05 11:18:05 -06:00
Asher
1f6ff2f763 Show --install-extension in help menu
Also add --force so extensions can be updated without prompts.

Closes #1392.
2020-03-05 10:42:49 -06:00
Asher
04542c99fd Resolve passed-in directory
Makes relative paths work correctly.
2020-03-05 10:38:13 -06:00
Asher
8c47ba255a Preserve current working directory
Fixes #1388.
2020-03-05 10:26:14 -06:00
Asher
4e6f6bc2cc Fix .zip uploading to gcs as .tar.gz 2020-03-04 16:52:28 -06:00
Asher
7c65a54fcf Overwrite releases 2020-03-04 16:41:05 -06:00
Asher
1f43a673df Always build extensions on CI 2020-03-04 16:07:26 -06:00
Asher
744327ffd4 Update release dockerfile to use a symlink 2020-03-04 16:02:25 -06:00
Asher
a442d3e3f9 Make symlinking the entry script work 2020-03-04 15:22:32 -06:00
Asher
3e8a6f93a4 Build arm64 without travis_wait 2020-03-04 14:18:05 -06:00
Asher
308a84e6ec Fix centos image for arm64 2020-03-04 13:12:03 -06:00
Asher
cc139acfd1 Cache built-in extensions 2020-03-03 17:04:42 -06:00
Asher
32f8f481b6 Use Centos 7 for building
This will bring the libc requirements back down.
2020-03-03 16:19:51 -06:00
Asher
ec55ed39ee Fix json key decoding on Mac
It seems the short flag in MacOS is -D, not -d. The long flag is the
same. There are no other options with -d so I'm not sure why.
2020-03-03 16:01:22 -06:00
Asher
ee4b939efa Fix zip step for Darwin 2020-03-03 15:07:58 -06:00
Asher
538e8d8085 Store gcs key in Travis settings
Instead of encrypted in the repository.
2020-03-03 15:06:36 -06:00
Asher
2c4ca14d53 Merge pull request #1338 from cdr/restructure 2020-03-03 13:25:08 -06:00
Asher
8d934be6dc Elaborate what won't work over an insecure domain
Closes #997.
2020-03-03 13:10:32 -06:00
Kyle Carberry
c54450941c Update favicon 2020-03-03 02:25:53 +00:00
Asher
e0e019fbd5 Fix mime type for compressed client-side extensions
In Firefox using the gzip mime type will (probably correctly) cause it
not to decompress while in Chromium it still will (incorrectly).
2020-03-02 18:24:35 -06:00
Asher
77af2a5b0e Fix worker require paths when behind proxy 2020-03-02 18:04:27 -06:00
Asher
ecac0dd751 Handle unexpected string errors
Looks like sometimes VS Code throws strings. For example if ifconfig is
missing.
2020-03-02 17:22:23 -06:00
Asher
79b4c64a03 Add example gif 2020-03-02 15:13:39 -06:00
Asher
ccd01c49b9 Integrate update notifications into VS Code 2020-03-02 15:01:24 -06:00
Asher
069c5230cd Move VS Code to the root 2020-03-02 12:55:34 -06:00
Asher
c146457de4 Add recents section 2020-03-02 11:52:39 -06:00
Asher
88cab27165 Compress when sending client-side extension tars 2020-02-28 14:25:28 -06:00
Asher
a8914b025f Output code-server version on startup 2020-02-28 13:30:21 -06:00
Asher
0f87798ed6 Don't write bad password back out to input
Closes #1379.
2020-02-28 10:49:43 -06:00
Asher
963ebaca5b Register a service worker
To make installing as a PWA possible. Fixes #1181.
2020-02-27 16:37:00 -06:00
Kyle Carberry
eef2ed0e78 Update PWA icons 2020-02-27 20:43:31 +00:00
Asher
0e3720169f Add spacing between items
Also fix padding not being respected when blocks exceed container
height.
2020-02-27 12:58:40 -06:00
Asher
21cfeb9da0 Add the ability to kill running VS Code instance 2020-02-27 12:04:23 -06:00
Asher
fd65cadaea Upload into releases directory 2020-02-27 11:21:40 -06:00
Asher
70ad2354bb Remove gif link for now 2020-02-27 10:57:39 -06:00
Asher
01710cf6ff Update download instructions
We aren't providing binaries any longer.
2020-02-26 15:42:31 -06:00
Asher
b5c425b3a6 Major version -> 3.0.0 2020-02-26 15:09:56 -06:00
Asher
b00f6bf078 Improve error display when VS Code fails to load
Was looking a bit janky with the style changes.
2020-02-26 14:57:07 -06:00
Asher
a2639ac617 Add yarn vscode step to build instructions 2020-02-26 14:48:08 -06:00
Asher
07fcf1be7a Merge branch 'master' into restructure 2020-02-26 14:42:33 -06:00
Asher
75ca5b2b0b Add gcs upload to CI 2020-02-26 14:25:14 -06:00
Asher
082f25faf1 Add a note about external authentication
Closes #1369.
2020-02-26 14:25:13 -06:00
Asher
595ce6f5e3 Update styling
Just sort of winging it.
2020-02-26 14:25:12 -06:00
Anmol Sethi
b1760c8d29 Fix clean.sh 2020-02-25 21:30:58 -05:00
Asher
c870398c86 Switch to loose files
For #1306.
2020-02-25 18:23:35 -06:00
Asher
f76c809f7d Fix workspace storage creation
Fixes #1308.
2020-02-25 12:47:22 -06:00
Asher
4c6e4bedeb Fix port being randomized
Also make it a number.
2020-02-24 16:49:10 -06:00
Asher
04e449c546 Require cert-key with cert
Fixes #1312.
2020-02-24 15:01:59 -06:00
Anmol Sethi
c147711ade Remind user that generated password is in logs
Closes #446
2020-02-21 15:22:13 -05:00
Anmol Sethi
bd7583a254 Obey process.env.PORT 2020-02-21 14:49:58 -05:00
Asher
33b3523bf4 Prefer command-line directory to last visited directory
If you want to use the last visited directory you should omit the
directory from the command line.

Fixes #1132.
2020-02-21 12:45:57 -06:00
Asher
cf0f11105b Handle upgrade from binary to loose files
As best we can, anyway.
2020-02-21 12:32:58 -06:00
Anmol Sethi
9b7a203fe5 Make login page pretty 2020-02-20 20:08:25 -05:00
Asher
e44ac0a30e Use last positional argument as working directory
Instead of the first.
2020-02-20 18:48:17 -06:00
Asher
319cd3f7ab Make updating work for both binary and loose releases 2020-02-20 18:48:16 -06:00
Anmol Sethi
815dc06118 Use npm rebuild instead of yarn --no-scripts in vscode.sh 2020-02-20 19:11:01 -05:00
Anmol Sethi
3a2644a2bc Fix vscode.sh 2020-02-20 18:36:38 -05:00
Anmol Sethi
65690fca65 Fix CSP for Safari 2020-02-20 18:24:32 -05:00
Anmol Sethi
25288b1afd Cleanup FAQ and mention GPG/SSH forwarding 2020-02-20 17:45:04 -05:00
Asher
288e794c99 Update locale file location
Should make language packs work again.
2020-02-20 12:52:23 -06:00
Asher
c567a06ff5 Fix HTTPS redirects and TLS sockets
It still won't work behind a base path, but if you're using a reverse
proxy you can just redirect to HTTPS yourself. And should probably
handle TLS termination there too.

For sockets I just needed to add back the proxy call.
2020-02-19 17:59:12 -06:00
Asher
e5b68a8f4c Switch to new extensions API 2020-02-19 14:36:48 -06:00
Asher
51a5c77cb8 Add binary extraction
I temporarily removed this during the refactor so it needed to be added
back. This time I bundled it with the nbin loader code since it's all
related (will also make it easier to remove).
2020-02-19 14:15:01 -06:00
Anmol Sethi
b9e7a3daa7 Add back .editorconfig 2020-02-19 14:22:14 -05:00
Asher
80b2d9481f Don't display stack trace for cli parse failures
Just display the error message and exit. The stack trace isn't necessary
(since it's likely user error) and is potentially confusing.
2020-02-19 11:15:39 -06:00
Asher
0e2eaa9b34 Add valid values for --log 2020-02-19 11:11:29 -06:00
Asher
0263188431 Handle --long=value format in the cli parser 2020-02-19 10:54:23 -06:00
Anmol Sethi
fa30639784 Update issue template 2020-02-19 00:58:28 -05:00
Anmol Sethi
015b8dcf13 Merge branch 'ensure-release' into restructure 2020-02-19 00:28:21 -05:00
Anmol Sethi
9f3240346c Doc fixes 2020-02-19 00:27:02 -05:00
Anmol Sethi
b76364db31 Flesh out FAQ 2020-02-18 23:31:48 -05:00
Anmol Sethi
a065c12e83 CI Fixes 2020-02-18 23:31:40 -05:00
Anmol Sethi
76831f11fc Merge branch 'fix-ci' into restructure 2020-02-18 19:07:34 -05:00
Anmol Sethi
b6aa0cbcba Fix docs 2020-02-18 19:07:01 -05:00
Anmol Sethi
5681c87e33 Fix bugs in CI 2020-02-18 19:06:35 -05:00
Asher
46d6e17508 Prepare for release
- Add VS Code icon
- Trim dashboard to just display dedicated VS Code section
- Version was getting unset during build
- Add back nbin shim which I temporarily took out earlier
- Update tests for log level env var changes
2020-02-18 17:31:23 -06:00
Anmol Sethi
1aaa53622d Pass through travis tag in run.sh 2020-02-18 16:34:28 -05:00
Asher
bdb189a9bb Allow https images
Fixes extension icons not loading.
2020-02-18 14:57:12 -06:00
Asher
8793110941 Implement endpoint for getting recent directories 2020-02-18 14:13:22 -06:00
Asher
16bcf59cb0 Merge pull request #1357 2020-02-18 13:33:06 -06:00
Asher
f6b092b12d Merge branch 'restructure' 2020-02-18 13:30:37 -06:00
Asher
d47591e253 Inject base path into manifest
Might fix #1181, although not for the reasons I initially
thought (because the URLs are resolved from the manifest path, not the
path of the current page). This should ensure that the URLs used by the
manifest are always correct regardless of the manifest's path.
2020-02-18 13:01:18 -06:00
Anmol Sethi
1a91588c42 Add docker image pushing 2020-02-18 13:28:13 -05:00
Asher
39a57700bc Enable access to vscode cli 2020-02-18 12:24:12 -06:00
Anmol Sethi
1a54f6b7ef Merge remote-tracking branch 'origin/restructure' into anmol-restructure 2020-02-18 12:52:29 -05:00
Anmol Sethi
eb3cf303ad Add back travis since github actions is trash 2020-02-18 11:32:57 -05:00
Anmol Sethi
0d31a51eeb Add github release creation 2020-02-15 16:20:41 -05:00
Anmol Sethi
61d1af0413 Add macOS release step 2020-02-14 21:16:23 -05:00
Anmol Sethi
4aa15401c3 Format and lint 2020-02-14 20:00:19 -05:00
Anmol Sethi
80b1b1b672 Shake CI and docs up 2020-02-14 19:46:17 -05:00
Asher
0ec83f8736 Check updates daily instead of every time
Also add a way to force a check.
2020-02-14 16:46:22 -06:00
Asher
db54f78e8e Implement automatic updates 2020-02-14 15:58:39 -06:00
Asher
b8fa7da972 Simplify frontend
Just a login form and a list of applications. No modals or anything like
that.
2020-02-13 20:10:14 -06:00
Asher
bf1be16d11 Fix base path
Now it should work whether you have a trailing slash or not.
2020-02-13 12:41:58 -06:00
Asher
cc79edb312 Fix duplicate files opening with folder parameter
Reworked from d574012871 to fit in the
new structure.
2020-02-13 12:04:22 -06:00
Asher
ac4f2b8215 Update VS Code to 1.42.0 2020-02-13 11:55:15 -06:00
Asher
c8fc54bfb1 Update VS Code version for CI 2020-02-12 18:03:53 -06:00
Asher
d574012871 Fix duplicate files opening with folder parameter
When using a query parameter without a scheme, the scheme defaults to
`file`. This results in the files in the explorer being technically
different from the file picker files because they are file:// instead of
vscode-remote://, causing the same file to open twice and causing
numerous issues.

Normally the file explorer wouldn't even load at all in this case but we
provide a file service for file:// URLs as a failsafe for certain files
that wouldn't load correctly in the past. These files load fine now
using the vscode-remote scheme, so I'm also removing that service.

Related: #1351.
Fixes #1294.
2020-02-12 16:57:44 -06:00
Asher
250a54220c Update VS Code to 1.42.0 2020-02-12 14:31:34 -06:00
Asher
5baf16622f Fix VS Code product configuration not loading 2020-02-07 16:47:51 -06:00
Asher
256419004d Implement cli parser 2020-02-07 14:43:08 -06:00
Anmol Sethi
6a693e7181 Merge pull request #1346 from SuperSandro2000/patch-1
Dockerfile: Combine two runs
2020-02-06 16:10:50 -06:00
Sandro
b38cfa473e Dockerfile: Combine two runs 2020-02-06 23:05:40 +01:00
Asher
26f8216ec8 Un-nest a switch 2020-02-06 14:00:38 -06:00
Asher
63f3c04c57 Move user data directory logic out of patch 2020-02-06 13:12:00 -06:00
Asher
8a0f1d846e Move start path logic out of patch and fix it 2020-02-06 12:29:38 -06:00
Asher
efaeb3b110 Add hidden username field for accessibility 2020-02-06 10:26:53 -06:00
Asher
6cebfa469d Generalize initial app logic 2020-02-05 18:47:00 -06:00
Asher
205775ac97 Only serve HTML on specific index.html requests
Otherwise there is risk of an infinite loop through the iframe where the
fallback keeps loading the root HTML which itself has an iframe...
2020-02-05 17:45:24 -06:00
Asher
4cc181cedc Make routing base path agnostic 2020-02-05 17:38:21 -06:00
Asher
a149c5fc60 Pass arguments to code-server during watch 2020-02-05 14:48:15 -06:00
Asher
6e809b6a31 Handle when VS Code fails to load
This is mostly for development where VS Code might not have finished
compiling yet.
2020-02-05 14:23:42 -06:00
Asher
7c6fe56043 Add logo to background 2020-02-05 13:30:29 -06:00
Asher
8cc11d1688 Improve routing 2020-02-05 13:07:07 -06:00
Asher
dbc5c065f8 Improve HTTP provider registration 2020-02-04 16:55:27 -06:00
Asher
4a54e914fc Remove 32 bit arm builds from CI 2020-02-04 16:01:00 -06:00
Asher
b30aefcfb1 Add linting steps and improve testing steps 2020-02-04 15:42:55 -06:00
Asher
b29346ecdf Implement new structure 2020-02-04 14:31:44 -06:00
Anmol Sethi
ef8da3864f Merge pull request #1336 from cdr/revert-1334-patch-1
Revert "Decomission Travis"
2020-02-04 11:35:37 -06:00
Anmol Sethi
108eb297d8 Revert "Decomission Travis" 2020-02-04 11:35:19 -06:00
Anmol Sethi
19f3acd9f0 Merge pull request #1334 from sr229/patch-1
Decomission Travis
2020-02-04 11:35:12 -06:00
Dean Sheather
3ee6b0ff0b Automatically push releases to GCS (#1318) 2020-02-04 11:32:45 -06:00
Anmol Sethi
e270f7da1b Merge pull request #1335 from cdr/revert-1225-master
Revert "Updates manifest.json"
2020-02-04 11:30:14 -06:00
Anmol Sethi
e6117decd0 Revert "Set display property in manifest to fullscreen"
This reverts commit c7127cb248.
2020-02-04 11:30:00 -06:00
TheHllm
c7127cb248 Set display property in manifest to fullscreen 2020-02-04 11:23:21 -06:00
Ben Potter
50234e5f04 Reflects new location of vscodeVersion (#1327) 2020-02-04 11:10:32 -06:00
Ayane Satomi
5f562dc113 Decomission Travis
We won't need Travis from now on since it's only purpose is to do Mac builds, which no one uses anymore.
2020-02-04 20:43:54 +08:00
Anmol Sethi
bb8bad49dc Remvoe question issue template
Closes #1331
2020-02-03 11:35:18 -06:00
Anmol Sethi
a674d882bf Remove buggy -v flag from README.md docker run
Closes #1270

Will describe in FAQ how to keep state.
2020-01-27 12:17:59 -06:00
Asher
f51e045cd5 Use the nbin centos container to build again
This will put the glibc requirement back down to what it used to be.
2020-01-17 16:27:36 -06:00
Asher
8122b7f69e Remove unused upload service
No longer needed since VS Code has their own now.
2020-01-17 12:23:36 -06:00
Asher
25f18beda4 Fix version test 2020-01-16 18:09:19 -06:00
Asher
7e7923706f Fix version generated from Git tag 2020-01-16 17:59:11 -06:00
Asher
ae35673489 Use custom Yarn cache directory
Makes it easier to upload and restore.
2020-01-16 15:39:44 -06:00
Asher
23f142fdc6 Cache Yarn cache 2020-01-16 15:23:25 -06:00
Asher
101139fabf Fix Drone CI releases
Also skip the 32 bit arm releases since they don't currently build
anyway.
2020-01-16 14:53:59 -06:00
Asher
e2d354c8f2 Move manifest icon to the root as well 2020-01-16 12:11:56 -06:00
Asher
7c178805ea Add comment about the manifest's served location
Also for #1278.
2020-01-16 11:44:17 -06:00
Asher
45f70e741f Move manifest to the root
Fixes #1278.
2020-01-16 11:36:17 -06:00
Asher
1474a82c7d Add insecure access notification 2020-01-16 11:15:22 -06:00
Asher
d97feca3ba Add code-server version to the about dialog 2020-01-15 18:02:19 -06:00
Asher
b2669e78bf Implement ExtHostStoragePaths for the browser
This appears to make vscodevim work again.
2020-01-15 17:13:06 -06:00
Asher
66ee6e8201 Ignore 32 bit arm failures for now
Seems we are running out of memory.
2020-01-15 13:48:27 -06:00
Asher
62f050fda7 Add a simple test 2020-01-15 13:22:45 -06:00
Asher
57425377e5 Use CI dockerfile for pushing Docker image 2020-01-15 13:22:45 -06:00
Asher
174cb2f8a9 Remove unused Docker step from CI script 2020-01-15 13:22:42 -06:00
Asher
42bddce21f Add defaults for environment variables
So we don't have to keep setting them for each CI and every single step
since there doesn't seem to be a way to share them between steps in
Drone.
2020-01-15 13:21:58 -06:00
Asher
f2a15795a1 Use draft releases for Drone
This gives us a chance to review it and add notes.
2020-01-15 13:21:58 -06:00
Asher
6dd5e515c5 Travis release on tags only and remove Docker push
The manual tagging is necessary to sync up the releases of the two
different CIs.
2020-01-15 13:21:55 -06:00
Asher
92da02ef3e Add Drone CI caching 2020-01-15 13:20:58 -06:00
Ayane Satomi
3ce7129492 Drone CI migration (#1261) 2020-01-15 13:14:05 -06:00
Asher
336ee28888 Update Node to 12.14.0 2020-01-08 16:30:44 -06:00
Asher
3f2240ab65 Update logger 2020-01-08 16:30:34 -06:00
Asher
1087037728 Don't push latest and v2 Docker tags automatically
We should only push those when the version is confirmed to work.
2020-01-08 15:05:12 -06:00
Asher
1959d82912 Increase cache timeout
The Mac build seems to be terminated due to a timeout during the caching
stage.
2020-01-08 13:20:50 -06:00
Asher
8024144381 Update VS Code to 1.41.1 2020-01-07 18:27:41 -06:00
Asher
6a1dcab7a6 Update nbin
Should finally be able to build with Node v12 now.
2020-01-07 18:27:28 -06:00
Asher
e6d1f2a7c8 Update VS Code to 1.41.0 2019-12-16 16:52:29 -06:00
Asher
44c4722edf Fix data directory path in Dockerfile 2019-12-10 12:06:52 -06:00
Asher
e5fc63f2c8 Fix accessing manifest behind basic auth
Apparently the manifest spec doesn't include sending credentials in an
attempt to be secure by default.

Fixes #1212.
2019-12-09 11:25:59 -06:00
Asher
015a99e87d Always install VS Code dependencies
This fixes the case where the script is killed before all the
dependencies were fully installed.
2019-12-09 10:55:24 -06:00
Simen Eriksen
884491d72b Update Dockerfile to fix EACCES issue on mount (#1191)
https://github.com/cdr/code-server/issues/1188 
Fixes issue with permissions mounting in directories in the container. Folders are generated by root causing issues when the container user "coder" wants to create sub-folders. This fix solves it, at least on Crostini (ChromeOS)
2019-12-05 13:38:03 -06:00
Asher
e14362f322 Pass along Node options 2019-11-14 17:20:23 -06:00
Asher
917aa48072 Update enterprise link
Fixes #1172.
2019-11-14 11:16:08 -06:00
Asher
938c6ef829 Update fail2ban configuration
Fixes #1177.
2019-11-14 11:14:27 -06:00
Sandro
0add01d383 Delete apt lists from final image (#1174) 2019-11-14 11:12:21 -06:00
Asher
2018024810 Hash password
Fixes issues with unexpected characters breaking things when setting the
cookie (like semicolons).

This change as-is does not affect the security of code-server
itself (we've just replaced the static password with a static hash) but
if we were to add a salt in the future it would let us invalidate keys
by rehashing with a new salt which could be handy.
2019-11-07 15:57:57 -06:00
Asher
a1d6bcb8e5 Handle cookies more robustly
If you visit /login/ instead of /login the cookie will be set at /login
instead of / which means the cookie can't be read at the root. It will
redirect to the login page which *can* read the cookie at /login and
redirect back resulting in an infinite loop.

The previous solution relied on setting the cookie at / (any invalid
value works) which then overrode the login page cookie since
parseCookies only kept a single value. So the login page would see the
same cookie the root was seeing and not redirect back. However, that
behavior depends on the cookies being in the right order which I'm not
sure is guaranteed.

This new method tests all available cookies and always sets the cookie
so the root path will be able to read it in case the login page is
seeing a cookie the root can't.

It also goes a step further and explicitly sets the path on the cookie
which fixes the case where there is a permanent misconfiguration
redirecting /login to /login/. Otherwise the cookie would continually be
set on /login only and you'd have another loop. It also means you only
need to delete one cookie to log out.

Lastly add some properties to make the cookies a bit more secure.
2019-11-07 13:36:18 -06:00
ecrode
727ac6483b Clear password when redirecting to login
Should prevent endless redirects when the cookie is set on a different path or domain (like with a dot prefix).
2019-11-07 11:38:10 -06:00
Asher
2c15c09fc0 Add missing telemetry option 2019-11-06 15:47:34 -06:00
Asher
2ad2582cc0 Minor readme updates and fixes 2019-11-05 13:49:18 -06:00
Asher
cee0ac213c Fix error activating extensions on insecure domains
Doesn't affect Firefox but it does affect other browsers.

Fixes #1136.
2019-11-04 17:10:00 -06:00
Asher
780a673017 Add meta tag to allow full screen app on iOS
Fixes #933.
2019-11-04 16:01:01 -06:00
Asher
af71203955 Fix relaunching during an update 2019-11-01 10:51:23 -05:00
Asher
fc3acfabb2 Fix update check 2019-10-30 17:35:50 -05:00
Asher
3d5db8313a Add secure domain to requirements 2019-10-30 10:33:07 -05:00
Asher
73cf8f34e3 Fix outgoing scheme transformation
Accidentally used local instead of remote.

Fixes #1127.
2019-10-30 10:32:57 -05:00
dependabot[bot]
766efd6079 Bump mixin-deep from 1.3.1 to 1.3.2 (#1126)
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-29 15:20:12 -05:00
Asher
87485948ad Kill inner process if parent process dies
Fixes #1076.
2019-10-29 14:43:27 -05:00
Asher
7e4a73ce2d Fix schema matching against vscode-remote
Fixes #1104.
2019-10-29 11:42:28 -05:00
Asher
2f0878d9b7 Revert remote scheme change
It doesn't show in the explorer anymore so there's no point. Also remove
the local scheme transform which is no longer required with the latest
client-side extension implementation.
2019-10-29 11:26:50 -05:00
Marc-André Daigneault
f65c9b23fc Add docker-compose file (#680) 2019-10-29 11:08:01 -05:00
Asher
cd859d117f Start pushing to latest Docker tag 2019-10-29 11:04:38 -05:00
Asher
e22964915a Support opening workspaces from command line
Partly addresses #1121.
2019-10-28 16:25:51 -05:00
Asher
197d0b6ca9 Strip internal env vars when spawning the shell
This should fix all those reports of code-server dropping straight to
Node and things like #1121.
2019-10-28 16:08:32 -05:00
Asher
422503ef98 Proxy child exit code when exiting parent process
This fixes code-server exiting with zero on errors.
2019-10-28 14:57:01 -05:00
Asher
ea36345d2c Allow fetching any resource
Fixes #1118.
2019-10-28 14:29:51 -05:00
Asher
a89d83cbba Fix other incorrect usages of split 2019-10-28 14:03:13 -05:00
Asher
83ff31b620 Fix passwords that contain =
Fixes #1119.

Apparently `split` does not work the way I'd expect.
2019-10-28 13:47:31 -05:00
Asher
3a9b032c72 Add heartbeat file (#1115)
Fixes #1050.
2019-10-28 09:59:34 -05:00
Asher
f73e9225b4 Remove directory restrictions for /webview/vscode-resource
This makes viewing images work. Fixes #1111.
2019-10-25 15:52:39 -05:00
Asher
168ccb0dfc Prevent cache changes when patch updates 2019-10-25 13:12:04 -05:00
Asher
58f7f5b769 Properly fix blank --cert flag
See #1109.
2019-10-25 12:04:43 -05:00
Asher
b8e6369fbe Fix empty --cert not generating self-signed certificate
Fixes #1101.
2019-10-25 11:01:42 -05:00
Asher
d81d5f499f Remove Cloud Run button
Unfortunately it doesn't allow websockets so it's not working.
2019-10-24 16:45:22 -05:00
Asher
4be178d234 Move Google Cloud button to match Digital Ocean 2019-10-24 16:09:02 -05:00
Ayane Satomi
9c40466b4b Add Google Cloud quick-launch button (#1069) 2019-10-24 16:07:44 -05:00
Asher
95693fb58e Handle /webview/vscode-resource/file urls
See #1103.
2019-10-24 14:35:25 -05:00
Asher
e7945bea94 Enable password authentication by default
Fixes #1062.
2019-10-24 12:35:26 -05:00
Asher
91f49e1efd Set SHELL to /bin/bash in Docker
Fixes #1081, fixes #918.
2019-10-23 13:34:00 -05:00
Asher
eea9c1618c Move client-side extension code out of patch 2019-10-23 13:12:11 -05:00
Asher
f1b38e4e48 Fix out-of-order readme section 2019-10-23 11:54:47 -05:00
Asher
ff99a1d768 Add security section to readme
See #1062.
2019-10-23 11:49:17 -05:00
Asher
7f07b8f66c Push Docker using Linux build
Instead of doing a separate redundant build. The main problem was that
the files weren't being cached. There is probably a better way of
solving this but this seems to be the simplest for now.
2019-10-22 18:43:21 -05:00
Asher
faae03da6b Add prerequisites for building 2019-10-22 17:49:43 -05:00
Asher
a6e4f96737 Fix webview html being excluded
Also skip the workbench html since we have our own.
2019-10-22 16:09:27 -05:00
Asher
cc7585bbc2 Port onigasm fix for PHP 2019-10-22 11:39:00 -05:00
Asher
14a0cd3ffd Remove build files in source
They aren't used in subsequent files and just slow down CI since it has
to extract from the cache and then package the changes.
2019-10-22 11:26:46 -05:00
Asher
3ff83eda45 Ensure VS Code dependencies and built-in extensions exist
Fixes #1087.
2019-10-22 10:49:00 -05:00
Asher
f133b00851 Fix darwin detection 2019-10-21 16:51:01 -05:00
Asher
ece840834d Move login page to browser directory
Fixes it not being included in the optimized build as well as making it
more consistent.
2019-10-21 15:02:41 -05:00
Asher
76f6ff4145 Fix alpine check 2019-10-21 14:09:04 -05:00
Asher
2458cde498 Update source & build paths in Dockerfile 2019-10-21 12:25:18 -05:00
Asher
82e2b8a169 Move source to its own directory
This matches how the rest of the build is organized but also hopefully
solves an issue where the VS Code directory is empty because we try to
cache it directly and Travis might be creating it.
2019-10-21 11:16:47 -05:00
Asher
5aa2abaf9f Cache VS Code in CI 2019-10-21 11:06:09 -05:00
Asher
fdb2308c62 Update path to built binary in Dockerfile 2019-10-21 10:56:27 -05:00
Asher
4cd2f2cd52 Simplify build and development steps 2019-10-18 19:10:55 -05:00
Asher
88cef85f62 Add dependencies required for running build script
They were getting pulled from VS Code which doesn't work if you are
running a build in a clean repo.
2019-10-18 18:40:10 -05:00
Asher
bdd11f741b Update to 1.39.2
Also too the opportunity to rewrite the build script since there was a
change in the build steps (mainly how the product JSON is inserted) and
to get the build changes out of the patch. It also no longer relies on
external caching (we'll want to do this within CI instead).
2019-10-18 18:20:02 -05:00
Asher
56ce780522 Prevent process.exit() 2019-10-11 17:00:17 -05:00
Asher
567010e163 Cache extension tar requests 2019-10-11 14:28:02 -05:00
Asher
4ae2c81157 Remember last workspace or directory 2019-10-11 14:26:20 -05:00
Asher
ae43e2016f Handle up/down on server 2019-10-10 17:05:30 -05:00
Asher
3f6cbfa4dd Handle up/down/close on client 2019-10-10 16:12:38 -05:00
Asher
1c50b5285e Resolve bundling issues with node-browser 2019-10-10 15:36:56 -05:00
Asher
ea9c511db8 Check major version when getting latest version 2019-10-08 16:23:39 -05:00
Adam Vernon
e1e3f32643 Add missing PWA icon (#1060)
- Copy old icon back into repository
- Update path to icon from manifest file
- Add link metadata tag for iOS PWA icon to workbench.html
- Add link metadata tag for iOS PWA icon to login page
2019-10-07 12:55:02 -05:00
Asher
4290cffe3b Update packages for in-browser extensions 2019-10-07 11:59:09 -05:00
Asher
548d095611 Add support for running extensions in the browser 2019-10-04 18:14:19 -05:00
Ayane Satomi
846dcbb947 [doc/quickstart] use version from GH-379 (#1012)
This is the same PR but with some fixes for v2.

Co-Authored-by: nwtnsqrd <30381446+nwtnsqrd@users.noreply.github.com>
Signed-off-by: Ayane Satomi <chinodesuuu@gmail.com>
2019-09-30 11:19:46 -05:00
yeya24
d7d3368cc2 update yaml tag to v2 (#1034)
Signed-off-by: yeya24 <yb532204897@gmail.com>
2019-09-30 10:30:33 -05:00
Kyle Carberry
134040fea3 Update extensions section of readme 2019-09-27 09:39:29 -05:00
Asher
65caa26d40 Pass log level to extension host 2019-09-23 16:57:48 -05:00
Asher
7812f6b75a Fix uncaught errors when extracting zips
Fixes #966.
2019-09-23 16:57:48 -05:00
Asher
637e58f255 Prevent opening invalid paths 2019-09-23 16:57:47 -05:00
Asher
6135630fc0 Fix not being able to change language while code-server is running
See #948. Only applies if code-server already tried to load a
language.
2019-09-23 16:57:46 -05:00
Ayane Satomi
22058c5f86 [README] add Requirements section (#1000)
* [README] add Requirements section

This is based user feedback and testing. This may expand if need be

Signed-off-by: Ayane Satomi <chinodesuuu@gmail.com>

* [README] raised the bar a bit more

Apparently we were targeting GLIBC 2.17

Signed-off-by: Ayane Satomi <chinodesuuu@gmail.com>
2019-09-18 13:21:38 -05:00
Asher
616bdb35f3 Fix using port zero 2019-09-18 11:39:16 -05:00
Asher
42f7b5d12b Still using the wrong tag for Docker 2019-09-17 15:25:17 -05:00
Asher
53818b0e36 Use conditional instead of inline if 2019-09-17 15:12:58 -05:00
Asher
6f08b13540 Fix 404 when logging into Docker 2019-09-17 14:59:01 -05:00
Asher
d36526b1c8 Move DOCKER_BUILD check into deploy script 2019-09-17 14:45:03 -05:00
Asher
1252eb6a8a Don't use a sequence for Docker deploy
Travis deploy scripts can't be sequences yet.
2019-09-17 14:39:21 -05:00
Asher
4733c31a2f Fix incorrect Docker tag 2019-09-17 14:21:23 -05:00
Asher
17c5173d8b Use --build-arg for GitHub token 2019-09-17 14:00:51 -05:00
Asher
d0a08f6dd7 Pass GITHUB_TOKEN through to Docker build
Also, back to drafts.
2019-09-17 13:42:14 -05:00
Asher
7b5d6d186b Fix some CI issues
- Don't error if trying to tag and the tag already exists.
- Add the build arguments to the Docker build.
- Set an environment variable for the Docker build rather than using
- the username because the username always exists (on master anyway).
2019-09-17 13:17:38 -05:00
Asher
3851927396 Push latest v2 Docker image as well 2019-09-17 12:31:40 -05:00
Asher
7eececead6 Use v2 tag for Docker example 2019-09-17 12:27:32 -05:00
Luca Casonato
b8c3d96fcd Auto docker building and deploy in travis CI (#521)
* made spacing for travis yaml consistent in file

* cleaned up .travis.yml and moved some code into a script

* checking if i can get travis to build this

* travis

* fixed an if statement

* fixed travis.yml file

* replaced my name with codercom
2019-09-17 11:55:05 -05:00
Asher
a2ee6c8e73 Don't build on tags 2019-09-17 11:26:59 -05:00
Asher
ef069d9b0e Update nbin
The latest version contains a fix for passing null options when reading
a file. Fixes #544.
2019-09-16 18:04:41 -05:00
Asher
6a864f9f47 Make websocket upgrade check case-insensitive
Fixes #925.
2019-09-16 15:05:45 -05:00
Asher
5c16399810 Fix accessing versioned resource using file service
Fixes #986.
2019-09-16 15:05:44 -05:00
Ayane Satomi
0141ded35d [doc/quickstart] add init docs (#981)
Add init docs so people have an idea what to do when starting up a service.

Resolves GH-947
2019-09-16 15:05:25 -05:00
137 changed files with 15708 additions and 6247 deletions

View File

@@ -1,12 +1,2 @@
Dockerfile
build
deployment
doc
.github
.gitignore
.node-version
.travis.yml
LICENSE
README.md
node_modules
release
**
!release

6
.editorconfig Normal file
View File

@@ -0,0 +1,6 @@
root = true
[*]
indent_style = space
trim_trailing_whitespace = true
indent_size = 2

24
.eslintrc.yaml Normal file
View File

@@ -0,0 +1,24 @@
parser: "@typescript-eslint/parser"
env:
browser: true
es6: true # Map, etc.
mocha: true
node: true
parserOptions:
ecmaVersion: 2018
sourceType: module
extends:
- eslint:recommended
- plugin:@typescript-eslint/recommended
- plugin:import/recommended
- plugin:import/typescript
- plugin:prettier/recommended
- prettier # Removes eslint rules that conflict with prettier.
- prettier/@typescript-eslint # Remove conflicts again.
rules:
# For overloads.
no-dupe-class-members: off
"@typescript-eslint/no-use-before-define": off

3
.github/CODEOWNERS vendored
View File

@@ -1,2 +1 @@
* @code-asher @kylecarbs
Dockerfile @nhooyr
* @code-asher @nhooyr

View File

@@ -1,22 +0,0 @@
---
name: Bug Report
about: Report problems and unexpected behavior.
title: ''
labels: 'bug'
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- All extension-specific issues should be created with the `Extension Bug` template. -->
- `code-server` version: <!-- The version of code-server -->
- OS Version: <!-- OS version, cloud provider, -->
## Description
<!-- Describes the problem here -->
## Steps to Reproduce
1. <!-- step 1: click ... -->
1. <!-- step 2: ... -->

View File

@@ -1,22 +0,0 @@
---
name: Extension Bug
about: Report problems and unexpected behavior with extensions.
title: ''
labels: 'extension-specific'
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates. -->
- `code-server` version: <!-- The version of code-server -->
- OS Version: <!-- OS version, cloud provider, -->
- Extension: <!-- Link to extension -->
## Description
<!-- Describes the problem here -->
## Steps to Reproduce
1. <!-- step 1: click ... -->
1. <!-- step 2: ... -->

View File

@@ -1,11 +0,0 @@
---
name: Feature Request
about: Suggest an idea for this project.
title: ''
labels: 'feature'
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Describe the feature you'd like. -->

View File

@@ -1,17 +0,0 @@
---
name: Question
about: Ask a question.
title: ''
labels: 'question'
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates. -->
## Description
<!-- A description of the the question. -->
## Related Issues
<!-- Any issues related to your question. -->

11
.github/issue_template.md vendored Normal file
View File

@@ -0,0 +1,11 @@
<!--
Please file all questions and support requests at https://www.reddit.com/r/codeserver/
The issue tracker is only for bugs.
Please see https://github.com/cdr/code-server/blob/master/doc/FAQ.md#how-do-i-debug-issues-with-code-server
and include any logging information relevant to the issue.
Please search for existing issues before filing.
Please ensure you cannot reproduce on VS Code before filing.
-->

View File

@@ -1,6 +1,4 @@
<!-- Please answer these questions before submitting your PR. Thanks! -->
### Describe in detail the problem you had and how this PR fixes it
### Is there an open issue you can link to?
<!--
Please link to the issue this PR solves.
If there is no existing issue, please first create one unless the fix is minor.
-->

10
.gitignore vendored
View File

@@ -1,3 +1,9 @@
node_modules
*.tsbuildinfo
.cache
build
release
dist*
out*
release/
release-upload/
node_modules
binaries

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "lib/vscode"]
path = lib/vscode
url = https://github.com/microsoft/vscode

View File

@@ -1 +0,0 @@
10.16.0

1
.npmrc
View File

@@ -1 +0,0 @@
scripts-prepend-node-path=true

4
.prettierrc.yaml Normal file
View File

@@ -0,0 +1,4 @@
printWidth: 120
semi: false
trailingComma: all
arrowParens: always

2
.stylelintrc.yaml Normal file
View File

@@ -0,0 +1,2 @@
extends:
- stylelint-config-recommended

View File

@@ -1,49 +1,58 @@
language: node_js
node_js:
- 10.16.0
services:
- docker
matrix:
language: minimal
jobs:
include:
- os: linux
dist: trusty
env:
- VSCODE_VERSION="1.38.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="linux"
- os: linux
dist: trusty
env:
- VSCODE_VERSION="1.38.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="alpine"
- os: osx
env:
- VSCODE_VERSION="1.38.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER"
before_install:
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export MINIFY="true"; fi
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export PACKAGE="true"; fi
script:
- travis_wait 30 scripts/ci.bash
- name: Test
if: tag IS blank
script: ./ci/image/run.sh "yarn && git submodule update --init && yarn vscode:patch && ./ci/ci.sh"
deploy: null
- name: Linux Release
if: tag IS present
script:
- travis_wait 60 ./ci/image/run.sh "yarn && yarn vscode && ci/release.sh && ./ci/build-test.sh"
- ./ci/release-image/push.sh
- name: Linux ARM64 Release
if: tag IS present
script:
- ./ci/image/run.sh "yarn && yarn vscode && ci/release.sh && ./ci/build-test.sh"
- ./ci/release-image/push.sh
arch: arm64
- name: MacOS Release
if: tag IS present
os: osx
language: node_js
node_js: 12
script: yarn && yarn vscode && travis_wait 60 ci/release.sh && ./ci/build-test.sh
before_deploy:
- echo "$VERSION-vsc$VSCODE_VERSION" "$TRAVIS_COMMIT"
- git config --local user.name "$USER_NAME"
- git config --local user.email "$USER_EMAIL"
- git tag "$VERSION-vsc$VSCODE_VERSION" "$TRAVIS_COMMIT"
- echo "$JSON_KEY" | base64 --decode > ./ci/key.json
deploy:
provider: releases
file_glob: true
draft: true
tag_name: "$VERSION-vsc$VSCODE_VERSION"
target_commitish: "$TRAVIS_COMMIT"
name: "$VERSION-vsc$VSCODE_VERSION"
skip_cleanup: true
api_key:
secure: YL/x24KjYjgYXPcJWk3FV7FGxI79Mh6gBECQEcdlf3fkLEoKFVgzHBoUNWrFPzyR4tgLyWNAgcpD9Lkme1TRWTom7UPjXcwMNyLcLa+uec7ciSAnYD9ntLTpiCuPDD1u0LtRGclSi/EHQ+F8YVq+HZJpXTsJeAmOmihma3GVbGKSZr+BRum+0YZSG4w+o4TOlYzw/4bLWS52MogZcwpjd+hemBbgXLuGU2ziKv2vEKCZFbEeA16II4x1WLI4mutDdCeh7+3aLzGLwDa49NxtsVYNjyNFF75JhCTCNA55e2YMiLz9Uq69IXe/mi5F7xUaFfhIqqLNyKBnKeEOzu3dYnc+8n3LjnQ+00PmkF05nx9kBn3UfV1kwQGh6QbyDmTtBP07rtUMyI14aeQqHjxsaVRdMnwj9Q2DjXRr8UDqESZF0rmK3pHCXS2fBhIzLE8tLVW5Heiba2pQRFMHMZW+KBE97FzcFh7is90Ait3T8enfcd/PWFPYoBejDAdjwxwOkezh5N5ZkYquEfDYuWrFi6zRFCktsruaAcA+xGtTf9oilBBzUqu8Ie+YFWH5me83xakcblJWdaW/D2rLJAJH3m6LFm8lBqyUgDX5t/etob6CpDuYHu5D1J3XINOj/+aLAcadq6qlh70PMZS3zYffUu3JlzaD2amlSHIT8b5YXFc=
file:
- release/*.tar.gz
- release/*.zip
on:
repo: cdr/code-server
branch: master
- provider: releases
edge: true
draft: true
overwrite: true
tag_name: $TRAVIS_TAG
target_commitish: $TRAVIS_COMMIT
name: $TRAVIS_TAG
file:
- release/*.tar.gz
- release/*.zip
on:
tags: true
- provider: gcs
edge: true
bucket: "codesrv-ci.cdr.sh"
upload_dir: "releases"
key_file: ./ci/key.json
local_dir: release-upload
on:
tags: true
# TODO: The gcs provider fails to install on arm64.
condition: $TRAVIS_CPU_ARCH = amd64
cache:
timeout: 600
yarn: true
timeout: 1000
directories:
- .cache
- lib/vscode/.build/extensions

View File

@@ -1,59 +0,0 @@
FROM node:10.16.0
ARG codeServerVersion=docker
ARG vscodeVersion
# Install VS Code's deps. These are the only two it seems we need.
RUN apt-get update && apt-get install -y \
libxkbfile-dev \
libsecret-1-dev
# Ensure latest yarn.
RUN npm install -g yarn@1.13
WORKDIR /src
COPY . .
RUN yarn \
&& MINIFY=true yarn build "${vscodeVersion}" "${codeServerVersion}" \
&& yarn binary "${vscodeVersion}" "${codeServerVersion}" \
&& mv "/src/build/code-server${codeServerVersion}-vsc${vscodeVersion}-linux-x86_64-built/code-server${codeServerVersion}-vsc${vscodeVersion}-linux-x86_64" /src/build/code-server \
&& rm -r /src/build/vscode-* \
&& rm -r /src/build/code-server*-linux-*
# We deploy with ubuntu so that devs have a familiar environment.
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y \
openssl \
net-tools \
git \
locales \
sudo \
dumb-init \
vim \
curl \
wget
RUN locale-gen en_US.UTF-8
# We cannot use update-locale because docker will not use the env variables
# configured in /etc/default/locale so we need to set it manually.
ENV LC_ALL=en_US.UTF-8
RUN adduser --gecos '' --disabled-password coder && \
echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
USER coder
# We create first instead of just using WORKDIR as when WORKDIR creates, the
# user is root.
RUN mkdir -p /home/coder/project
WORKDIR /home/coder/project
# This ensures we have a volume mounted even if the user forgot to do bind
# mount. So that they do not lose their data if they delete the container.
VOLUME [ "/home/coder/project" ]
COPY --from=0 /src/build/code-server /usr/local/bin/code-server
EXPOSE 8080
ENTRYPOINT ["dumb-init", "code-server", "--host", "0.0.0.0"]

136
README.md
View File

@@ -1,134 +1,58 @@
# code-server &middot; [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/cdr/code-server/blob/master/LICENSE) [!["Latest Release"](https://img.shields.io/github/release/cdr/code-server.svg)](https://github.com/cdr/code-server/releases/latest) [![Build Status](https://img.shields.io/travis/com/cdr/code-server/master)](https://github.com/cdr/code-server)
# code-server
`code-server` is [VS Code](https://github.com/Microsoft/vscode) running on a
remote server, accessible through the browser.
Try it out:
```bash
docker run -it -p 127.0.0.1:8080:8080 -v "${HOME}/.local/share/code-server:/home/coder/.local/share/code-server" -v "$PWD:/home/coder/project" codercom/code-server
docker run -it -p 127.0.0.1:8080:8080 -v "$PWD:/home/coder/project" -u "$(id -u):$(id -g)" codercom/code-server:latest
```
- **Consistent environment:** Code on your Chromebook, tablet, and laptop with a
consistent dev environment. develop more easily for Linux if you have a
Windows or Mac, and pick up where you left off when switching workstations.
- **Code anywhere:** Code on your Chromebook, tablet, and laptop with a
consistent dev environment. Develop on a Linux machine and pick up from any
device with a web browser.
- **Server-powered:** Take advantage of large cloud servers to speed up tests,
compilations, downloads, and more. Preserve battery life when you're on the go
since all intensive computation runs on your server.
![Screenshot](/doc/assets/ide.gif)
![Example gif](/doc/assets/code-server.gif)
## Getting Started
### Requirements
- 64-bit host.
- At least 1GB of RAM.
- 2 cores or more are recommended (1 core works but not optimally).
- Secure connection over HTTPS or localhost (required for service workers and
clipboard support).
- For Linux: GLIBC 2.17 or later and GLIBCXX 3.4.15 or later.
### Run over SSH
Use [sshcode](https://github.com/codercom/sshcode) for a simple setup.
### Docker
See the Docker one-liner mentioned above. Dockerfile is at [/Dockerfile](/Dockerfile).
To debug Golang using the
[ms-vscode-go extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go),
you need to add `--security-opt seccomp=unconfined` to your `docker run`
arguments when launching code-server with Docker. See
[#725](https://github.com/cdr/code-server/issues/725) for details.
### Digital Ocean
[![Create a Droplet](./doc/assets/droplet.svg)](https://marketplace.digitalocean.com/apps/code-server?action=deploy)
### Binaries
1. [Download a binary](https://github.com/cdr/code-server/releases). (Linux and
OS X supported. Windows coming soon)
2. Unpack the downloaded file then run the binary.
[![Create a Droplet](./doc/assets/droplet.svg)](https://marketplace.digitalocean.com/apps/code-server)
### Releases
1. [Download a release](https://github.com/cdr/code-server/releases). (Linux and
OS X supported. Windows support planned.)
2. Unpack the downloaded release then run the included `code-server` script.
3. In your browser navigate to `localhost:8080`.
- For self-hosting and other information see [doc/quickstart.md](doc/quickstart.md).
- For hosting on cloud platforms see [doc/deploy.md](doc/deploy.md).
## FAQ
### Build
- If you also plan on developing, set the `OUT` environment variable. Otherwise
it will build in this directory which will cause issues because `yarn watch`
will try to compile the build directory as well.
- Run `yarn build ${vscodeVersion} ${codeServerVersion}` in this directory (for
example: `yarn build 1.36.0 development`).
- If you target the same VS Code version our Travis builds do everything will
work but if you target some other version it might not (we have to do some
patching to VS Code so different versions aren't always compatible).
- You can run the built code with `node path/to/build/out/vs/server/main.js` or run
`yarn binary` with the same arguments in the previous step to package the
code into a single binary.
## Known Issues
- Creating custom VS Code extensions and debugging them doesn't work.
- Extension profiling and tips are currently disabled.
## Future
- **Stay up to date!** Get notified about new releases of code-server.
![Screenshot](/doc/assets/release.gif)
- Windows support.
- Electron and Chrome OS applications to bridge the gap between local<->remote.
- Run VS Code unit tests against our builds to ensure features work as expected.
## Extensions
At the moment we can't use the official VS Code Marketplace. We've created a
custom extension marketplace focused around open-sourced extensions. However,
you can manually download the extension to your extensions directory. It's also
possible to set your own marketplace URLs by setting the `SERVICE_URL` and
`ITEM_URL` environment variables.
## Telemetry
Use the `--disable-telemetry` flag to completely disable telemetry. We use the
data collected to improve code-server.
See [./doc/FAQ.md](./doc/FAQ.md).
## Contributing
### Development
```shell
git clone https://github.com/microsoft/vscode
cd vscode
git checkout <see travis.yml for the VS Code version to use here>
git clone https://github.com/cdr/code-server src/vs/server
cd src/vs/server
yarn patch:apply
yarn
yarn watch
# Wait for the initial compilation to complete (it will say "Finished compilation").
# Run the next command in another shell.
yarn start
# Visit http://localhost:8080
```
If you run into issues about a different version of Node being used, try running
`npm rebuild` in the VS Code directory and ignore the error at the end from
`vscode-ripgrep`.
### Upgrading VS Code
We patch VS Code to provide and fix some functionality. As the web portion of VS
Code matures, we'll be able to shrink and maybe even entirely eliminate our
patch. In the meantime, however, upgrading the VS Code version requires ensuring
that the patch still applies and has the intended effects.
To generate a new patch, **stage all the changes** you want to be included in
the patch in the VS Code source, then run `yarn patch:generate` in this
directory.
Our changes include:
- Change the remote schema to `code-server`.
- Allow multiple extension directories (both user and built-in).
- Modify the loader, websocket, webview, service worker, and asset requests to
use the URL of the page as a base (and TLS if necessary for the websocket).
- Send client-side telemetry through the server and get the initial log level
from the server.
- Add an upload service for use in editor windows and the explorer along with a
file prefix to ignore for temporary files created during upload.
- Make changing the display language work.
- Make hiding or toggling the menu bar possible.
- Make it possible for us to load code on the client.
- Modify the build process to include our code.
## License
[MIT](LICENSE)
See [./doc/CONTRIBUTING.md](./doc/CONTRIBUTING.md).
## Enterprise
Visit [our enterprise page](https://coder.com/enterprise) for more information
about our enterprise offering.
## Commercialization
If you would like to commercialize code-server, please contact
contact@coder.com.
Visit [our enterprise page](https://coder.com) for more information about our
enterprise offerings.

21
ci/build-test.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# build-test.bash -- Make sure the build worked.
# This is to make sure we don't have Node version errors or any other
# compilation-related errors.
set -euo pipefail
function main() {
cd "$(dirname "${0}")/.." || exit 1
local output
output=$(node ./build/out/node/entry.js --list-extensions 2>&1)
if echo "$output" | grep 'was compiled against a different Node.js version'; then
echo "$output"
exit 1
else
echo "Build ran successfully"
fi
}
main "$@"

372
ci/build.ts Normal file
View File

@@ -0,0 +1,372 @@
import * as cp from "child_process"
import * as fs from "fs-extra"
import Bundler from "parcel-bundler"
import * as path from "path"
import * as util from "util"
enum Task {
Build = "build",
Watch = "watch",
}
class Builder {
private readonly rootPath = path.resolve(__dirname, "..")
private readonly vscodeSourcePath = path.join(this.rootPath, "lib/vscode")
private readonly buildPath = path.join(this.rootPath, "build")
private readonly codeServerVersion: string
private currentTask?: Task
public constructor() {
this.ensureArgument("rootPath", this.rootPath)
this.codeServerVersion = this.ensureArgument(
"codeServerVersion",
process.env.VERSION || require(path.join(this.rootPath, "package.json")).version,
)
}
public run(task: Task | undefined): void {
this.currentTask = task
this.doRun(task).catch((error) => {
console.error(error.message)
process.exit(1)
})
}
private async task<T>(message: string, fn: () => Promise<T>): Promise<T> {
const time = Date.now()
this.log(`${message}...`, !process.env.CI)
try {
const t = await fn()
process.stdout.write(`took ${Date.now() - time}ms\n`)
return t
} catch (error) {
process.stdout.write("failed\n")
throw error
}
}
/**
* Writes to stdout with an optional newline.
*/
private log(message: string, skipNewline = false): void {
process.stdout.write(`[${this.currentTask || "default"}] ${message}`)
if (!skipNewline) {
process.stdout.write("\n")
}
}
private async doRun(task: Task | undefined): Promise<void> {
if (!task) {
throw new Error("No task provided")
}
switch (task) {
case Task.Watch:
return this.watch()
case Task.Build:
return this.build()
default:
throw new Error(`No task matching "${task}"`)
}
}
/**
* Make sure the argument is set. Display the value if it is.
*/
private ensureArgument(name: string, arg?: string): string {
if (!arg) {
throw new Error(`${name} is missing`)
}
this.log(`${name} is "${arg}"`)
return arg
}
/**
* Build VS Code and code-server.
*/
private async build(): Promise<void> {
process.env.NODE_OPTIONS = "--max-old-space-size=32384 " + (process.env.NODE_OPTIONS || "")
process.env.NODE_ENV = "production"
await this.task("cleaning up old build", async () => {
if (!process.env.SKIP_VSCODE) {
return fs.remove(this.buildPath)
}
// If skipping VS Code, keep the existing build if any.
try {
const files = await fs.readdir(this.buildPath)
return Promise.all(files.filter((f) => f !== "lib").map((f) => fs.remove(path.join(this.buildPath, f))))
} catch (error) {
if (error.code !== "ENOENT") {
throw error
}
}
})
const commit = require(path.join(this.vscodeSourcePath, "build/lib/util")).getVersion(this.rootPath) as string
if (!process.env.SKIP_VSCODE) {
await this.buildVscode(commit)
} else {
this.log("skipping vs code build")
}
await this.buildCodeServer(commit)
this.log(`final build: ${this.buildPath}`)
}
private async buildCodeServer(commit: string): Promise<void> {
await this.task("building code-server", async () => {
return util.promisify(cp.exec)("tsc --outDir ./out-build --tsBuildInfoFile ./.prod.tsbuildinfo", {
cwd: this.rootPath,
})
})
await this.task("bundling code-server", async () => {
return this.createBundler("dist-build", commit).bundle()
})
await this.task("copying code-server into build directory", async () => {
await fs.mkdirp(this.buildPath)
await Promise.all([
fs.copy(path.join(this.rootPath, "out-build"), path.join(this.buildPath, "out")),
fs.copy(path.join(this.rootPath, "dist-build"), path.join(this.buildPath, "dist")),
// For source maps and images.
fs.copy(path.join(this.rootPath, "src"), path.join(this.buildPath, "src")),
])
})
await this.copyDependencies("code-server", this.rootPath, this.buildPath, false, {
commit,
version: this.codeServerVersion,
})
}
private async buildVscode(commit: string): Promise<void> {
await this.task("building vs code", () => {
return util.promisify(cp.exec)("yarn gulp compile-build", { cwd: this.vscodeSourcePath })
})
await this.task("building builtin extensions", async () => {
const exists = await fs.pathExists(path.join(this.vscodeSourcePath, ".build/extensions"))
if (exists && !process.env.CI) {
process.stdout.write("already built, skipping...")
} else {
await util.promisify(cp.exec)("yarn gulp compile-extensions-build", { cwd: this.vscodeSourcePath })
}
})
await this.task("optimizing vs code", async () => {
return util.promisify(cp.exec)("yarn gulp optimize --gulpfile ./coder.js", { cwd: this.vscodeSourcePath })
})
if (process.env.MINIFY) {
await this.task("minifying vs code", () => {
return util.promisify(cp.exec)("yarn gulp minify --gulpfile ./coder.js", { cwd: this.vscodeSourcePath })
})
}
const vscodeBuildPath = path.join(this.buildPath, "lib/vscode")
await this.task("copying vs code into build directory", async () => {
await fs.mkdirp(path.join(vscodeBuildPath, "resources/linux"))
await Promise.all([
fs.move(
path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`),
path.join(vscodeBuildPath, "out"),
),
fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")),
fs.copy(
path.join(this.vscodeSourcePath, "resources/linux/code.png"),
path.join(vscodeBuildPath, "resources/linux/code.png"),
),
])
})
await this.copyDependencies("vs code", this.vscodeSourcePath, vscodeBuildPath, true, {
commit,
date: new Date().toISOString(),
})
}
private async copyDependencies(
name: string,
sourcePath: string,
buildPath: string,
ignoreScripts: boolean,
merge: object,
): Promise<void> {
await this.task(`copying ${name} dependencies`, async () => {
return Promise.all(
["node_modules", "package.json", "yarn.lock"].map((fileName) => {
return fs.copy(path.join(sourcePath, fileName), path.join(buildPath, fileName))
}),
)
})
const fileName = name === "code-server" ? "package" : "product"
await this.task(`writing final ${name} ${fileName}.json`, async () => {
const json = JSON.parse(await fs.readFile(path.join(sourcePath, `${fileName}.json`), "utf8"))
return fs.writeFile(
path.join(buildPath, `${fileName}.json`),
JSON.stringify(
{
...json,
...merge,
},
null,
2,
),
)
})
if (process.env.MINIFY) {
await this.task(`restricting ${name} to production dependencies`, async () => {
await util.promisify(cp.exec)(`yarn --production ${ignoreScripts ? "--ignore-scripts" : ""}`, {
cwd: buildPath,
})
})
}
}
private async watch(): Promise<void> {
let server: cp.ChildProcess | undefined
const restartServer = (): void => {
if (server) {
server.kill()
}
const s = cp.fork(path.join(this.rootPath, "out/node/entry.js"), process.argv.slice(3))
console.log(`[server] spawned process ${s.pid}`)
s.on("exit", () => console.log(`[server] process ${s.pid} exited`))
server = s
}
const vscode = cp.spawn("yarn", ["watch"], { cwd: this.vscodeSourcePath })
const tsc = cp.spawn("tsc", ["--watch", "--pretty", "--preserveWatchOutput"], { cwd: this.rootPath })
const bundler = this.createBundler()
const cleanup = (code?: number | null): void => {
this.log("killing vs code watcher")
vscode.removeAllListeners()
vscode.kill()
this.log("killing tsc")
tsc.removeAllListeners()
tsc.kill()
if (server) {
this.log("killing server")
server.removeAllListeners()
server.kill()
}
this.log("killing bundler")
process.exit(code || 0)
}
process.on("SIGINT", () => cleanup())
process.on("SIGTERM", () => cleanup())
vscode.on("exit", (code) => {
this.log("vs code watcher terminated unexpectedly")
cleanup(code)
})
tsc.on("exit", (code) => {
this.log("tsc terminated unexpectedly")
cleanup(code)
})
const bundle = bundler.bundle().catch(() => {
this.log("parcel watcher terminated unexpectedly")
cleanup(1)
})
bundler.on("buildEnd", () => {
console.log("[parcel] bundled")
})
bundler.on("buildError", (error) => {
console.error("[parcel]", error)
})
vscode.stderr.on("data", (d) => process.stderr.write(d))
tsc.stderr.on("data", (d) => process.stderr.write(d))
// From https://github.com/chalk/ansi-regex
const pattern = [
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))",
].join("|")
const re = new RegExp(pattern, "g")
/**
* Split stdout on newlines and strip ANSI codes.
*/
const onLine = (proc: cp.ChildProcess, callback: (strippedLine: string, originalLine: string) => void): void => {
let buffer = ""
if (!proc.stdout) {
throw new Error("no stdout")
}
proc.stdout.setEncoding("utf8")
proc.stdout.on("data", (d) => {
const data = buffer + d
const split = data.split("\n")
const last = split.length - 1
for (let i = 0; i < last; ++i) {
callback(split[i].replace(re, ""), split[i])
}
// The last item will either be an empty string (the data ended with a
// newline) or a partial line (did not end with a newline) and we must
// wait to parse it until we get a full line.
buffer = split[last]
})
}
let startingVscode = false
let startedVscode = false
onLine(vscode, (line, original) => {
console.log("[vscode]", original)
// Wait for watch-client since "Finished compilation" will appear multiple
// times before the client starts building.
if (!startingVscode && line.includes("Starting watch-client")) {
startingVscode = true
} else if (startingVscode && line.includes("Finished compilation")) {
if (startedVscode) {
bundle.then(restartServer)
}
startedVscode = true
}
})
onLine(tsc, (line, original) => {
// tsc outputs blank lines; skip them.
if (line !== "") {
console.log("[tsc]", original)
}
if (line.includes("Watching for file changes")) {
bundle.then(restartServer)
}
})
}
private createBundler(out = "dist", commit?: string): Bundler {
return new Bundler(
[
path.join(this.rootPath, "src/browser/pages/app.ts"),
path.join(this.rootPath, "src/browser/register.ts"),
path.join(this.rootPath, "src/browser/serviceWorker.ts"),
],
{
cache: true,
cacheDir: path.join(this.rootPath, ".cache"),
detailedReport: true,
minify: !!process.env.MINIFY,
hmr: false,
logLevel: 1,
outDir: path.join(this.rootPath, out),
publicUrl: `/static/${commit || "development"}/dist`,
target: "browser",
},
)
}
}
const builder = new Builder()
builder.run(process.argv[2] as Task)

12
ci/ci.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
cd "$(dirname "$0")/.."
yarn fmt
yarn lint
yarn test
}
main "$@"

11
ci/clean.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
git clean -Xffd
git submodule foreach --recursive git clean -xffd
git submodule foreach --recursive git reset --hard
}
main "$@"

18
ci/code-server.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env sh
# Runs code-server with the bundled Node binary.
# More complicated than readlink -f or realpath to support macOS.
# See https://github.com/cdr/code-server/issues/1537
get_installation_dir() {
# We read the symlink, which may be relative from $0.
dst="$(readlink "$0")"
# We cd into the $0 directory.
cd "$(dirname "$0")"
# Now we can cd into the dst directory.
cd "$(dirname "$dst")"
# Finally we use pwd -P to print the absolute path of the directory of $dst.
pwd -P
}
dir=$(get_installation_dir)
exec "$dir/node" "$dir/out/node/entry.js" "$@"

13
ci/dev-image/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM node:12
RUN apt-get update && apt-get install -y \
curl \
iproute2 \
vim \
iptables \
net-tools \
libsecret-1-dev \
libx11-dev \
libxkbfile-dev
CMD ["/bin/bash"]

49
ci/dev-image/exec.sh Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# exec.sh opens an interactive bash session inside of a docker container
# for improved isolation during development
# if the container exists it is restarted if necessary, then reused
set -euo pipefail
cd "$(dirname "$0")"
# Ensure submodules are cloned and up to date.
git submodule update --init
container_name=code-server-dev
enter() {
echo "--- Entering $container_name"
docker exec -it $container_id /bin/bash
}
run() {
echo "--- Spawning $container_name"
container_id=$(docker run \
-it \
--name $container_name \
"-v=$PWD:/code-server" \
"-w=/code-server" \
"-p=127.0.0.1:8080:8080" \
$([[ -t 0 ]] && echo -it || true) \
$container_name)
}
build() {
echo "--- Building $container_name"
cd ../../
docker build -t $container_name -f ./ci/dev-image/Dockerfile . > /dev/null
}
container_id=$(docker container inspect --format="{{.Id}}" $container_name 2> /dev/null) || true
if [ "$container_id" != "" ]; then
echo "-- Starting container"
docker start $container_id > /dev/null
enter
exit 0
fi
build
run
enter

32
ci/fmt.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
shfmt -i 2 -w -s -sr $(git ls-files "*.sh")
local prettierExts
prettierExts=(
"*.js"
"*.ts"
"*.tsx"
"*.html"
"*.json"
"*.css"
"*.md"
"*.toml"
"*.yaml"
"*.yml"
)
prettier --write --loglevel=warn $(git ls-files "${prettierExts[@]}")
if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then
echo "Files need generation or are formatted incorrectly:"
git -c color.ui=always status | grep --color=no '\[31m'
echo "Please run the following locally:"
echo " yarn fmt"
exit 1
fi
}
main "$@"

23
ci/image/Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM centos:7
RUN yum update -y && yum install -y \
devtoolset-6 \
gcc-c++ \
xz \
ccache \
git \
wget \
openssl \
libxkbfile-devel \
libsecret-devel \
libx11-devel
RUN mkdir /usr/share/node && cd /usr/share/node \
&& curl "https://nodejs.org/dist/v12.16.3/node-v12.16.3-linux-$(uname -m | sed 's/86_//; s/aarch/arm/').tar.xz" | tar xJ --strip-components=1 --
ENV PATH "$PATH:/usr/share/node/bin"
RUN npm install -g yarn@1.22.4
RUN curl -L "https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_linux_$(uname -m | sed 's/x86_/amd/; s/aarch64/arm/')" > /usr/local/bin/shfmt \
&& chmod +x /usr/local/bin/shfmt
ENTRYPOINT ["/bin/bash", "-c"]

26
ci/image/run.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
# This, strangely enough, fixes the arm build being terminated for not having
# output on Travis. It's as if output is buffered and only displayed once a
# certain amount is collected. Five seconds didn't work but one second seems
# to generate enough output to make it work.
local pid
while true; do
echo 'Still running...'
sleep 1
done &
pid=$!
docker build ci/image
imageTag="$(docker build -q ci/image)"
docker run -t --rm -e CI -e GITHUB_TOKEN -e TRAVIS_TAG -v "$(yarn cache dir):/usr/local/share/.cache/yarn/v6" -v "$PWD:/repo" -w /repo "$imageTag" "$*"
kill $pid
}
main "$@"

10
ci/lib.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
set_version() {
local code_server_version=${VERSION:-${TRAVIS_TAG:-}}
if [[ -z $code_server_version ]]; then
code_server_version=$(grep version ./package.json | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[:space:]')
fi
export VERSION=$code_server_version
}

11
ci/lint.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js")
stylelint $(git ls-files "*.css")
tsc --noEmit
}
main "$@"

View File

@@ -0,0 +1,43 @@
FROM debian:10
RUN apt-get update \
&& apt-get install -y \
curl \
dumb-init \
htop \
locales \
man \
nano \
git \
procps \
ssh \
sudo \
vim \
&& rm -rf /var/lib/apt/lists/*
# https://wiki.debian.org/Locale#Manually
RUN sed -i "s/# en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen \
&& locale-gen
ENV LANG=en_US.UTF-8
RUN chsh -s /bin/bash
ENV SHELL=/bin/bash
RUN adduser --gecos '' --disabled-password coder && \
echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
RUN curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4/fixuid-0.4-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \
chown root:root /usr/local/bin/fixuid && \
chmod 4755 /usr/local/bin/fixuid && \
mkdir -p /etc/fixuid && \
printf "user: coder\ngroup: coder\n" > /etc/fixuid/config.yml
COPY release/code-server*.tar.gz /tmp/
RUN cd /tmp && tar -xzf code-server*.tar.gz && rm code-server*.tar.gz && \
mv code-server* /usr/local/lib/code-server && \
ln -s /usr/local/lib/code-server/code-server /usr/local/bin/code-server
EXPOSE 8080
USER coder
WORKDIR /home/coder
ENTRYPOINT ["dumb-init", "fixuid", "-q", "/usr/local/bin/code-server", "--bind-addr", "0.0.0.0:8080", "."]

22
ci/release-image/push.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
source ./ci/lib.sh
set_version
if [[ ${CI:-} ]]; then
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
fi
imageTag="codercom/code-server:$VERSION"
if [[ ${TRAVIS_CPU_ARCH:-} == "arm64" ]]; then
imageTag+="-arm64"
fi
docker build -t "$imageTag" -f ./ci/release-image/Dockerfile .
docker push codercom/code-server
}
main "$@"

76
ci/release.sh Executable file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# ci.bash -- Build code-server in the CI.
set -euo pipefail
function package() {
local target
target=$(uname | tr '[:upper:]' '[:lower:]')
if [[ $target == "linux" ]]; then
# Alpine's ldd doesn't have a version flag but if you use an invalid flag
# (like --version) it outputs the version to stderr and exits with 1.
local ldd_output
ldd_output=$(ldd --version 2>&1 || true)
if echo "$ldd_output" | grep -iq musl; then
target="alpine"
fi
fi
local arch
arch=$(uname -m | sed 's/aarch/arm/')
echo -n "Creating release..."
cp "$(command -v node)" ./build
cp README.md ./build
cp LICENSE.txt ./build
cp ./lib/vscode/ThirdPartyNotices.txt ./build
cp ./ci/code-server.sh ./build/code-server
local archive_name="code-server-$VERSION-$target-$arch"
mkdir -p ./release
local ext
if [[ $target == "linux" ]]; then
ext=".tar.gz"
tar -czf "release/$archive_name$ext" --transform "s/^\.\/build/$archive_name/" ./build
else
mv ./build "./$archive_name"
ext=".zip"
zip -r "release/$archive_name$ext" "./$archive_name"
mv "./$archive_name" ./build
fi
echo "done (release/$archive_name)"
# release-upload is for uploading to the GCP bucket whereas release is used for GitHub.
mkdir -p "./release-upload/$VERSION"
cp "./release/$archive_name$ext" "./release-upload/$VERSION/$target-$arch$ext"
mkdir -p "./release-upload/latest"
cp "./release/$archive_name$ext" "./release-upload/latest/$target-$arch$ext"
}
# This script assumes that yarn has already ran.
function build() {
# Always minify and package on CI.
if [[ ${CI:-} ]]; then
export MINIFY="true"
fi
yarn build
}
function main() {
cd "$(dirname "${0}")/.."
source ./ci/lib.sh
set_version
build
if [[ ${CI:-} ]]; then
package
fi
}
main "$@"

4
ci/tsconfig.json Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"include": ["./**/*.ts"]
}

3417
ci/vscode.patch Normal file

File diff suppressed because it is too large Load Diff

22
ci/vscode.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
# 1. Ensures VS Code is cloned.
# 2. Patches it.
# 3. Installs it.
main() {
cd "$(dirname "$0")/.."
git submodule update --init
# If the patch fails to apply, then it's likely already applied
yarn vscode:patch &> /dev/null || true
(
cd lib/vscode
# Install VS Code dependencies.
yarn
)
}
main "$@"

40
doc/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,40 @@
# Contributing
## Development Workflow
- [VS Code prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites)
```shell
yarn
yarn vscode
yarn watch # Visit http://localhost:8080 once completed.
```
To develop inside of an isolated docker container:
```shell
./ci/dev-image/exec.sh
root@12345:/code-server# yarn
root@12345:/code-server# yarn vscode
root@12345:/code-server# yarn watch
```
Any changes made to the source will be live reloaded.
If changes are made to the patch and you've built previously you must manually
reset VS Code then run `yarn vscode:patch`.
Some docs are available at [../src/node/app](../src/node/app) on how code-server
works internally.
## Build
- [VS Code prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites)
```shell
yarn
yarn vscode
yarn build
node ./build/out/node/entry.js # Run the built JavaScript with Node.
```

179
doc/FAQ.md Normal file
View File

@@ -0,0 +1,179 @@
# FAQ
## Questions?
Please file all questions and support requests at https://www.reddit.com/r/codeserver/
The issue tracker is only for bugs.
## What's the deal with extensions?
Unfortunately, the Microsoft VS Code Marketplace license prohibits use with any non Microsoft
product.
See https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf
> Marketplace Offerings are intended for use only with Visual Studio Products and Services
> and you may only install and use Marketplace Offerings with Visual Studio Products and Services.
As a result, Coder has created its own marketplace for open source extensions. It works by scraping
GitHub for VS Code extensions and building them. It's not perfect but getting better by the day with
more and more extensions.
Issue [#1299](https://github.com/cdr/code-server/issues/1299) is a big one in making the experience here
better by allowing the community to submit extensions and repos to avoid waiting until the scraper finds
an extension.
If an extension does not work, try to grab its VSIX from its Github releases or build it yourself and
copy it to the extensions folder.
## How is this different from VS Code Online?
VS Code Online is a closed source managed service by Microsoft and only runs on Azure.
code-server is open source and can be freely run on any machine.
## How should I expose code-server to the internet?
By far the most secure method of using code-server is via
[sshcode](https://github.com/codercom/sshcode) as it runs code-server and then forwards
its port over SSH and requires no setup on your part other than having a working SSH server.
You can also forward your SSH key and GPG agent to the remote machine to securely access GitHub
and securely sign commits without duplicating your keys onto the the remote machine.
1. https://developer.github.com/v3/guides/using-ssh-agent-forwarding/
1. https://wiki.gnupg.org/AgentForwarding
If you cannot use sshcode, then you will need to ensure there is some sort of authorization in
front of code-server and that you are using HTTPS to secure all connections.
By default when listening externally, code-server enables password authentication using a
randomly generated password so you can use that. You can set the `PASSWORD` environment variable
to use your own instead. If you want to handle authentication yourself, use `--auth none`
to disable password authentication.
**note**: code-server will rate limit password authentication attempts at 2 a minute and 12 an hour.
If you want to use external authentication you should handle this with a reverse
proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy).
For HTTPS, you can use a self signed certificate by passing in just `--cert` or pass in an existing
certificate by providing the path to `--cert` and the path to its key with `--cert-key`.
If `code-server` has been passed a certificate it will also respond to HTTPS
requests and will redirect all HTTP requests to HTTPS. Otherwise it will respond
only to HTTP requests.
You can use [Let's Encrypt](https://letsencrypt.org/) to get an SSL certificate
for free.
## How do I securely access web services?
code-server is capable of proxying to any port using either a subdomain or a
subpath which means you can securely access these services using code-server's
built-in authentication.
### Sub-domains
You will need a DNS entry that points to your server for each port you want to
access. You can either set up a wildcard DNS entry for `*.<domain>` if your domain
name registrar supports it or you can create one for every port you want to
access (`3000.<domain>`, `8080.<domain>`, etc).
You should also set up TLS certificates for these subdomains, either using a
wildcard certificate for `*.<domain>` or individual certificates for each port.
Start code-server with the `--proxy-domain` flag set to your domain.
```
code-server --proxy-domain <domain>
```
Now you can browse to `<port>.<domain>`. Note that this uses the host header so
ensure your reverse proxy forwards that information if you are using one.
### Sub-paths
Just browse to `/proxy/<port>/`.
## x86 releases?
node has dropped support for x86 and so we decided to as well. See
[nodejs/build/issues/885](https://github.com/nodejs/build/issues/885).
## Alpine builds?
Just install `libc-dev` and code-server should work.
## Multi Tenancy
If you want to run multiple code-server's on shared infrastructure, we recommend using virtual
machines with a VM per user. This will easily allow users to run a docker daemon. If you want
to use kubernetes, you'll definitely want to use [kubevirt](https://kubevirt.io) to give each
user a virtual machine instead of just a container. Docker in docker while supported requires
privileged containers which are a security risk in a multi tenant infrastructure.
## Docker in code-server docker container?
If you'd like to access docker inside of code-server, we'd recommend running a docker:dind container
and mounting in a directory to share between dind and the code-server container at /var/run. After, install
the docker CLI in the code-server container and you should be able to access the daemon as the socket
will be shared at /var/run/docker.sock.
In order to make volume mounts work, mount the home directory in the code-server container and the
dind container at the same path. i.e you'd volume mount a directory from the host to `/home/coder`
on both. This will allow any volume mounts in the home directory to work. Similar process
to make volume mounts in any other directory work.
## Collaboration
At the moment we have no plans for multi user collaboration on code-server but we understand there is strong
demand and will work on it when the time is right.
## How can I disable telemetry?
Use the `--disable-telemetry` flag to completely disable telemetry. We use the
data collected only to improve code-server.
## How does code-server decide what workspace or folder to open?
code-server tries the following in order:
1. The `workspace` query parameter.
2. The `folder` query parameter.
3. The workspace or directory passed on the command line.
4. The last opened workspace or directory.
## How do I debug issues with code-server?
First run code-server with at least `debug` logging (or `trace` to be really
thorough) by setting the `--log` flag or the `LOG_LEVEL` environment variable.
`-vvv` and `--verbose` are aliases for `--log trace`.
```
code-server --log debug
```
Once this is done, replicate the issue you're having then collect logging
information from the following places:
1. stdout.
2. The most recently created directory in the `logs` directory (found in the
data directory; see below for how to find that).
3. The browser console and network tabs.
Additionally, collecting core dumps (you may need to enable them first) if
code-server crashes can be helpful.
### Where is the data directory?
If the `XDG_DATA_HOME` environment variable is set the data directory will be
`$XDG_DATA_HOME/code-server`. Otherwise the default is:
1. Linux: `~/.local/share/code-server`.
2. Mac: `~/Library/Application\ Support/code-server`.
## Enterprise
Visit [our enterprise page](https://coder.com) for more information about our
enterprise offerings.

BIN
doc/assets/code-server.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -1,75 +0,0 @@
# Installing code-server in your ChromiumOS/ChromeOS/CloudReady machine
This guide will show you how to install code-server into your CrOS machine.
## Using Crostini
One of the easier ways to run code-server is via
[Crostini](https://www.aboutchromebooks.com/tag/project-crostini/), the Linux
apps support feature in CrOS. Make sure you have enough RAM, HDD space and your
CPU has VT-x/ AMD-V support. If your chromebook has this, then you are
qualified to use Crostini.
If you are running R69, you might want to enable this on
[Chrome Flags](chrome://flags/#enable-experimental-crostini-ui).
If you run R72, however, this is already enabled for you.
After checking your prerequisites, follow the steps in [the self-host install guide](index.md)
on installing code-server. Once done, make sure code-server works by running
it. After running it, simply go to `penguin.linux.test:8080` to access
code-server. Now you should be greeted with this screen. If you did,
congratulations, you have installed code-server in your Chromebook!
![code-server on Chromebook](assets/cros.png)
Alternatively, if you ran code-server in another container and you need the IP
for that specific container, simply go to Termina's shell via `crosh` and type
`vsh termina`.
```bash
Loading extra module: /usr/share/crosh/dev.d/50-crosh.sh
Welcome to crosh, the Chrome OS developer shell.
If you got here by mistake, don't panic! Just close this tab and carry on.
Type 'help' for a list of commands.
If you want to customize the look/behavior, you can use the options page.
Load it by using the Ctrl+Shift+P keyboard shortcut.
crosh> vsh termina
(termina) chronos@localhost ~ $
```
While in termina, run `lxc list`. It should output the list of running containers.
```bash
(termina) chronos@localhost ~ $ lxc list
+---------|---------|-----------------------|------|------------|-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+---------|---------|-----------------------|------|------------|-----------+
| penguin | RUNNING | 100.115.92.199 (eth0) | | PERSISTENT | 0 |
+---------|---------|-----------------------|------|------------|-----------+
(termina) chronos@localhost ~ $
```
For this example, we show the default `penguin` container, which is exposed on
`eth0` at 100.115.92.199. Simply enter the IP of the container where the
code-server runs to Chrome.
## Using Crouton
[Crouton](https://github.com/dnschneid/crouton) is one of the old ways to get a
running full Linux via `chroot` on a Chromebook. To use crouton, enable
developer mode and go to `crosh`. This time, run `shell`, which should drop you
to `bash`.
Make sure you downloaded `crouton`, if so, go ahead and run it under
`~/Downloads`. After installing your chroot container via crouton, go ahead and
enter `enter-chroot` to enter your container.
Follow the instructions set in [the self-host install guide](index.md) to
install code-server. After that is done, run `code-server` and verify it works
by going to `localhost:8080`.
> At this point in writing, `localhost` seems to work in this method. However,
> the author is not sure if it applies still to newer Chromebooks.

View File

@@ -1,73 +0,0 @@
# Set up instance
## EC2 on AWS
- Click **Launch Instance** from your [EC2 dashboard](https://console.aws.amazon.com/ec2/v2/home).
- Select the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
- Select an appropriate instance size (we recommend t2.medium/large, depending
on team size and number of repositories/languages enabled), then
**Next: Configure Instance Details**.
- Select **Next: ...** until you get to the **Configure Security Group** page,
then add a **Custom TCP Rule** rule with port range set to `8080` and source
set to "Anywhere".
> Rules with source of 0.0.0.0/0 allow all IP addresses to access your
> instance. We recommend setting [security group rules](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html?icmpid=docs_ec2_console)
> to allow access from known IP addresses only.
- Click **Launch**.
- You will be prompted to create a key pair.
- From the dropdown choose "create a new pair", give the key pair a name.
- Click **Download Key Pair** and store the file in a safe place.
- Click **Launch Instances**.
- Head to your [EC2 dashboard](https://console.aws.amazon.com/ec2/v2/home)
and choose instances from the left panel.
- In the description of your EC2 instance copy the public DNS (iPv4) address
using the copy to clipboard button.
- Open a terminal on your computer and SSH into your instance:
```
ssh -i ${path to key pair} ubuntu@${public address}
```
## DigitalOcean
[Open your DigitalOcean dashboard](https://cloud.digitalocean.com/droplets/new)
to create a new droplet
- **Choose an image -** Select the **Distributions** tab and then choose Ubuntu.
- **Choose a size -** We recommend at least 4GB RAM and 2 CPU, more depending
on team size and number of repositories/languages enabled.
- Launch your instance.
- Open a terminal on your computer and SSH into your instance:
```
ssh root@${instance ip}
```
## Google Cloud
> Pre-requisite: Set up the [Google Cloud SDK](https://cloud.google.com/sdk/docs/)
> on your local machine
- [Open your Google Cloud console](https://console.cloud.google.com/compute/instances)
to create a new VM instance and click **Create Instance**.
- Choose an appropriate machine type (we recommend 2 vCPU and 7.5 GB RAM, more
depending on team size and number of repositories/languages enabled).
- Choose Ubuntu 16.04 LTS as your boot disk.
- Expand the "Management, security, disks, networking, sole tenancy" section,
go to the "Networking" tab, then under network tags add "code-server".
- Create your VM, and **take note** of its public IP address.
- Visit "VPC network" in the console and go to "Firewall rules". Create a new
firewall rule called "http-8080". Under "Target tags" add "code-server", and
under "Protocols and ports" tick "Specified protocols and ports" and "tcp".
Beside "tcp", add "8080", then create the rule.
- Open a terminal on your computer and SSH into your Google Cloud VM:
```
gcloud compute ssh --zone ${region} ${instance name}
```
# Run code-server
- Download the latest code-server release from the
[releases page](https://github.com/cdr/code-server/releases/latest)
to the instance, extract the file, then run the code-server binary:
```
wget https://github.com/cdr/code-server/releases/download/{version}/code-server{version}-linux-x64.tar.gz
tar -xvzf code-server{version}-linux-x64.tar.gz
cd code-server{version}-linux-x64
./code-server
```
- Open your browser and visit http://$public_ip:8080/ where `$public_ip` is
your instance's public IP address.
- For long-term use, set up a systemd service to run code-server.

View File

@@ -1,15 +0,0 @@
# Fail2Ban filter for code-server
[Definition]
failregex = ^INFO\s+Failed login attempt\s+{\"password\":\"(\\.|[^"])*\",\"remoteAddress\":\"<HOST>\"
# Use this instead for proxies (ensure the proxy is configured to send the
# X-Forwarded-For header).
# failregex = ^INFO\s+Failed login attempt\s+{\"password\":\"(\\.|[^"])*\",\"xForwardedFor\":\"<HOST>\"
ignoreregex =
datepattern = "timestamp":{EPOCH}}$
# Author: Dean Sheather

View File

@@ -1,73 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: code-server
---
apiVersion: v1
kind: Service
metadata:
name: code-server
namespace: code-server
spec:
ports:
- port: 8080
name: https
protocol: TCP
selector:
app: code-server
type: ClusterIP
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gp2
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
fsType: ext4
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: code-store
namespace: code-server
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 60Gi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: code-server
name: code-server
namespace: code-server
spec:
selector:
matchLabels:
app: code-server
replicas: 1
template:
metadata:
labels:
app: code-server
spec:
containers:
- image: codercom/code-server
imagePullPolicy: Always
name: code-servery
ports:
- containerPort: 8080
name: https
volumeMounts:
- name: code-server-storage
mountPath: /go/src
volumes:
- name: code-server-storage
persistentVolumeClaim:
claimName: code-store

View File

@@ -1,43 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: code-server
---
apiVersion: v1
kind: Service
metadata:
name: code-server
namespace: code-server
spec:
ports:
- port: 8080
name: https
protocol: TCP
selector:
app: code-server
type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: code-server
name: code-server
namespace: code-server
spec:
selector:
matchLabels:
app: code-server
replicas: 1
template:
metadata:
labels:
app: code-server
spec:
containers:
- image: codercom/code-server
imagePullPolicy: Always
name: code-server
ports:
- containerPort: 8080
name: https

View File

@@ -1,35 +0,0 @@
# Protecting code-server from bruteforce attempts
code-server outputs all failed login attempts, along with the IP address,
provided password, user agent and timestamp by default.
When using a reverse proxy such as Nginx or Apache, the remote address may
appear to be `127.0.0.1` or a similar address so `X-Forwarded-For` should be
used instead. Ensure that you are setting this value in your reverse proxy:
Nginx:
```
location / {
...
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
...
}
```
Apache:
```
<VirtualEnv>
...
SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
...
</VirtualEnv>
```
It is extremely important that you ensure that your code-server instance is not
accessible from the internet (use localhost or block it in your firewall).
## Fail2Ban
Fail2Ban allows for automatically banning and logging repeated failed
authentication attempts for many applications through regex filters. A working
filter for code-server can be found in `./code-server.fail2ban.conf`. Once this
is installed and configured correctly, repeated failed login attempts should
automatically be banned from connecting to your server.

View File

@@ -1,57 +0,0 @@
# Quickstart Guide
1. Visit the [releases page](https://github.com/cdr/code-server/releases) and
download the latest binary for your operating system.
2. Unpack the downloaded file then run the binary.
3. In your browser navigate to `localhost:8080`.
## Usage
Run `code-server --help` to view available options.
### Encrypting traffic with HTTPS
To encrypt the traffic between the browser and server use `code-server --cert`
followed by the path to your certificate. Additionally, you can use certificate
keys with `--cert-key` followed by the path to your key. If you pass `--cert`
without any path code-server will generate a self-signed certificate.
You can use [Let's Encrypt](https://letsencrypt.org/) to get an SSL certificate
for free.
### Nginx Reverse Proxy
The trailing slashes are important.
```
server {
listen 80;
listen [::]:80;
server_name code.example.com code.example.org;
location /some/path/ { # Or / if hosting at the root.
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
}
}
```
### Apache Reverse Proxy
```
<VirtualHost *:80>
ServerName code.example.com
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:8080/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:8080/$1 [P,L]
ProxyRequests off
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
ProxyPass / http://localhost:8080/ nocanon
ProxyPassReverse / http://localhost:8080/
</VirtualHost>
```

1
lib/vscode Submodule

Submodule lib/vscode added at ff91584411

View File

@@ -1,7 +0,0 @@
// Once our entry file is loaded we no longer need nbin to bypass normal Node
// execution. We can still shim the fs into the binary even when bypassing. This
// will ensure for example that a spawn like `${process.argv[0]} -e` will work
// while still allowing us to access files within the binary.
process.env.NBIN_BYPASS = true;
require("../../bootstrap-amd").load("vs/server/src/cli");

View File

@@ -1,36 +1,65 @@
{
"name": "code-server",
"license": "MIT",
"version": "3.2.0",
"scripts": {
"ensure-in-vscode": "bash ./scripts/tasks.bash ensure-in-vscode",
"preinstall": "yarn ensure-in-vscode && cd ../../../ && yarn || true",
"postinstall": "rm -rf node_modules/@types/node",
"start": "yarn ensure-in-vscode && nodemon --watch ../../../out --verbose ../../../out/vs/server/main.js",
"watch": "yarn ensure-in-vscode && cd ../../../ && yarn watch",
"build": "bash ./scripts/tasks.bash build",
"package": "bash ./scripts/tasks.bash package",
"package-prebuilt": "bash ./scripts/tasks.bash package-prebuilt",
"binary": "bash ./scripts/tasks.bash binary",
"patch:generate": "yarn ensure-in-vscode && cd ../../../ && git diff --staged > ./src/vs/server/scripts/vscode.patch",
"patch:apply": "yarn ensure-in-vscode && cd ../../../ && git apply ./src/vs/server/scripts/vscode.patch"
"clean": "ci/clean.sh",
"vscode": "ci/vscode.sh",
"vscode:patch": "cd ./lib/vscode && git apply ../../ci/vscode.patch",
"vscode:diff": "cd ./lib/vscode && git diff HEAD > ../../ci/vscode.patch",
"test": "mocha -r ts-node/register ./test/*.test.ts",
"lint": "ci/lint.sh",
"fmt": "ci/fmt.sh",
"runner": "cd ./ci && NODE_OPTIONS=--max_old_space_size=32384 ts-node ./build.ts",
"build": "yarn runner build",
"watch": "yarn runner watch"
},
"devDependencies": {
"@coder/nbin": "^1.2.0",
"@types/adm-zip": "^0.4.32",
"@types/fs-extra": "^8.0.1",
"@types/http-proxy": "^1.17.4",
"@types/mocha": "^5.2.7",
"@types/node": "^12.12.7",
"@types/parcel-bundler": "^1.12.1",
"@types/pem": "^1.9.5",
"@types/safe-compare": "^1.1.0",
"@types/tar-fs": "^1.16.1",
"@types/semver": "^7.1.0",
"@types/tar-fs": "^1.16.2",
"@types/tar-stream": "^1.6.1",
"nodemon": "^1.19.1"
"@types/ws": "^6.0.4",
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0",
"eslint": "^6.2.0",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-prettier": "^3.1.0",
"leaked-handles": "^5.2.0",
"mocha": "^6.2.0",
"parcel-bundler": "^1.12.4",
"prettier": "^1.18.2",
"stylelint": "^13.0.0",
"stylelint-config-recommended": "^3.0.0",
"ts-node": "^8.4.1",
"typescript": "3.7.2"
},
"resolutions": {
"@types/node": "^10.12.12",
"safe-buffer": "^5.1.1"
"@types/node": "^12.12.7",
"safe-buffer": "^5.1.1",
"vfile-message": "^2.0.2"
},
"dependencies": {
"@coder/logger": "^1.1.8",
"@coder/logger": "1.1.11",
"adm-zip": "^0.4.14",
"fs-extra": "^8.1.0",
"http-proxy": "^1.18.0",
"httpolyglot": "^0.1.2",
"limiter": "^1.1.5",
"node-pty": "^0.9.0",
"pem": "^1.14.2",
"safe-compare": "^1.1.4",
"semver": "^7.1.3",
"tar": "^6.0.1",
"tar-fs": "^2.0.0",
"tar-stream": "^2.1.0"
"ws": "^7.2.0"
}
}

View File

@@ -1,58 +0,0 @@
// This builds the package and product JSON files for the final build.
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const rootPath = path.resolve(__dirname, "..");
const sourcePath = process.argv[2];
const buildPath = process.argv[3];
const vscodeVersion = process.argv[4];
const codeServerVersion = process.argv[5];
const util = require(path.join(sourcePath, "build/lib/util"));
function computeChecksum(filename) {
return crypto.createHash("md5").update(fs.readFileSync(filename))
.digest("base64").replace(/=+$/, "");
}
const computeChecksums = (filenames) => {
const result = {};
filenames.forEach(function (filename) {
result[filename] = computeChecksum(path.join(buildPath, "out", filename));
});
return result;
};
const mergeAndWrite = (name, json = {}) => {
const aJson = JSON.parse(fs.readFileSync(path.join(sourcePath, `${name}.json`)));
const bJson = JSON.parse(fs.readFileSync(path.join(rootPath, "scripts", `${name}.json`)));
delete aJson.scripts;
delete aJson.dependencies;
delete aJson.devDependencies;
delete aJson.optionalDependencies;
fs.writeFileSync(path.join(buildPath, `${name}.json`), JSON.stringify({
...aJson,
...bJson,
...json,
}, null, 2));
};
const writeProduct = () => {
const checksums = computeChecksums([
"vs/workbench/workbench.web.api.js",
"vs/workbench/workbench.web.api.css",
"vs/code/browser/workbench/workbench.html",
"vs/code/browser/workbench/workbench.js",
"vs/server/src/cli.js",
"vs/server/src/uriTransformer.js",
"vs/server/src/login/index.html"
]);
const date = new Date().toISOString();
const commit = util.getVersion(rootPath);
mergeAndWrite("product", { commit, date, checksums });
mergeAndWrite("package", { codeServerVersion: `${codeServerVersion}-vsc${vscodeVersion}` });
};
writeProduct();

View File

@@ -1,84 +0,0 @@
#!/bin/bash
set -euo pipefail
# Build using a Docker container.
function docker-build() {
local target="${TARGET:-}"
local image="codercom/nbin-${target}"
local token="${GITHUB_TOKEN:-}"
local minify="${MINIFY:-}"
if [[ "${target}" == "linux" ]] ; then
image="codercom/nbin-centos"
fi
local containerId
containerId=$(docker create --network=host --rm -it -v "$(pwd)"/.cache:/src/.cache "${image}")
docker start "${containerId}"
docker exec "${containerId}" mkdir -p /src
# TODO: temporary as long as we are rebuilding modules.
if [[ "${image}" == "codercom/nbin-alpine" ]] ; then
docker exec "${containerId}" apk add libxkbfile-dev libsecret-dev
else
# TODO: at some point git existed but it seems to have disappeared.
docker exec "${containerId}" yum install -y libxkbfile-devel libsecret-devel git
fi
function docker-exec() {
local command="${1}" ; shift
local args="'${vscodeVersion}' '${codeServerVersion}'"
docker exec "${containerId}" \
bash -c "cd /src && CI=true GITHUB_TOKEN=${token} MINIFY=${minify} yarn ${command} ${args}"
}
docker cp ./. "${containerId}":/src
docker-exec build
if [[ -n "${package}" ]] ; then
docker-exec binary
docker-exec package
mkdir -p release
docker cp "${containerId}":/src/release/. ./release/
fi
docker stop "${containerId}"
}
# Build locally.
function local-build() {
function local-exec() {
local command="${1}" ; shift
CI=true yarn "${command}" "${vscodeVersion}" "${codeServerVersion}"
}
local-exec build
if [[ -n "${package}" ]] ; then
local-exec binary
local-exec package
fi
}
# Build code-server in the CI.
function main() {
cd "$(dirname "${0}")/.."
local codeServerVersion="${VERSION:-}"
local vscodeVersion="${VSCODE_VERSION:-}"
local ostype="${OSTYPE:-}"
local package="${PACKAGE:-}"
if [[ -z "${codeServerVersion}" ]] ; then
>&2 echo "Must set VERSION environment variable"; exit 1
fi
if [[ -z "${vscodeVersion}" ]] ; then
>&2 echo "Must set VSCODE_VERSION environment variable"; exit 1
fi
if [[ "${ostype}" == "darwin"* ]]; then
local-build
else
docker-build
fi
}
main "$@"

View File

@@ -1,16 +0,0 @@
// This file is prepended to loader/entry code (like our main.js or VS Code's
// bootstrap-fork.js). {{ROOT_PATH}} is replaced during the build process.
if (!global.NBIN_LOADED) {
try {
const nbin = require("nbin");
nbin.shimNativeFs("{{ROOT_PATH}}");
global.NBIN_LOADED = true;
const path = require("path");
const rg = require("vscode-ripgrep");
rg.binaryRgPath = rg.rgPath;
rg.rgPath = path.join(
require("os").tmpdir(),
`code-server/${path.basename(rg.binaryRgPath)}`
);
} catch (error) { /* Not in the binary. */ }
}

View File

@@ -1,23 +0,0 @@
const { Binary } = require("@coder/nbin");
const fs = require("fs");
const path = require("path");
const source = process.argv[2];
const target = process.argv[3];
const binaryName = process.argv[4];
const bin = new Binary({
mainFile: path.join(source, "out/vs/server/main.js"),
target: target,
});
bin.writeFiles(path.join(source, "**"));
bin.build().then((binaryData) => {
const outputPath = path.join(source, binaryName);
fs.writeFileSync(outputPath, binaryData);
fs.chmodSync(outputPath, "755");
}).catch((ex) => {
console.error(ex);
process.exit(1);
});

View File

@@ -1,5 +0,0 @@
{
"name": "code-server",
"main": "out/vs/server/main",
"desktopName": "code-server-url-handler.desktop"
}

View File

@@ -1,19 +0,0 @@
{
"nameShort": "code-server",
"nameLong": "code-server",
"applicationName": "code-server",
"dataFolderName": ".code-server",
"win32MutexName": "codeserver",
"win32DirName": "Code Server",
"win32NameVersion": "Code Server",
"win32RegValueName": "CodeServer",
"win32AppId": "",
"win32x64AppId": "",
"win32UserAppId": "",
"win32x64UserAppId": "",
"win32AppUserModelId": "CodeServer",
"win32ShellNameShort": "C&ode Server",
"darwinBundleIdentifier": "com.code.server",
"linuxIconName": "com.code.server",
"urlProtocol": "code-server"
}

View File

@@ -1,276 +0,0 @@
#!/bin/bash
set -euox pipefail
function log() {
local message="${1}" ; shift
local level="${1:-info}"
if [[ "${level}" == "error" ]] ; then
>&2 echo "${message}"
else
echo "${message}"
fi
}
# Copy code-server into VS Code along with its dependencies.
function copy-server() {
local serverPath="${sourcePath}/src/vs/server"
rm -rf "${serverPath}"
mkdir -p "${serverPath}"
cp -r "${rootPath}/src" "${serverPath}"
cp -r "${rootPath}/typings" "${serverPath}"
cp "${rootPath}/main.js" "${serverPath}"
cp "${rootPath}/package.json" "${serverPath}"
cp "${rootPath}/yarn.lock" "${serverPath}"
if [[ -d "${rootPath}/node_modules" ]] ; then
cp -r "${rootPath}/node_modules" "${serverPath}"
else
# Ignore scripts to avoid also installing VS Code dependencies which has
# already been done.
cd "${serverPath}" && yarn --ignore-scripts
rm -r node_modules/@types/node # I keep getting type conflicts
fi
# TODO: Duplicate identifier issue. There must be a better way to fix this.
if [[ "${target}" == "darwin" ]] ; then
rm "${serverPath}/node_modules/fsevents/node_modules/safe-buffer/index.d.ts"
fi
}
# Prepend the nbin shim which enables finding files within the binary.
function prepend-loader() {
local filePath="${buildPath}/${1}" ; shift
cat "${rootPath}/scripts/nbin-shim.js" "${filePath}" > "${filePath}.temp"
mv "${filePath}.temp" "${filePath}"
# Using : as the delimiter so the escaping here is easier to read.
# ${parameter/pattern/string}, so the pattern is /: (if the pattern starts
# with / it matches all instances) and the string is \\: (results in \:).
if [[ "${target}" == "darwin" ]] ; then
sed -i "" -e "s:{{ROOT_PATH}}:${buildPath//:/\\:}:g" "${filePath}"
else
sed -i "s:{{ROOT_PATH}}:${buildPath//:/\\:}:g" "${filePath}"
fi
}
# Copy code-server into VS Code then build it.
function build-code-server() {
copy-server
cd "${sourcePath}" && yarn gulp compile-build --max-old-space-size=32384
local min=""
if [[ -n "${minify}" ]] ; then
min="-min"
yarn gulp minify-vscode --max-old-space-size=32384
else
yarn gulp optimize-vscode --max-old-space-size=32384
fi
rm -rf "${buildPath}"
mkdir -p "${buildPath}"
# Rebuild to make sure native modules work on the target system.
cp "${sourcePath}/remote/"{package.json,yarn.lock,.yarnrc} "${buildPath}"
cd "${buildPath}" && yarn --production --force --build-from-source
rm "${buildPath}/"{package.json,yarn.lock,.yarnrc}
cp -r "${sourcePath}/.build/extensions" "${buildPath}"
cp -r "${sourcePath}/out-vscode${min}" "${buildPath}/out"
node "${rootPath}/scripts/build-json.js" "${sourcePath}" "${buildPath}" "${vscodeVersion}" "${codeServerVersion}"
# Only keep production dependencies for the server.
cp "${rootPath}/"{package.json,yarn.lock} "${buildPath}/out/vs/server"
cd "${buildPath}/out/vs/server" && yarn --production --ignore-scripts
rm "${buildPath}/out/vs/server/"{package.json,yarn.lock}
# onigasm 2.2.2 has a bug that makes it broken for PHP files so use 2.2.1.
# https://github.com/NeekSandhu/onigasm/issues/17
local onigasmPath="${buildPath}/node_modules/onigasm-umd"
rm -rf "${onigasmPath}"
git clone "https://github.com/alexandrudima/onigasm-umd" "${onigasmPath}"
cd "${onigasmPath}" && yarn && yarn add --dev onigasm@2.2.1 && yarn package
mkdir "${onigasmPath}-temp"
mv "${onigasmPath}/"{release,LICENSE} "${onigasmPath}-temp"
rm -rf "${onigasmPath}"
mv "${onigasmPath}-temp" "${onigasmPath}"
prepend-loader "out/vs/server/main.js"
prepend-loader "out/bootstrap-fork.js"
prepend-loader "extensions/node_modules/typescript/lib/tsserver.js"
log "Final build: ${buildPath}"
}
# Download and extract a tar from a URL with either curl or wget depending on
# which is available.
function download-tar() {
local url="${1}" ; shift
if command -v wget &> /dev/null ; then
wget "${url}" --quiet -O - | tar -C "${stagingPath}" -xz
else
curl "${url}" --silent --fail | tar -C "${stagingPath}" -xz
fi
}
# Download a pre-built package. If it doesn't exist and we are in the CI, exit.
# Otherwise the return will be whether it existed or not. The pre-built package
# is provided to reduce CI build time.
function download-pre-built() {
local archiveName="${1}" ; shift
local url="https://codesrv-ci.cdr.sh/${archiveName}"
if ! download-tar "${url}" ; then
if [[ -n "${ci}" ]] ; then
log "${url} does not exist" "error"
exit 1
fi
return 1
fi
return 0
}
# Fully build code-server.
function build-task() {
mkdir -p "${stagingPath}"
if [[ ! -d "${sourcePath}" ]] ; then
if ! download-pre-built "vscode-${vscodeVersion}.tar.gz" ; then
git clone https://github.com/microsoft/vscode --quiet \
--branch "${vscodeVersion}" --single-branch --depth=1 \
"${sourcePath}"
fi
fi
cd "${sourcePath}"
git reset --hard && git clean -fd
git apply "${rootPath}/scripts/vscode.patch"
if [[ ! -d "${sourcePath}/node_modules" ]] ; then
if [[ -n "${ci}" ]] ; then
log "Pre-built VS Code ${vscodeVersion} has no node_modules" "error"
exit 1
fi
yarn
fi
if [[ ! -d "${sourcePath}/.build/extensions" ]] ; then
if [[ -n "${ci}" ]] ; then
log "Pre-built VS Code ${vscodeVersion} has no built extensions" "error"
exit 1
fi
yarn gulp compile-extensions-build --max-old-space-size=32384
fi
build-code-server
}
# Package the binary into a tar or zip for release.
function package-task() {
local archivePath="${releasePath}/${binaryName}"
rm -rf "${archivePath}"
mkdir -p "${archivePath}"
cp "${buildPath}/${binaryName}" "${archivePath}/code-server"
cp "${rootPath}/README.md" "${archivePath}"
cp "${sourcePath}/LICENSE.txt" "${archivePath}"
cp "${sourcePath}/ThirdPartyNotices.txt" "${archivePath}"
cd "${releasePath}"
if [[ "${target}" == "darwin" ]] ; then
zip -r "${binaryName}.zip" "${binaryName}"
log "Archive: ${archivePath}.zip"
else
tar -czf "${binaryName}.tar.gz" "${binaryName}"
log "Archive: ${archivePath}.tar.gz"
fi
}
# Bundle built code into a binary.
function binary-task() {
cd "${rootPath}"
node "${rootPath}/scripts/nbin.js" "${buildPath}" "${target}" "${binaryName}"
log "Binary: ${buildPath}/${binaryName}"
}
# Check if it looks like we are inside VS Code.
function in-vscode () {
local dir="${1}" ; shift
local maybeVsCode
local dirName
maybeVsCode="$(cd "${dir}/../../.." ; pwd -P)"
dirName="$(basename "${maybeVsCode}")"
if [[ "${dirName}" != "vscode" ]] ; then
return 1
fi
if [[ ! -f "${maybeVsCode}/package.json" ]] ; then
return 1
fi
if ! grep '"name": "code-oss-dev"' "${maybeVsCode}/package.json" -q ; then
return 1
fi
return 0
}
function main() {
local rootPath
rootPath="$(cd "$(dirname "${0}")/.." ; pwd -P)"
local task="${1}" ; shift
if [[ "${task}" == "ensure-in-vscode" ]] ; then
if ! in-vscode "${rootPath}"; then
log "Not in VS Code" "error"
exit 1
fi
exit 0
fi
# This lets you build in a separate directory since building within this
# directory while developing makes it hard to keep developing since compiling
# will compile everything in the build directory as well.
local outPath="${OUT:-${rootPath}}"
local releasePath="${outPath}/release"
local stagingPath="${outPath}/build"
# If we're inside a VS Code directory, assume we want to develop. In that case
# we should set an OUT directory and not build in this directory.
if in-vscode "${outPath}" ; then
log "Set the OUT environment variable to something outside of VS Code" "error"
exit 1
fi
local vscodeVersion="${1}" ; shift
local sourceName="vscode-${vscodeVersion}-source"
local sourcePath="${stagingPath}/${sourceName}"
if [[ "${task}" == "package-prebuilt" ]] ; then
local archiveName="vscode-${vscodeVersion}.tar.gz"
cd "${sourcePath}"
git reset --hard && git clean -xfd -e '.build/extensions' -e 'node_modules'
cd "${stagingPath}"
tar -czf "${archiveName}" "${sourceName}"
mkdir -p "${releasePath}" && mv -f "${archiveName}" "${releasePath}"
exit 0
fi
local codeServerVersion="${1}" ; shift
local ci="${CI:-}"
local minify="${MINIFY:-}"
local arch
arch=$(uname -m)
local target="linux"
local ostype="${OSTYPE:-}"
if [[ "${ostype}" == "darwin"* ]] ; then
target="darwin"
else
# On Alpine there seems no way to get the version except to use an invalid
# command which will output the version to stderr and exit with 1.
local output
output=$(ldd --version 2>&1 || :)
if [[ "${output}" == "musl"* ]] ; then
target="alpine"
fi
fi
local binaryName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}"
local buildPath="${stagingPath}/${binaryName}-built"
"${task}-task" "$@"
}
main "$@"

File diff suppressed because it is too large Load Diff

View File

@@ -1,369 +0,0 @@
import * as vscode from "vscode";
import { CoderApi, VSCodeApi } from "../typings/api";
import { createCSSRule } from "vs/base/browser/dom";
import { Emitter, Event } from "vs/base/common/event";
import { IDisposable } from "vs/base/common/lifecycle";
import { URI } from "vs/base/common/uri";
import { generateUuid } from "vs/base/common/uuid";
import { localize } from "vs/nls";
import { SyncActionDescriptor } from "vs/platform/actions/common/actions";
import { CommandsRegistry, ICommandService } from "vs/platform/commands/common/commands";
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
import { IContextMenuService } from "vs/platform/contextview/browser/contextView";
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions } from "vs/platform/files/common/files";
import { IInstantiationService, ServiceIdentifier } from "vs/platform/instantiation/common/instantiation";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { INotificationService } from "vs/platform/notification/common/notification";
import { Registry } from "vs/platform/registry/common/platform";
import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from "vs/platform/statusbar/common/statusbar";
import { IStorageService } from "vs/platform/storage/common/storage";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { IThemeService } from "vs/platform/theme/common/themeService";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import * as extHostTypes from "vs/workbench/api/common/extHostTypes";
import { CustomTreeView, CustomTreeViewPanel } from "vs/workbench/browser/parts/views/customView";
import { ViewContainerViewlet } from "vs/workbench/browser/parts/views/viewsViewlet";
import { Extensions as ViewletExtensions, ShowViewletAction, ViewletDescriptor, ViewletRegistry } from "vs/workbench/browser/viewlet";
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from "vs/workbench/common/actions";
import { Extensions as ViewsExtensions, ITreeItem, ITreeViewDataProvider, ITreeViewDescriptor, IViewContainersRegistry, IViewsRegistry, TreeItemCollapsibleState } from "vs/workbench/common/views";
import { IEditorGroupsService } from "vs/workbench/services/editor/common/editorGroupsService";
import { IEditorService } from "vs/workbench/services/editor/common/editorService";
import { IExtensionService } from "vs/workbench/services/extensions/common/extensions";
import { IWorkbenchLayoutService } from "vs/workbench/services/layout/browser/layoutService";
import { IViewletService } from "vs/workbench/services/viewlet/browser/viewlet";
/**
* Client-side implementation of VS Code's API.
* TODO: Views aren't quite working.
* TODO: Implement menu items for views (for item actions).
* TODO: File system provider doesn't work.
*/
export const vscodeApi = (serviceCollection: ServiceCollection): VSCodeApi => {
const getService = <T>(id: ServiceIdentifier<T>): T => serviceCollection.get<T>(id) as T;
const commandService = getService(ICommandService);
const notificationService = getService(INotificationService);
const fileService = getService(IFileService);
const viewsRegistry = Registry.as<IViewsRegistry>(ViewsExtensions.ViewsRegistry);
const statusbarService = getService(IStatusbarService);
// It would be nice to just export what VS Code creates but it looks to me
// that it assumes it's running in the extension host and wouldn't work here.
// It is probably possible to create an extension host that runs in the
// browser's main thread, but I'm not sure how much jank that would require.
// We could have a web worker host but we want DOM access.
return {
EventEmitter: <any>Emitter, // It can take T so T | undefined should work.
FileSystemError: extHostTypes.FileSystemError,
FileType,
StatusBarAlignment: extHostTypes.StatusBarAlignment,
ThemeColor: extHostTypes.ThemeColor,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
Uri: URI,
commands: {
executeCommand: <T = any>(commandId: string, ...args: any[]): Promise<T | undefined> => {
return commandService.executeCommand(commandId, ...args);
},
registerCommand: (id: string, command: (...args: any[]) => any): IDisposable => {
return CommandsRegistry.registerCommand(id, command);
},
},
window: {
createStatusBarItem(alignmentOrOptions?: extHostTypes.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number): StatusBarEntry {
return new StatusBarEntry(statusbarService, alignmentOrOptions, priority);
},
registerTreeDataProvider: <T>(id: string, dataProvider: vscode.TreeDataProvider<T>): IDisposable => {
const tree = new TreeViewDataProvider(dataProvider);
const view = viewsRegistry.getView(id);
(view as ITreeViewDescriptor).treeView.dataProvider = tree;
return {
dispose: () => tree.dispose(),
};
},
showErrorMessage: async (message: string): Promise<string | undefined> => {
notificationService.error(message);
return undefined;
},
},
workspace: {
registerFileSystemProvider: (scheme: string, provider: vscode.FileSystemProvider): IDisposable => {
return fileService.registerProvider(scheme, new FileSystemProvider(provider));
},
},
};
};
/**
* Coder API. This should only provide functionality that can't be made
* available through the VS Code API.
*/
export const coderApi = (serviceCollection: ServiceCollection): CoderApi => {
const getService = <T>(id: ServiceIdentifier<T>): T => serviceCollection.get<T>(id) as T;
return {
registerView: (viewId, viewName, containerId, containerName, icon): void => {
const cssClass = `extensionViewlet-${containerId}`;
const id = `workbench.view.extension.${containerId}`;
class CustomViewlet extends ViewContainerViewlet {
public constructor(
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IEditorService _editorService: IEditorService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
) {
super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(
new ViewletDescriptor(CustomViewlet as any, id, containerName, cssClass, undefined, URI.parse(icon)),
);
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction as any, id, localize("showViewlet", "Show {0}", containerName)),
"View: Show {0}",
localize("view", "View"),
);
// Generate CSS to show the icon in the activity bar.
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`);
const container = Registry.as<IViewContainersRegistry>(ViewsExtensions.ViewContainersRegistry).registerViewContainer(containerId);
Registry.as<IViewsRegistry>(ViewsExtensions.ViewsRegistry).registerViews([{
id: viewId,
name: viewName,
ctorDescriptor: { ctor: CustomTreeViewPanel },
treeView: getService(IInstantiationService).createInstance(CustomTreeView as any, viewId, container),
}] as ITreeViewDescriptor[], container);
},
};
};
class OpenCustomViewletAction extends ShowViewletAction {
public constructor(
id: string, label: string,
@IViewletService viewletService: IViewletService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
) {
super(id, label, id, viewletService, editorGroupService, layoutService);
}
}
class FileSystemProvider implements IFileSystemProvider {
private readonly _onDidChange = new Emitter<IFileChange[]>();
public readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChange.event;
public readonly capabilities: FileSystemProviderCapabilities;
public readonly onDidChangeCapabilities: Event<void> = Event.None;
public constructor(private readonly provider: vscode.FileSystemProvider) {
this.capabilities = FileSystemProviderCapabilities.Readonly;
}
public watch(resource: URI, opts: IWatchOptions): IDisposable {
return this.provider.watch(resource, opts);
}
public async stat(resource: URI): Promise<IStat> {
return this.provider.stat(resource);
}
public async readFile(resource: URI): Promise<Uint8Array> {
return this.provider.readFile(resource);
}
public async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
return this.provider.writeFile(resource, content, opts);
}
public async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
return this.provider.delete(resource, opts);
}
public mkdir(_resource: URI): Promise<void> {
throw new Error("not implemented");
}
public async readdir(resource: URI): Promise<[string, FileType][]> {
return this.provider.readDirectory(resource);
}
public async rename(resource: URI, target: URI, opts: FileOverwriteOptions): Promise<void> {
return this.provider.rename(resource, target, opts);
}
public async copy(resource: URI, target: URI, opts: FileOverwriteOptions): Promise<void> {
return this.provider.copy!(resource, target, opts);
}
public open(_resource: URI, _opts: FileOpenOptions): Promise<number> {
throw new Error("not implemented");
}
public close(_fd: number): Promise<void> {
throw new Error("not implemented");
}
public read(_fd: number, _pos: number, _data: Uint8Array, _offset: number, _length: number): Promise<number> {
throw new Error("not implemented");
}
public write(_fd: number, _pos: number, _data: Uint8Array, _offset: number, _length: number): Promise<number> {
throw new Error("not implemented");
}
}
class TreeViewDataProvider<T> implements ITreeViewDataProvider {
private readonly root = Symbol("root");
private readonly values = new Map<string, T>();
private readonly children = new Map<T | Symbol, ITreeItem[]>();
public constructor(private readonly provider: vscode.TreeDataProvider<T>) {}
public async getChildren(item?: ITreeItem): Promise<ITreeItem[]> {
const value = item && this.itemToValue(item);
const children = await Promise.all(
(await this.provider.getChildren(value) || [])
.map(async (childValue) => {
const treeItem = await this.provider.getTreeItem(childValue);
const handle = this.createHandle(treeItem);
this.values.set(handle, childValue);
return {
handle,
collapsibleState: TreeItemCollapsibleState.Collapsed,
};
})
);
this.clear(value || this.root, item);
this.children.set(value || this.root, children);
return children;
}
public dispose(): void {
throw new Error("not implemented");
}
private itemToValue(item: ITreeItem): T {
if (!this.values.has(item.handle)) {
throw new Error(`No element found with handle ${item.handle}`);
}
return this.values.get(item.handle)!;
}
private clear(value: T | Symbol, item?: ITreeItem): void {
if (this.children.has(value)) {
this.children.get(value)!.map((c) => this.clear(this.itemToValue(c), c));
this.children.delete(value);
}
if (item) {
this.values.delete(item.handle);
}
}
private createHandle(item: vscode.TreeItem): string {
return item.id
? `coder-tree-item-id/${item.id}`
: `coder-tree-item-uuid/${generateUuid()}`;
}
}
interface IStatusBarEntry extends IStatusbarEntry {
alignment: StatusbarAlignment;
priority?: number;
}
class StatusBarEntry implements vscode.StatusBarItem {
private static ID = 0;
private _id: number;
private entry: IStatusBarEntry;
private visible: boolean;
private disposed: boolean;
private statusId: string;
private statusName: string;
private accessor?: IStatusbarEntryAccessor;
private timeout: any;
constructor(private readonly statusbarService: IStatusbarService, alignmentOrOptions?: extHostTypes.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number) {
this._id = StatusBarEntry.ID--;
if (alignmentOrOptions && typeof alignmentOrOptions !== "number") {
this.statusId = alignmentOrOptions.id;
this.statusName = alignmentOrOptions.name;
this.entry = {
alignment: alignmentOrOptions.alignment === extHostTypes.StatusBarAlignment.Right
? StatusbarAlignment.RIGHT : StatusbarAlignment.LEFT,
priority,
text: "",
};
} else {
this.statusId = "web-api";
this.statusName = "Web API";
this.entry = {
alignment: alignmentOrOptions === extHostTypes.StatusBarAlignment.Right
? StatusbarAlignment.RIGHT : StatusbarAlignment.LEFT,
priority,
text: "",
};
}
}
public get alignment(): extHostTypes.StatusBarAlignment {
return this.entry.alignment === StatusbarAlignment.RIGHT
? extHostTypes.StatusBarAlignment.Right : extHostTypes.StatusBarAlignment.Left;
}
public get id(): number { return this._id; }
public get priority(): number | undefined { return this.entry.priority; }
public get text(): string { return this.entry.text; }
public get tooltip(): string | undefined { return this.entry.tooltip; }
public get color(): string | extHostTypes.ThemeColor | undefined { return this.entry.color; }
public get command(): string | undefined { return this.entry.command; }
public set text(text: string) { this.update({ text }); }
public set tooltip(tooltip: string | undefined) { this.update({ tooltip }); }
public set color(color: string | extHostTypes.ThemeColor | undefined) { this.update({ color }); }
public set command(command: string | undefined) { this.update({ command }); }
public show(): void {
this.visible = true;
this.update();
}
public hide(): void {
clearTimeout(this.timeout);
this.visible = false;
if (this.accessor) {
this.accessor.dispose();
this.accessor = undefined;
}
}
private update(values?: Partial<IStatusBarEntry>): void {
this.entry = { ...this.entry, ...values };
if (this.disposed || !this.visible) {
return;
}
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
if (!this.accessor) {
this.accessor = this.statusbarService.addEntry(this.entry, this.statusId, this.statusName, this.entry.alignment, this.priority);
} else {
this.accessor.update(this.entry);
}
}, 0);
}
public dispose(): void {
this.hide();
this.disposed = true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,40 @@
{
"name": "code-server",
"short_name": "code-server",
"start_url": "{{BASE}}",
"display": "fullscreen",
"background-color": "#fff",
"description": "Run editors on a remote server.",
"icons": [
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-128.png",
"type": "image/png",
"sizes": "128x128"
},
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-256.png",
"type": "image/png",
"sizes": "256x256"
},
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png",
"type": "image/png",
"sizes": "384x384"
},
{
"src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<meta
http-equiv="Content-Security-Policy"
content="style-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/>
<title>code-server</title>
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/pages/app.js"></script>
</body>
</html>

37
src/browser/pages/app.ts Normal file
View File

@@ -0,0 +1,37 @@
import { getOptions, normalize } from "../../common/util"
import { ApiEndpoint } from "../../common/http"
import "./error.css"
import "./global.css"
import "./home.css"
import "./login.css"
import "./update.css"
const options = getOptions()
const isInput = (el: Element): el is HTMLInputElement => {
return !!(el as HTMLInputElement).name
}
document.querySelectorAll("form").forEach((form) => {
if (!form.classList.contains("-x11")) {
return
}
form.addEventListener("submit", (event) => {
event.preventDefault()
const values: { [key: string]: string } = {}
Array.from(form.elements).forEach((element) => {
if (isInput(element)) {
values[element.name] = element.value
}
})
fetch(normalize(`${options.base}/api/${ApiEndpoint.process}`), {
method: "POST",
body: JSON.stringify(values),
})
})
})
// TEMP: Until we can get the real ready event.
const event = new CustomEvent("ide-ready")
window.dispatchEvent(event)

View File

@@ -0,0 +1,32 @@
.error-display {
box-sizing: border-box;
padding: 20px;
text-align: center;
}
.error-display > .header {
font-size: 6rem;
margin: 0;
}
.error-display > .body {
color: #444;
font-size: 1.2rem;
}
.error-display > .links {
margin-top: 16px;
}
.error-display > .links > .link {
color: rgb(87, 114, 245);
text-decoration: none;
}
.error-display > .links > .link:hover {
text-decoration: underline;
}
.error-display .success {
color: green;
}

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<meta
http-equiv="Content-Security-Policy"
content="style-src 'self'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/>
<title>{{ERROR_TITLE}} - code-server</title>
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>
<div class="center-container">
<div class="error-display">
<h2 class="header">{{ERROR_HEADER}}</h2>
<div class="body">
{{ERROR_BODY}}
</div>
<div class="links">
<a class="link" href="{{BASE}}{{TO}}">go home</a>
</div>
</div>
</div>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
</body>
</html>

View File

@@ -0,0 +1,85 @@
html,
body,
#root {
height: 100%;
width: 100%;
}
body {
background: rgb(244, 247, 252);
color: #111;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol";
overflow: hidden;
}
input,
button {
font-family: inherit;
font-size: 1rem;
line-height: 1rem;
}
.-button {
background-color: rgb(87, 114, 245);
border-radius: 5px;
border: none;
box-sizing: border-box;
color: white;
cursor: pointer;
padding: 18px 20px;
text-decoration: none;
}
.center-container {
align-items: center;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100%;
padding: 20px;
width: 100%;
}
.card-box {
background-color: rgb(250, 253, 258);
border-radius: 5px;
box-shadow: rgba(60, 66, 87, 0.117647) 0px 7px 14px 0px, rgba(0, 0, 0, 0.117647) 0px 3px 6px 0px;
max-width: 650px;
width: 100%;
}
.card-box > .header {
border-bottom: 1px solid #ddd;
color: #444;
padding: 30px;
}
.card-box > .header > .main {
margin: 0;
font-size: 1.5rem;
}
.card-box > .header > .sub {
color: #555;
margin-top: 10px;
}
.card-box > .content {
padding: 40px;
}
.card-box > .content > .none {
margin: 2px 0;
}
.card-box + .card-box {
margin-top: 26px;
}
canvas {
top: 0;
left: 0;
}

View File

@@ -0,0 +1,51 @@
.block-row {
display: flex;
}
.block-row > .item {
flex: 1;
margin: 2px 0;
}
.block-row > button.item {
background: none;
border: none;
cursor: pointer;
text-align: left;
}
.block-row > .item > .sub {
font-size: 0.95em;
}
.block-row .-link {
color: rgb(87, 114, 245);
display: block;
text-decoration: none;
}
.block-row .-link:hover {
text-decoration: underline;
}
.block-row > .item > .icon {
height: 1rem;
margin-right: 5px;
vertical-align: top;
width: 1rem;
}
.block-row > .item > .icon.-missing {
background-color: rgba(87, 114, 245, 0.2);
display: inline-block;
text-align: center;
}
.kill-form {
display: inline-block;
}
.kill-form > .kill {
border-radius: 3px;
padding: 2px 5px;
}

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<meta
http-equiv="Content-Security-Policy"
content="style-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/>
<title>code-server</title>
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>
<div class="center-container">
<div class="card-box">
<div class="header">
<h2 class="main">Editors</h2>
<div class="sub">Choose an editor to launch below.</div>
</div>
<div class="content">
{{APP_LIST:EDITORS}}
</div>
</div>
<div class="card-box">
<div class="header">
<h2 class="main">Other</h2>
<div class="sub">Choose an application to launch below.</div>
</div>
<div class="content">
{{APP_LIST:OTHER}}
</div>
</div>
<div class="card-box">
<div class="header">
<h2 class="main">Version</h2>
<div class="sub">Version information and updates.</div>
</div>
<div class="content">
{{UPDATE:NAME}}
</div>
</div>
</div>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/pages/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,39 @@
body {
overflow: auto;
}
.login-form {
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
}
.login-form > .field {
display: flex;
flex-direction: row;
width: 100%;
}
.login-form > .error {
color: red;
margin-top: 16px;
}
.login-form > .field > .password {
background-color: rgb(244, 247, 252);
border-radius: 5px;
border: 1px solid #ddd;
box-sizing: border-box;
color: black;
flex: 1;
padding: 16px;
}
.login-form > .user {
display: none;
}
.login-form > .field > .submit {
margin-left: 20px;
}

View File

@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<meta
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>code-server login</title>
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>
<div class="center-container">
<div class="card-box">
<div class="header">
<h1 class="main">Welcome to code-server</h1>
<div class="sub">Please log in below. Check code-server's logs for the generated password.</div>
</div>
<div class="content">
<form class="login-form" method="post">
<input class="user" type="text" autocomplete="username" />
<input id="base" type="hidden" name="base" value="/" />
<div class="field">
<input
required
autofocus
class="password"
type="password"
placeholder="PASSWORD"
name="password"
autocomplete="current-password"
/>
<input class="submit -button" value="SUBMIT" type="submit" />
</div>
{{ERROR}}
</form>
</div>
</div>
</div>
</body>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
<script>
const parts = window.location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = "{{BASE}}"
const url = new URL(window.location.origin + "/" + parts.join("/"))
document.getElementById("base").value = url.pathname
</script>
</html>

View File

@@ -0,0 +1,26 @@
.update-form {
text-align: center;
}
.update-form > .cancel {
background-color: red;
}
.update-form > .error {
color: red;
margin-top: 16px;
}
.update-form > .links {
margin-top: 20px;
}
.update-form > .links > .link {
color: rgb(87, 114, 245);
text-align: center;
text-decoration: none;
}
.update-form > .links > .link:hover {
text-decoration: underline;
}

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<meta
http-equiv="Content-Security-Policy"
content="style-src 'self'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/>
<title>code-server</title>
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>
<div class="center-container">
<div class="card-box">
<div class="header">
<h1 class="main">Update</h1>
<div class="sub">Update code-server.</div>
</div>
<div class="content">
<form class="update-form" action="{{BASE}}/update/apply">
{{UPDATE_STATUS}} {{ERROR}}
<div class="links">
<a class="link" href="{{BASE}}{{TO}}">go home</a>
</div>
</form>
</div>
</div>
</div>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
</body>
</html>

View File

@@ -0,0 +1,110 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="Content-Security-Policy"
content="font-src 'self' data:; connect-src ws: wss: 'self' https:; default-src ws: wss: 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; manifest-src 'self'; img-src 'self' data: https:;"
/>
<!-- Disable pinch zooming -->
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}" />
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}" />
<meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}" />
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}" />
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link
rel="manifest"
href="{{BASE}}/static/{{COMMIT}}/src/browser/media/manifest.json"
crossorigin="use-credentials"
/>
<!-- PROD_ONLY
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="{{BASE}}/static/{{COMMIT}}/lib/vscode/out/vs/workbench/workbench.web.api.css">
END_PROD_ONLY -->
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Prefetch to avoid waterfall -->
<!-- PROD_ONLY
<link rel="prefetch" href="{{BASE}}/static/{{COMMIT}}/lib/vscode/node_modules/semver-umd/lib/semver-umd.js">
END_PROD_ONLY -->
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body aria-label=""></body>
<!-- Startup (do not modify order of script tags!) -->
<script>
const parts = window.location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = "{{BASE}}"
const url = new URL(window.location.origin + "/" + parts.join("/"))
const staticBase = url.href.replace(/\/+$/, "") + "/static/{{COMMIT}}/lib/vscode"
let nlsConfig
try {
nlsConfig = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
if (nlsConfig._resolvedLanguagePackCoreLocation) {
const bundles = Object.create(null)
nlsConfig.loadBundle = (bundle, language, cb) => {
let result = bundles[bundle]
if (result) {
return cb(undefined, result)
}
// FIXME: Only works if path separators are /.
const path = nlsConfig._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
fetch(`${url.href}/resource/?path=${encodeURIComponent(path)}`)
.then((response) => response.json())
.then((json) => {
bundles[bundle] = json
cb(undefined, json)
})
.catch(cb)
}
}
} catch (error) {
/* Probably fine. */
}
self.require = {
baseUrl: `${staticBase}/out`,
paths: {
"vscode-textmate": `${staticBase}/node_modules/vscode-textmate/release/main`,
"onigasm-umd": `${staticBase}/node_modules/onigasm-umd/release/main`,
xterm: `${staticBase}/node_modules/xterm/lib/xterm.js`,
"xterm-addon-search": `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
"xterm-addon-unicode11": `${staticBase}/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
"xterm-addon-web-links": `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
"xterm-addon-webgl": `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
"semver-umd": `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
},
"vs/nls": nlsConfig,
}
</script>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/dist/register.js"></script>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/lib/vscode/out/vs/loader.js"></script>
<!-- PROD_ONLY
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/lib/vscode/out/vs/workbench/workbench.web.api.nls.js"></script>
<script data-cfasync="false" src="{{BASE}}/static/{{COMMIT}}/lib/vscode/out/vs/workbench/workbench.web.api.js"></script>
END_PROD_ONLY -->
<script>
require(["vs/code/browser/workbench/workbench"], function() {})
</script>
<script>
try {
document.body.style.background = JSON.parse(localStorage.getItem("colorThemeData")).colorMap["editor.background"]
} catch (error) {
// Oh well.
}
</script>
</html>

14
src/browser/register.ts Normal file
View File

@@ -0,0 +1,14 @@
import { getOptions, normalize } from "../common/util"
const options = getOptions()
if ("serviceWorker" in navigator) {
const path = normalize(`${options.base}/static/${options.commit}/dist/serviceWorker.js`)
navigator.serviceWorker
.register(path, {
scope: options.base || "/",
})
.then(function() {
console.log("[Service Worker] registered")
})
}

View File

@@ -0,0 +1,24 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
self.addEventListener("install", () => {
console.log("[Service Worker] install")
})
self.addEventListener("activate", (event: any) => {
event.waitUntil((self as any).clients.claim())
})
self.addEventListener("fetch", (event: any) => {
if (!navigator.onLine) {
event.respondWith(
new Promise((resolve) => {
resolve(
new Response("OFFLINE", {
status: 200,
statusText: "OK",
}),
)
}),
)
}
})

View File

@@ -1,276 +0,0 @@
import * as path from "path";
import { VSBuffer } from "vs/base/common/buffer";
import { Emitter, Event } from "vs/base/common/event";
import { IDisposable } from "vs/base/common/lifecycle";
import { OS } from "vs/base/common/platform";
import { URI, UriComponents } from "vs/base/common/uri";
import { transformOutgoingURIs } from "vs/base/common/uriIpc";
import { IServerChannel } from "vs/base/parts/ipc/common/ipc";
import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnostics";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { ExtensionIdentifier, IExtensionDescription } from "vs/platform/extensions/common/extensions";
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions } from "vs/platform/files/common/files";
import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider";
import { ILogService } from "vs/platform/log/common/log";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { getTranslations } from "vs/server/src/nls";
import { getUriTransformer } from "vs/server/src/util";
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
/**
* Extend the file provider to allow unwatching.
*/
class Watcher extends DiskFileSystemProvider {
public readonly watches = new Map<number, IDisposable>();
public dispose(): void {
this.watches.forEach((w) => w.dispose());
this.watches.clear();
super.dispose();
}
public _watch(req: number, resource: URI, opts: IWatchOptions): void {
this.watches.set(req, this.watch(resource, opts));
}
public unwatch(req: number): void {
this.watches.get(req)!.dispose();
this.watches.delete(req);
}
}
export class FileProviderChannel implements IServerChannel, IDisposable {
private readonly provider: DiskFileSystemProvider;
private readonly watchers = new Map<string, Watcher>();
public constructor(
private readonly environmentService: IEnvironmentService,
private readonly logService: ILogService,
) {
this.provider = new DiskFileSystemProvider(this.logService);
}
public listen(context: any, event: string, args?: any): Event<any> {
switch (event) {
// This is where the actual file changes are sent. The watch method just
// adds things that will fire here. That means we have to split up
// watchers based on the session otherwise sessions would get events for
// other sessions. There is also no point in having the watcher unless
// something is listening. I'm not sure there is a different way to
// dispose, anyway.
case "filechange":
const session = args[0];
const emitter = new Emitter({
onFirstListenerAdd: () => {
const provider = new Watcher(this.logService);
this.watchers.set(session, provider);
const transformer = getUriTransformer(context.remoteAuthority);
provider.onDidChangeFile((events) => {
emitter.fire(events.map((event) => ({
...event,
resource: transformer.transformOutgoing(event.resource),
})));
});
provider.onDidErrorOccur((event) => emitter.fire(event));
},
onLastListenerRemove: () => {
this.watchers.get(session)!.dispose();
this.watchers.delete(session);
},
});
return emitter.event;
}
throw new Error(`Invalid listen "${event}"`);
}
public call(_: unknown, command: string, args?: any): Promise<any> {
switch (command) {
case "stat": return this.stat(args[0]);
case "open": return this.open(args[0], args[1]);
case "close": return this.close(args[0]);
case "read": return this.read(args[0], args[1], args[2]);
case "write": return this.write(args[0], args[1], args[2], args[3], args[4]);
case "delete": return this.delete(args[0], args[1]);
case "mkdir": return this.mkdir(args[0]);
case "readdir": return this.readdir(args[0]);
case "rename": return this.rename(args[0], args[1], args[2]);
case "copy": return this.copy(args[0], args[1], args[2]);
case "watch": return this.watch(args[0], args[1], args[2], args[3]);
case "unwatch": return this.unwatch(args[0], args[1]);
}
throw new Error(`Invalid call "${command}"`);
}
public dispose(): void {
this.watchers.forEach((w) => w.dispose());
this.watchers.clear();
}
private async stat(resource: UriComponents): Promise<IStat> {
return this.provider.stat(this.transform(resource));
}
private async open(resource: UriComponents, opts: FileOpenOptions): Promise<number> {
return this.provider.open(this.transform(resource), opts);
}
private async close(fd: number): Promise<void> {
return this.provider.close(fd);
}
private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> {
const buffer = VSBuffer.alloc(length);
const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length);
return [buffer, bytesRead];
}
private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise<number> {
return this.provider.write(fd, pos, buffer.buffer, offset, length);
}
private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise<void> {
return this.provider.delete(this.transform(resource), opts);
}
private async mkdir(resource: UriComponents): Promise<void> {
return this.provider.mkdir(this.transform(resource));
}
private async readdir(resource: UriComponents): Promise<[string, FileType][]> {
return this.provider.readdir(this.transform(resource));
}
private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this.provider.rename(this.transform(resource), URI.from(target), opts);
}
private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this.provider.copy(this.transform(resource), URI.from(target), opts);
}
private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise<void> {
this.watchers.get(session)!._watch(req, this.transform(resource), opts);
}
private async unwatch(session: string, req: number): Promise<void> {
this.watchers.get(session)!.unwatch(req);
}
private transform(resource: UriComponents): URI {
// Used for walkthrough content.
if (resource.path.indexOf("/static") === 0) {
return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static/, ""));
// Used by the webview service worker to load resources.
} else if (resource.path === "/vscode-resource" && resource.query) {
try {
const query = JSON.parse(resource.query);
if (query.requestResourcePath) {
return URI.file(query.requestResourcePath);
}
} catch (error) { /* Carry on. */ }
}
return URI.from(resource);
}
}
export class ExtensionEnvironmentChannel implements IServerChannel {
public constructor(
private readonly environment: IEnvironmentService,
private readonly log: ILogService,
private readonly telemetry: ITelemetryService,
private readonly connectionToken: string,
) {}
public listen(_: unknown, event: string): Event<any> {
throw new Error(`Invalid listen "${event}"`);
}
public async call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case "getEnvironmentData":
return transformOutgoingURIs(
await this.getEnvironmentData(args.language),
getUriTransformer(context.remoteAuthority),
);
case "getDiagnosticInfo": return this.getDiagnosticInfo();
case "disableTelemetry": return this.disableTelemetry();
}
throw new Error(`Invalid call "${command}"`);
}
private async getEnvironmentData(locale: string): Promise<IRemoteAgentEnvironment> {
return {
pid: process.pid,
connectionToken: this.connectionToken,
appRoot: URI.file(this.environment.appRoot),
appSettingsHome: this.environment.appSettingsHome,
settingsPath: this.environment.machineSettingsHome,
logsPath: URI.file(this.environment.logsPath),
extensionsPath: URI.file(this.environment.extensionsPath!),
extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, "extension-host")),
globalStorageHome: URI.file(this.environment.globalStorageHome),
userHome: URI.file(this.environment.userHome),
extensions: await this.scanExtensions(locale),
os: OS,
};
}
private async scanExtensions(locale: string): Promise<IExtensionDescription[]> {
const translations = await getTranslations(locale, this.environment.userDataPath);
const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise<IExtensionDescription[][]> => {
return Promise.all(paths.map((path) => {
return ExtensionScanner.scanExtensions(new ExtensionScannerInput(
pkg.version,
product.commit,
locale,
!!process.env.VSCODE_DEV,
path,
isBuiltin,
isUnderDevelopment,
translations,
), this.log);
}));
};
const scanBuiltin = async (): Promise<IExtensionDescription[][]> => {
return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]);
};
const scanInstalled = async (): Promise<IExtensionDescription[][]> => {
return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]);
};
return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => {
const uniqueExtensions = new Map<string, IExtensionDescription>();
allExtensions.forEach((multipleExtensions) => {
multipleExtensions.forEach((extensions) => {
extensions.forEach((extension) => {
const id = ExtensionIdentifier.toKey(extension.identifier);
if (uniqueExtensions.has(id)) {
const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath;
const newPath = extension.extensionLocation.fsPath;
this.log.warn(`${oldPath} has been overridden ${newPath}`);
}
uniqueExtensions.set(id, extension);
});
});
});
return Array.from(uniqueExtensions.values());
});
}
private getDiagnosticInfo(): Promise<IDiagnosticInfo> {
throw new Error("not implemented");
}
private async disableTelemetry(): Promise<void> {
this.telemetry.setEnabled(false);
}
}

View File

@@ -1,262 +0,0 @@
import * as cp from "child_process";
import * as os from "os";
import * as path from "path";
import { setUnexpectedErrorHandler } from "vs/base/common/errors";
import { main as vsCli } from "vs/code/node/cliProcessMain";
import { validatePaths } from "vs/code/node/paths";
import { ParsedArgs } from "vs/platform/environment/common/environment";
import { buildHelpMessage, buildVersionMessage, Option as VsOption, options as vsOptions } from "vs/platform/environment/node/argv";
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { ipcMain } from "vs/server/src/ipc";
import { enableCustomMarketplace } from "vs/server/src/marketplace";
import { MainServer } from "vs/server/src/server";
import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util";
const { logger } = localRequire<typeof import("@coder/logger/out/index")>("@coder/logger/out/index");
setUnexpectedErrorHandler((error) => logger.warn(error.message));
interface Args extends ParsedArgs {
auth?: AuthType;
"base-path"?: string;
cert?: string;
"cert-key"?: string;
format?: string;
host?: string;
open?: string;
port?: string;
socket?: string;
}
// @ts-ignore: Force `keyof Args` to work.
interface Option extends VsOption {
id: keyof Args;
}
const getArgs = (): Args => {
const options = vsOptions as Option[];
// The last item is _ which is like -- so our options need to come before it.
const last = options.pop()!;
// Remove options that won't work or don't make sense.
let i = options.length;
while (i--) {
switch (options[i].id) {
case "add":
case "diff":
case "file-uri":
case "folder-uri":
case "goto":
case "new-window":
case "reuse-window":
case "wait":
case "disable-gpu":
// TODO: pretty sure these don't work but not 100%.
case "max-memory":
case "prof-startup":
case "inspect-extensions":
case "inspect-brk-extensions":
options.splice(i, 1);
break;
}
}
options.push({ id: "base-path", type: "string", cat: "o", description: "Base path of the URL at which code-server is hosted (used for login redirects)." });
options.push({ id: "cert", type: "string", cat: "o", description: "Path to certificate. If the path is omitted, both this and --cert-key will be generated." });
options.push({ id: "cert-key", type: "string", cat: "o", description: "Path to the certificate's key if one was provided." });
options.push({ id: "extra-builtin-extensions-dir", type: "string", cat: "o", description: "Path to an extra builtin extension directory." });
options.push({ id: "extra-extensions-dir", type: "string", cat: "o", description: "Path to an extra user extension directory." });
options.push({ id: "format", type: "string", cat: "o", description: `Format for the version. ${buildAllowedMessage(FormatType)}.` });
options.push({ id: "host", type: "string", cat: "o", description: "Host for the server." });
options.push({ id: "auth", type: "string", cat: "o", description: `The type of authentication to use. ${buildAllowedMessage(AuthType)}.` });
options.push({ id: "open", type: "boolean", cat: "o", description: "Open in the browser on startup." });
options.push({ id: "port", type: "string", cat: "o", description: "Port for the main server." });
options.push({ id: "socket", type: "string", cat: "o", description: "Listen on a socket instead of host:port." });
options.push(last);
const args = parseMainProcessArgv(process.argv);
if (!args["user-data-dir"]) {
args["user-data-dir"] = path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local/share"), "code-server");
}
if (!args["extensions-dir"]) {
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions");
}
return validatePaths(args);
};
const startVscode = async (): Promise<void | void[]> => {
const args = getArgs();
const extra = args["_"] || [];
const options = {
auth: args.auth,
basePath: args["base-path"],
cert: args.cert,
certKey: args["cert-key"],
folderUri: extra.length > 1 ? extra[extra.length - 1] : undefined,
host: args.host,
password: process.env.PASSWORD,
};
if (options.auth && enumToArray(AuthType).filter((t) => t === options.auth).length === 0) {
throw new Error(`'${options.auth}' is not a valid authentication type.`);
} else if (options.auth && !options.password) {
options.password = await generatePassword();
}
if (!options.certKey && typeof options.certKey !== "undefined") {
throw new Error(`--cert-key cannot be blank`);
} else if (options.certKey && !options.cert) {
throw new Error(`--cert-key was provided but --cert was not`);
} if (!options.cert && typeof options.cert !== "undefined") {
const { cert, certKey } = await generateCertificate();
options.cert = cert;
options.certKey = certKey;
}
enableCustomMarketplace();
const server = new MainServer({
...options,
port: typeof args.port !== "undefined" && parseInt(args.port, 10) || 8080,
socket: args.socket,
}, args);
const [serverAddress, /* ignore */] = await Promise.all([
server.listen(),
unpackExecutables(),
]);
logger.info(`Server listening on ${serverAddress}`);
if (options.auth && !process.env.PASSWORD) {
logger.info(` - Password is ${options.password}`);
logger.info(" - To use your own password, set the PASSWORD environment variable");
} else if (options.auth) {
logger.info(" - Using custom password for authentication");
} else {
logger.info(" - No authentication");
}
if (server.protocol === "https") {
logger.info(
args.cert
? ` - Using provided certificate${args["cert-key"] ? " and key" : ""} for HTTPS`
: ` - Using generated certificate and key for HTTPS`,
);
} else {
logger.info(" - Not serving HTTPS");
}
if (!server.options.socket && args.open) {
// The web socket doesn't seem to work if browsing with 0.0.0.0.
const openAddress = `http://localhost:${server.options.port}`;
await open(openAddress).catch(console.error);
logger.info(` - Opened ${openAddress}`);
}
};
const startCli = (): boolean | Promise<void> => {
const args = getArgs();
if (args.help) {
const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`;
console.log(buildHelpMessage(product.nameLong, executable, pkg.codeServerVersion, undefined, false));
return true;
}
if (args.version) {
if (args.format === "json") {
console.log(JSON.stringify({
codeServerVersion: pkg.codeServerVersion,
commit: product.commit,
vscodeVersion: pkg.version,
}));
} else {
buildVersionMessage(pkg.codeServerVersion, product.commit).split("\n").map((line) => logger.info(line));
}
return true;
}
const shouldSpawnCliProcess = (): boolean => {
return !!args["install-source"]
|| !!args["list-extensions"]
|| !!args["install-extension"]
|| !!args["uninstall-extension"]
|| !!args["locate-extension"]
|| !!args["telemetry"];
};
if (shouldSpawnCliProcess()) {
enableCustomMarketplace();
return vsCli(args);
}
return false;
};
export class WrapperProcess {
private process?: cp.ChildProcess;
private started?: Promise<void>;
public constructor() {
ipcMain.onMessage(async (message) => {
switch (message) {
case "relaunch":
logger.info("Relaunching...");
this.started = undefined;
if (this.process) {
this.process.kill();
}
try {
await this.start();
} catch (error) {
logger.error(error.message);
process.exit(typeof error.code === "number" ? error.code : 1);
}
break;
default:
logger.error(`Unrecognized message ${message}`);
break;
}
});
}
public start(): Promise<void> {
if (!this.started) {
const child = this.spawn();
this.started = ipcMain.handshake(child);
this.process = child;
}
return this.started;
}
private spawn(): cp.ChildProcess {
return cp.spawn(process.argv[0], process.argv.slice(1), {
env: {
...process.env,
LAUNCH_VSCODE: "true",
},
stdio: ["inherit", "inherit", "inherit", "ipc"],
});
}
}
const main = async(): Promise<boolean | void | void[]> => {
if (process.env.LAUNCH_VSCODE) {
await ipcMain.handshake();
return startVscode();
}
return startCli() || new WrapperProcess().start();
};
// It's possible that the pipe has closed (for example if you run code-server
// --version | head -1). Assume that means we're done.
if (!process.stdout.isTTY) {
process.stdout.on("error", () => process.exit());
}
main().catch((error) => {
logger.error(error.message);
process.exit(typeof error.code === "number" ? error.code : 1);
});

View File

@@ -1,69 +0,0 @@
import { URI } from "vs/base/common/uri";
import { registerSingleton } from "vs/platform/instantiation/common/extensions";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { ILocalizationsService } from "vs/platform/localizations/common/localizations";
import { LocalizationsService } from "vs/platform/localizations/electron-browser/localizationsService";
import { IUpdateService } from "vs/platform/update/common/update";
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
import { TelemetryChannelClient } from "vs/server/src/telemetry";
import { IUploadService, UploadService } from 'vs/server/src/upload';
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
class TelemetryService extends TelemetryChannelClient {
public constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
) {
super(remoteAgentService.getConnection()!.getChannel("telemetry"));
}
}
registerSingleton(ILocalizationsService, LocalizationsService);
registerSingleton(ITelemetryService, TelemetryService);
registerSingleton(IUpdateService, UpdateService);
registerSingleton(IUploadService, UploadService, true);
import "vs/workbench/contrib/update/electron-browser/update.contribution";
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
import { coderApi, vscodeApi } from "vs/server/src/api";
/**
* This is called by vs/workbench/browser/web.main.ts after the workbench has
* been initialized so we can initialize our own client-side code.
*/
export const initialize = async (services: ServiceCollection): Promise<void> => {
const target = window as any;
target.ide = coderApi(services);
target.vscode = vscodeApi(services);
const event = new CustomEvent("ide-ready");
(event as any).ide = target.ide;
(event as any).vscode = target.vscode;
window.dispatchEvent(event);
};
export interface Query {
[key: string]: string | undefined;
}
/**
* Return the URL modified with the specified query variables. It's pretty
* stupid so it probably doesn't cover any edge cases. Undefined values will
* unset existing values. Doesn't allow duplicates.
*/
export const withQuery = (url: string, replace: Query): string => {
const uri = URI.parse(url);
const query = { ...replace };
uri.query.split("&").forEach((kv) => {
const [key, value] = kv.split("=", 2);
if (!(key in query)) {
query[key] = value;
}
});
return uri.with({
query: Object.keys(query)
.filter((k) => typeof query[k] !== "undefined")
.map((k) => `${k}=${query[k]}`).join("&"),
}).toString(true);
};

60
src/common/api.ts Normal file
View File

@@ -0,0 +1,60 @@
export interface Application {
readonly categories?: string[]
readonly comment?: string
readonly directory?: string
readonly exec?: string
readonly genericName?: string
readonly icon?: string
readonly installed?: boolean
readonly name: string
/**
* Path if this is a browser app (like VS Code).
*/
readonly path?: string
/**
* PID if this is a process.
*/
readonly pid?: number
readonly version?: string
}
export interface ApplicationsResponse {
readonly applications: ReadonlyArray<Application>
}
export enum SessionError {
FailedToStart = 4000,
Starting = 4001,
InvalidState = 4002,
Unknown = 4003,
}
export interface SessionResponse {
/**
* Whether the process was spawned or an existing one was returned.
*/
created: boolean
pid: number
}
export interface RecentResponse {
readonly paths: string[]
readonly workspaces: string[]
}
export interface HealthRequest {
readonly event: "health"
}
export type ClientMessage = HealthRequest
export interface HealthResponse {
readonly event: "health"
readonly connections: number
}
export type ServerMessage = HealthResponse
export interface ReadyMessage {
protocol: string
}

40
src/common/emitter.ts Normal file
View File

@@ -0,0 +1,40 @@
export interface Disposable {
dispose(): void
}
export interface Event<T> {
(listener: (value: T) => void): Disposable
}
/**
* Emitter typecasts for a single event type.
*/
export class Emitter<T> {
private listeners: Array<(value: T) => void> = []
public get event(): Event<T> {
return (cb: (value: T) => void): Disposable => {
this.listeners.push(cb)
return {
dispose: (): void => {
const i = this.listeners.indexOf(cb)
if (i !== -1) {
this.listeners.splice(i, 1)
}
},
}
}
}
/**
* Emit an event with a value.
*/
public emit(value: T): void {
this.listeners.forEach((cb) => cb(value))
}
public dispose(): void {
this.listeners = []
}
}

24
src/common/http.ts Normal file
View File

@@ -0,0 +1,24 @@
export enum HttpCode {
Ok = 200,
Redirect = 302,
NotFound = 404,
BadRequest = 400,
Unauthorized = 401,
LargePayload = 413,
ServerError = 500,
}
export class HttpError extends Error {
public constructor(message: string, public readonly code: number) {
super(message)
this.name = this.constructor.name
}
}
export enum ApiEndpoint {
applications = "/applications",
process = "/process",
recent = "/recent",
run = "/run",
status = "/status",
}

77
src/common/util.ts Normal file
View File

@@ -0,0 +1,77 @@
import { logger, field } from "@coder/logger"
export interface Options {
base: string
commit: string
logLevel: number
pid?: number
}
/**
* Split a string up to the delimiter. If the delimiter doesn't exist the first
* item will have all the text and the second item will be an empty string.
*/
export const split = (str: string, delimiter: string): [string, string] => {
const index = str.indexOf(delimiter)
return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, ""]
}
export const plural = (count: number): string => (count === 1 ? "" : "s")
export const generateUuid = (length = 24): string => {
const possible = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
return Array(length)
.fill(1)
.map(() => possible[Math.floor(Math.random() * possible.length)])
.join("")
}
/**
* Remove extra slashes in a URL.
*/
export const normalize = (url: string, keepTrailing = false): string => {
return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "")
}
/**
* Get options embedded in the HTML or query params.
*/
export const getOptions = <T extends Options>(): T => {
let options: T
try {
const el = document.getElementById("coder-options")
if (!el) {
throw new Error("no options element")
}
const value = el.getAttribute("data-settings")
if (!value) {
throw new Error("no options value")
}
options = JSON.parse(value)
} catch (error) {
options = {} as T
}
const params = new URLSearchParams(location.search)
const queryOpts = params.get("options")
if (queryOpts) {
options = {
...options,
...JSON.parse(queryOpts),
}
}
if (typeof options.logLevel !== "undefined") {
logger.level = options.logLevel
}
if (options.base) {
const parts = location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = options.base
const url = new URL(location.origin + "/" + parts.join("/"))
options.base = normalize(url.pathname, true)
}
logger.debug("got options", field("options", options))
return options
}

View File

@@ -1,155 +0,0 @@
import * as cp from "child_process";
import { getPathFromAmdModule } from "vs/base/common/amd";
import { VSBuffer } from "vs/base/common/buffer";
import { Emitter } from "vs/base/common/event";
import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { ILogService } from "vs/platform/log/common/log";
import { getNlsConfiguration } from "vs/server/src/nls";
import { Protocol } from "vs/server/src/protocol";
import { uriTransformerPath } from "vs/server/src/util";
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
export abstract class Connection {
private readonly _onClose = new Emitter<void>();
public readonly onClose = this._onClose.event;
private disposed = false;
private _offline: number | undefined;
public constructor(protected protocol: Protocol, public readonly token: string) {}
public get offline(): number | undefined {
return this._offline;
}
public reconnect(socket: ISocket, buffer: VSBuffer): void {
this._offline = undefined;
this.doReconnect(socket, buffer);
}
public dispose(): void {
if (!this.disposed) {
this.disposed = true;
this.doDispose();
this._onClose.fire();
}
}
protected setOffline(): void {
if (!this._offline) {
this._offline = Date.now();
}
}
/**
* Set up the connection on a new socket.
*/
protected abstract doReconnect(socket: ISocket, buffer: VSBuffer): void;
protected abstract doDispose(): void;
}
/**
* Used for all the IPC channels.
*/
export class ManagementConnection extends Connection {
public constructor(protected protocol: Protocol, token: string) {
super(protocol, token);
protocol.onClose(() => this.dispose()); // Explicit close.
protocol.onSocketClose(() => this.setOffline()); // Might reconnect.
}
protected doDispose(): void {
this.protocol.sendDisconnect();
this.protocol.dispose();
this.protocol.getSocket().end();
}
protected doReconnect(socket: ISocket, buffer: VSBuffer): void {
this.protocol.beginAcceptReconnection(socket, buffer);
this.protocol.endAcceptReconnection();
}
}
export class ExtensionHostConnection extends Connection {
private process?: cp.ChildProcess;
public constructor(
locale:string, protocol: Protocol, buffer: VSBuffer, token: string,
private readonly log: ILogService,
private readonly environment: IEnvironmentService,
) {
super(protocol, token);
this.protocol.dispose();
this.spawn(locale, buffer).then((p) => this.process = p);
this.protocol.getUnderlyingSocket().pause();
}
protected doDispose(): void {
if (this.process) {
this.process.kill();
}
this.protocol.getSocket().end();
}
protected doReconnect(socket: ISocket, buffer: VSBuffer): void {
// This is just to set the new socket.
this.protocol.beginAcceptReconnection(socket, null);
this.protocol.dispose();
this.sendInitMessage(buffer);
}
private sendInitMessage(buffer: VSBuffer): void {
const socket = this.protocol.getUnderlyingSocket();
socket.pause();
this.process!.send({ // Process must be set at this point.
type: "VSCODE_EXTHOST_IPC_SOCKET",
initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
}, socket);
}
private async spawn(locale: string, buffer: VSBuffer): Promise<cp.ChildProcess> {
const config = await getNlsConfiguration(locale, this.environment.userDataPath);
const proc = cp.fork(
getPathFromAmdModule(require, "bootstrap-fork"),
[ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath}` ],
{
env: {
...process.env,
AMD_ENTRYPOINT: "vs/workbench/services/extensions/node/extensionHostProcess",
PIPE_LOGGING: "true",
VERBOSE_LOGGING: "true",
VSCODE_EXTHOST_WILL_SEND_SOCKET: "true",
VSCODE_HANDLES_UNCAUGHT_ERRORS: "true",
VSCODE_LOG_STACK: "false",
VSCODE_NLS_CONFIG: JSON.stringify(config),
},
silent: true,
},
);
proc.on("error", () => this.dispose());
proc.on("exit", () => this.dispose());
proc.stdout.setEncoding("utf8").on("data", (d) => this.log.info("Extension host stdout", d));
proc.stderr.setEncoding("utf8").on("data", (d) => this.log.error("Extension host stderr", d));
proc.on("message", (event) => {
if (event && event.type === "__$console") {
const severity = (<any>this.log)[event.severity] ? event.severity : "info";
(<any>this.log)[severity]("Extension host", event.arguments);
}
if (event && event.type === "VSCODE_EXTHOST_DISCONNECTED") {
this.setOffline();
}
});
const listen = (message: IExtHostReadyMessage) => {
if (message.type === "VSCODE_EXTHOST_IPC_READY") {
proc.removeListener("message", listen);
this.sendInitMessage(buffer);
}
};
return proc.on("message", listen);
}
}

View File

@@ -1,57 +0,0 @@
import * as appInsights from "applicationinsights";
import * as https from "https";
import * as http from "http";
import * as os from "os";
export class TelemetryClient implements appInsights.TelemetryClient {
public config: any = {};
public channel = {
setUseDiskRetryCaching: (): void => undefined,
};
public trackEvent(options: appInsights.EventTelemetry): void {
if (!options.properties) {
options.properties = {};
}
if (!options.measurements) {
options.measurements = {};
}
try {
const cpus = os.cpus();
options.measurements.cores = cpus.length;
options.properties["common.cpuModel"] = cpus[0].model;
} catch (error) {}
try {
options.measurements.memoryFree = os.freemem();
options.measurements.memoryTotal = os.totalmem();
} catch (error) {}
try {
options.properties["common.shell"] = os.userInfo().shell;
options.properties["common.release"] = os.release();
options.properties["common.arch"] = os.arch();
} catch (error) {}
try {
const url = process.env.TELEMETRY_URL || "https://v1.telemetry.coder.com/track";
const request = (/^http:/.test(url) ? http : https).request(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
request.on("error", () => { /* We don't care. */ });
request.write(JSON.stringify(options));
request.end();
} catch (error) {}
}
public flush(options: appInsights.FlushOptions): void {
if (options.callback) {
options.callback("");
}
}
}

View File

@@ -1,52 +0,0 @@
import * as cp from "child_process";
import { Emitter } from "vs/base/common/event";
enum ControlMessage {
okToChild = "ok>",
okFromChild = "ok<",
}
export type Message = "relaunch";
class IpcMain {
protected readonly _onMessage = new Emitter<Message>();
public readonly onMessage = this._onMessage.event;
public handshake(child?: cp.ChildProcess): Promise<void> {
return new Promise((resolve, reject) => {
const target = child || process;
if (!target.send) {
throw new Error("Not spawned with IPC enabled");
}
target.on("message", (message) => {
if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) {
target.removeAllListeners();
target.on("message", (msg) => this._onMessage.fire(msg));
if (child) {
target.send!(ControlMessage.okToChild);
}
resolve();
}
});
if (child) {
child.once("error", reject);
child.once("exit", (code) => {
const error = new Error(`Unexpected exit with code ${code}`);
(error as any).code = code;
reject(error);
});
} else {
target.send(ControlMessage.okFromChild);
}
});
}
public relaunch(): void {
if (!process.send) {
throw new Error("Not a child process with IPC enabled");
}
process.send("relaunch");
}
}
export const ipcMain = new IpcMain();

View File

@@ -1,29 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'unsafe-inline'; manifest-src 'self'; img-src 'self';">
<title>Authenticate: code-server</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon" />
<link rel="manifest" href="./manifest.json">
<link href="./static/out/vs/server/src/media/login.css" rel="stylesheet">
</head>
<body>
<form class="login-form" method="post">
<h4 class="title">code-server</h4>
<h2 class="subtitle">
Enter server password
</h2>
<div class="field">
<!-- The onfocus code places the cursor at the end of the value. -->
<input name="password" type="password" class="input" value=""
required autofocus
onfocus="const value=this.value;this.value='';this.value=value;">
</div>
<button class="button" type="submit">
<span class="label">Enter IDE</span>
</button>
<div class="error-display" style="display:none">{{ERROR}}</div>
</form>
</body>
</html>

View File

@@ -1,175 +0,0 @@
import * as fs from "fs";
import * as path from "path";
import * as util from "util";
import { CancellationToken } from "vs/base/common/cancellation";
import { mkdirp } from "vs/base/node/pfs";
import * as vszip from "vs/base/node/zip";
import * as nls from "vs/nls";
import product from "vs/platform/product/node/product";
import { localRequire } from "vs/server/src/util";
const tarStream = localRequire<typeof import("tar-stream")>("tar-stream/index");
// We will be overriding these, so keep a reference to the original.
const vszipExtract = vszip.extract;
const vszipBuffer = vszip.buffer;
export interface IExtractOptions {
overwrite?: boolean;
/**
* Source path within the TAR/ZIP archive. Only the files
* contained in this path will be extracted.
*/
sourcePath?: string;
}
export interface IFile {
path: string;
contents?: Buffer | string;
localPath?: string;
}
export const tar = async (tarPath: string, files: IFile[]): Promise<string> => {
const pack = tarStream.pack();
const chunks: Buffer[] = [];
const ended = new Promise<Buffer>((resolve) => {
pack.on("end", () => resolve(Buffer.concat(chunks)));
});
pack.on("data", (chunk: Buffer) => chunks.push(chunk));
for (let i = 0; i < files.length; i++) {
const file = files[i];
pack.entry({ name: file.path }, file.contents);
}
pack.finalize();
await util.promisify(fs.writeFile)(tarPath, await ended);
return tarPath;
};
export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
try {
await extractTar(archivePath, extractPath, options, token);
} catch (error) {
if (error.toString().includes("Invalid tar header")) {
await vszipExtract(archivePath, extractPath, options, token);
}
}
};
export const buffer = (targetPath: string, filePath: string): Promise<Buffer> => {
return new Promise<Buffer>(async (resolve, reject) => {
try {
let done: boolean = false;
await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => {
if (path.normalize(assetPath) === path.normalize(filePath)) {
done = true;
resolve(data);
}
});
if (!done) {
throw new Error("couldn't find asset " + filePath);
}
} catch (error) {
if (error.toString().includes("Invalid tar header")) {
vszipBuffer(targetPath, filePath).then(resolve).catch(reject);
} else {
reject(error);
}
}
});
};
const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise<void> => {
const buffer = await util.promisify(fs.readFile)(tarPath);
return new Promise<void>(async (resolve, reject): Promise<void> => {
const extractor = tarStream.extract();
extractor.once("error", reject);
extractor.on("entry", async (header, stream, next) => {
const name = header.name;
if (match.test(name)) {
extractData(stream).then((data) => {
callback(name, data);
next();
}).catch(reject);
stream.resume();
} else {
stream.on("end", () => next());
stream.resume();
}
});
extractor.on("finish", resolve);
extractor.write(buffer);
extractor.end();
});
};
const extractData = (stream: NodeJS.ReadableStream): Promise<Buffer> => {
return new Promise((resolve, reject): void => {
const fileData: Buffer[] = [];
stream.on("data", (data) => fileData.push(data));
stream.on("end", () => resolve(Buffer.concat(fileData)));
stream.on("error", reject);
});
};
const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
const buffer = await util.promisify(fs.readFile)(tarPath);
return new Promise<void>(async (resolve, reject): Promise<void> => {
const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : "");
const extractor = tarStream.extract();
extractor.once("error", reject);
extractor.on("entry", async (header, stream, next) => {
const rawName = path.normalize(header.name);
const nextEntry = (): void => {
stream.resume();
next();
};
if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) {
return nextEntry();
}
const fileName = rawName.replace(sourcePathRegex, "");
const targetFileName = path.join(targetPath, fileName);
if (/\/$/.test(fileName)) {
return mkdirp(targetFileName).then(nextEntry);
}
const dirName = path.dirname(fileName);
const targetDirName = path.join(targetPath, dirName);
if (targetDirName.indexOf(targetPath) !== 0) {
return reject(nls.localize("invalid file", "Error extracting {0}. Invalid file.", fileName));
}
return mkdirp(targetDirName, undefined, token).then(() => {
const fstream = fs.createWriteStream(targetFileName, { mode: header.mode });
fstream.once("close", () => next());
fstream.once("error", reject);
stream.pipe(fstream);
stream.resume();
});
});
extractor.once("finish", resolve);
extractor.write(buffer);
extractor.end();
});
};
/**
* Override original functionality so we can use a custom marketplace with
* either tars or zips.
*/
export const enableCustomMarketplace = (): void => {
(<any>product).extensionsGallery = { // Use `any` to override readonly.
serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com",
itemUrl: process.env.ITEM_URL || "",
controlUrl: "",
recommendationsUrl: "",
...(product.extensionsGallery || {}),
};
const target = vszip as typeof vszip;
target.zip = tar;
target.extract = extract;
target.buffer = buffer;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,94 +0,0 @@
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
html, body {
background-color: #FFFFFF;
height: 100%;
min-height: 100%;
}
body {
align-items: center;
display: flex;
font-family: "monospace";
justify-content: center;
margin: 0;
padding: 10px;
}
.login-form {
border-radius: 5px;
box-shadow: 0 18px 80px 10px rgba(69, 65, 78, 0.08);
color: #575962;
margin-top: -10%;
max-width: 328px;
padding: 40px;
position: relative;
width: 100%;
}
.login-form > .title {
text-align: center;
text-transform: uppercase;
font-size: 12px;
font-weight: 500;
letter-spacing: 1.5px;
line-height: 15px;
margin-bottom: 0px;
margin-bottom: 5px;
margin-top: 0px;
}
.login-form > .subtitle {
font-size: 19px;
font-weight: bold;
line-height: 25px;
margin-bottom: 45px;
margin: 0;
text-align: center;
}
.login-form > .field {
text-align: left;
font-size: 12px;
color: #797E84;
margin: 16px 0;
}
.login-form > .field > .input {
background: none !important;
border: 1px solid #ccc;
border-radius: 2px;
padding: 5px;
width: 100%;
}
.login-form > .button {
border: none;
border-radius: 24px;
box-shadow: 0 12px 17px 2px rgba(171,173,163,0.14), 0 5px 22px 4px rgba(171,173,163,0.12), 0 7px 8px -4px rgba(171,173,163,0.2);
cursor: pointer;
display: block;
padding: 15px 5px;
width: 100%;
}
.login-form > .button:hover {
background-color: rgb(0, 122, 204);
color: #fff;
}
.error-display {
box-sizing: border-box;
color: #bb2d0f;
font-size: 14px;
font-weight: 400;
line-height: 12px;
padding: 20px 8px 0;
text-align: center;
}

View File

@@ -1,13 +0,0 @@
{
"name": "code-server",
"short_name": "code-server",
"start_url": ".",
"display": "standalone",
"background-color": "#fff",
"description": "Run VS Code on a remote server.",
"icons": [{
"src": "static/code-server.png",
"sizes": "128x128",
"type": "image/png"
}]
}

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