Compare commits

..

176 Commits

Author SHA1 Message Date
G r e y
f621a74062 Simplify HttpError
This change is cosmetic

* Remove public keyword - public by default
* Remove details - this was unused throughout the entire codebase
2020-08-08 21:55:35 -05:00
G r e y
c78d164948 Fix nfpm typo (#1943) 2020-08-05 12:48:41 -04:00
Anmol Sethi
4dd2c86cca FAQ: Demonstrate how to switch the marketplace 2020-08-04 10:11:55 -04:00
Asher
486652abaf Update standalone test to account for timestamp
The updated logger outputs timestamps now.
2020-07-31 14:06:49 -05:00
Asher
5370f7876d Merge pull request #1927 from cdr/dead-code
Remove dead code
2020-07-31 12:25:56 -05:00
Asher
eccaf8eb50 Merge pull request #1931 from cdr/rimraf
Fix package step
2020-07-31 12:25:18 -05:00
Asher
cbf7c9556c Merge pull request #1920 from fxxjdedd/patch-1
feat: persist route query to local
2020-07-31 11:36:25 -05:00
Asher
b63cf192b5 Remove broken symlinks in extensions node modules
The broken symlinks cause nfpm to fail.
2020-07-31 10:49:45 -05:00
Asher
50ed29e0f0 Move rimraf to prod deps in extensions
The postinstall uses rimraf so it needs to exist in the final build.
2020-07-31 10:49:40 -05:00
futengda
ecb9bb2428 refactor: write lastVisited and query at the same time
In addition, the `settings.write` method now uses shallow merge by default
2020-07-31 12:25:20 +08:00
Asher
e86c066438 Add helper functions to make some code clearer 2020-07-30 12:14:31 -05:00
futengda
b6e791f7d0 refactor: write route.query via settings.write
I added a shallow parameter, because the query should not be extends, but should be replaced directly.
2020-07-30 16:54:02 +08:00
Asher
c581bca29d Force minimist update 2020-07-29 18:48:08 -05:00
Asher
2fa5037859 Log output to disk 2020-07-29 18:48:07 -05:00
Asher
7c2ca7d03e Add the ability to prepend to the proxy path
This is for applications like Jupyter that aren't base path agnostic.
2020-07-29 18:48:06 -05:00
Asher
c67d31580f Include details if any in JSON requests 2020-07-29 18:48:05 -05:00
Asher
58bd7008b4 Make dispose async 2020-07-29 18:48:04 -05:00
Asher
4b6c0a6fc3 Update logger 2020-07-29 18:48:03 -05:00
Asher
554b6d6fcf Remove apply portion of update endpoint
It can still be used to check for updates but will not apply them.

For now also remove the update check loop in VS Code since it's
currently unused (update check is hardcoded off right now) and won't
work anyway since it also applies the update which now won't work. In
the future we should integrate the check into the browser update
service.
2020-07-29 18:48:02 -05:00
jae
8021385ac4 Add enterprise context (#1923) 2020-07-29 13:37:19 -04:00
fxxjdedd
5ba650bb6f feat: persist route query to local
Provide a way for the shell script running in the docker container to get the url query.
2020-07-28 20:14:52 +08:00
Asher
e8f6d30055 Make providers endpoint-agnostic
A provider can now be registered on multiple endpoints (or potentially
moved if needed).
2020-07-27 12:00:48 -05:00
Asher
2819fd51e2 Remove unused endpoints
- dashboard
- app api
2020-07-27 12:00:42 -05:00
Anmol Sethi
638ab7c557 Fix CI 2020-07-22 18:31:24 -04:00
Anmol Sethi
0bd808270d doc/guide: Fix TOC 2020-07-22 17:29:45 -04:00
Anmol Sethi
bc78e16146 doc/guide: Improve nginx docs (#1902)
Made it a full alternative to caddy, just so we don't ever have to explain how to configure Nginx again.
2020-07-22 16:05:39 -04:00
Anmol Sethi
3764d296c6 .github/lock.yml: Formatting 2020-07-22 15:15:30 -04:00
Anmol Sethi
90eec91f9c Add .github/lock.yml
Too many people comment on super old issues.
2020-07-22 14:16:53 -04:00
Asher
d3164fc910 Merge pull request #1867 from Niek/patch-1
Add Nginx instructions to guide
2020-07-21 17:16:57 -05:00
Asher
6c5a9edced Tiny text changes 2020-07-21 17:16:32 -05:00
Asher
4727385a01 Merge pull request #1885 from cdr/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-21 13:56:29 -05:00
Asher
89cfe6876e Merge pull request #1896 from cdr/vscode-1.47.2
Update to VS Code 1.47.2
2020-07-21 13:51:22 -05:00
Asher
de8e9804ad Update to VS Code 1.47.2 2020-07-21 13:16:44 -05:00
Asher
81d25dd048 Add missing bootstrap-node.js to final build
Fixes #1884.
2020-07-21 11:31:27 -05:00
dependabot[bot]
0193516f55 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 01:02:31 +00:00
Kyle Carberry
19d14d2414 Add ThirdPartyNotices.txt 2020-07-16 19:01:09 -06:00
Niek van der Maas
739ac468ed Merge branch 'master' into patch-1 2020-07-16 19:25:25 +02:00
Niek van der Maas
6c3e4d2a76 Add trailing / 2020-07-16 19:24:13 +02:00
Asher
fb9ebeb7aa Merge pull request #1874 from cdr/vscode-1.47.0
Update VS Code to 1.47.0
2020-07-10 16:02:34 -05:00
Asher
641b36be6a Update VS Code to 1.47.0 2020-07-09 17:04:11 -05:00
Asher
e858a4f4c7 Merge pull request #1869 from cdr/vscode-1.46.1
Update VS Code to 1.46.1
2020-07-07 18:09:36 -05:00
Asher
a06522f254 Update VS Code to 1.46.1 2020-07-07 17:01:23 -05:00
Niek van der Maas
96af9761b7 Add Nginx instructions to guide
Added Nginx instructions for people who prefer to use this instead of Caddy
2020-07-06 10:45:09 +02:00
Asher
9ff0e455c3 Merge pull request #1853 from cdr/fix-heartbeat
Fix connections sticking around indefinitely
2020-06-30 17:43:36 -05:00
Asher
ebef18d626 Fix connections sticking around indefinitely
For some reason it only affects the extension host socket (something to
do with passing it via IPC?) but I changed both just to be sure.

Fixes #1795.
2020-06-30 16:41:47 -05:00
Ammar Bandukwala
a942531079 Update remote location restrictions in README 2020-06-25 15:33:55 -05:00
Kyle Carberry
cc9c9e9db5 Remove funding statement 2020-06-25 14:01:34 -06:00
Anmol Sethi
a7026cc82c Remove funding figure 2020-06-25 12:18:15 -04:00
Asher
ed285f97fd Merge pull request #1830 from cdr/fix-config
Initialize config and use correct settings path
2020-06-24 12:26:18 -05:00
Asher
1b7d4b5a18 Initialize config and use correct settings path
Fixes #1829.
2020-06-24 11:40:17 -05:00
Anmol Sethi
364f9dd854 Ask for resume/github re hiring 2020-06-23 07:58:28 -04:00
Anmol Sethi
7e1aebe009 Fix formatting 2020-06-22 01:42:46 -04:00
Anmol Sethi
609c7ef4ec Fix bad $PATH when building MacOS
The previous release mistakenly distributed the wrong version
of node...

Very sad.

See https://github.com/cdr/code-server/issues/1710#issuecomment-646472716
2020-06-22 00:57:40 -04:00
Anmol Sethi
5a6411fa49 Make our funding situation clear in Hiring section 2020-06-22 00:57:24 -04:00
Anmol Sethi
3da6c561b8 Fix wording in FAQ.md 2020-06-16 11:14:02 -04:00
Anmol Sethi
bb118eba6e Merge pull request #1808 from SAB-6/typo-fixes
fix typos in doc/FAQ.md
2020-06-16 11:13:07 -04:00
Shereef Bankole
96e57f1e6f fix typos in doc/FAQ.md 2020-06-14 23:46:01 +01:00
Anmol Sethi
2a9b7a4d5f Merge pull request #1806 from cdr/bsd
Add FreeBSD support to install script
2020-06-13 11:16:13 -04:00
Anmol Sethi
3d9e3b8717 Add FreeBSD support to install script 2020-06-13 11:14:32 -04:00
Anmol Sethi
264abed82c Fix typo 2020-06-12 02:32:40 -04:00
Anmol Sethi
19257a8bc2 Fix typo 2020-06-09 18:14:21 -04:00
Anmol Sethi
034266db47 Merge pull request #1790 from cdr/hiring
Mention we're hiring in the README.md
2020-06-09 18:13:57 -04:00
Anmol Sethi
69b8096eb3 Mention we're hiring in the README.md 2020-06-09 18:13:20 -04:00
Anmol Sethi
7b982ae782 Merge pull request #1773 from jeffjose/patch-1
Update --data-dir flag to --user-data-dir
2020-06-05 17:39:30 -04:00
Jeffrey Jose
3a37add48d Update --data-dir flag to --user-data-dir
Update --data-dir flag to --user-data-dir
2020-06-04 23:42:10 -07:00
Anmol Sethi
7958cc7e29 install.sh: Print creation of CACHE_DIR 2020-06-04 18:23:01 -04:00
Anmol Sethi
88c76d4794 Fix typo in guide.md 2020-06-04 16:49:49 -04:00
Anmol Sethi
022a2e0860 Merge branch 'docs' 2020-06-04 16:47:36 -04:00
Anmol Sethi
bd2e55dcf3 Make README more clear 2020-06-04 16:47:27 -04:00
Anmol Sethi
d3773c11f1 Merge pull request #1766 from cdr/v3.4.1
v3.4.1
2020-06-04 12:30:34 -04:00
Anmol Sethi
59694fb72e FAQ: Explain differences compared to Theia
Closes #1756
2020-06-04 07:30:41 -04:00
Anmol Sethi
ac2bf56ebc Explain $SERVICE_URL and $ITEM_URL in more detail
Closes #1762
2020-06-04 07:25:32 -04:00
Anmol Sethi
48f7c27248 v3.4.1 2020-06-04 06:24:24 -04:00
Anmol Sethi
06b387fe98 Merge pull request #1765 from cdr/global-storage
Always create globalStorageHome
2020-06-04 05:52:21 -04:00
Anmol Sethi
4cf81d88a7 Always create globalStorageHome
Closes #1693
2020-06-04 05:33:27 -04:00
Anmol Sethi
79d1e179f8 Merge pull request #1761 from cdr/static
Stop bundling libraries in release
2020-06-04 04:38:57 -04:00
Anmol Sethi
c00f931500 Remove zip library dependency 2020-06-03 18:24:59 -04:00
Anmol Sethi
fd5c5960c2 Fixes for release 2020-06-03 16:22:59 -04:00
Anmol Sethi
ab081cd522 Add warning when using outdated code-server script 2020-06-03 15:45:17 -04:00
Anmol Sethi
e2789608b2 Fix autoupdates for Darwin 2020-06-03 15:45:17 -04:00
Anmol Sethi
85ad7e4fb4 Remove duplicate log
Also confirmed that #1750 is fixed.
2020-06-03 15:45:17 -04:00
Anmol Sethi
cb9c5b2d49 Fix typos 2020-06-03 15:45:16 -04:00
Anmol Sethi
d4ef7c1412 Remove colons from image filenames 2020-06-03 15:45:16 -04:00
Anmol Sethi
5815b4a0c0 Rename dev/container -> dev/image 2020-06-03 15:45:16 -04:00
Anmol Sethi
bdb670e852 Rename container and release-container to images and release-image 2020-06-03 15:45:16 -04:00
Anmol Sethi
11d7932968 Stop bundling libraries in release
- Instead we now use CentOS 7 for the static build to guarantee
  that we only depend on libc v2.17

- For macOS we now pull in a static node binary and bundle that instead.
2020-06-03 15:45:16 -04:00
Anmol Sethi
02a77b528b Support recursive symlinks in release script
See
https://github.com/cdr/code-server/issues/1746#issuecomment-637830396
2020-06-03 15:45:16 -04:00
Anmol Sethi
206f195c1c Minor grammar fixes in FAQ 2020-06-03 15:45:16 -04:00
Anmol Sethi
2c2a6498af Parse config file in entry
This way setting --data-dir and --extension-dir in the config file
will work for --install--extension and whatnot.
2020-06-03 15:45:16 -04:00
Anmol Sethi
7ab47b3d83 Trim LD_LIBRARY_PATH on startup 2020-06-03 15:45:16 -04:00
Anmol Sethi
9a3b9fcac2 Fix install script to trim leading v from version
Updates #1746
2020-06-02 17:22:24 -04:00
Anmol Sethi
b88163392c Merge pull request #1736 from cdr/faq-updates
More questions in the FAQ
2020-05-27 21:39:56 -04:00
Anmol Sethi
c7cad402b4 Cleanup FAQ
Prominently explain how code-server is different from VS Code

Closes #1718
2020-05-27 21:39:19 -04:00
Anmol Sethi
8dfac0fb65 More questions in the FAQ
Closes #1732
2020-05-27 20:45:11 -04:00
Anmol Sethi
90caca3336 Minor fixes 2020-05-27 20:38:29 -04:00
Anmol Sethi
80bcfd918b Merge pull request #1701 from cdr/auto-install
Add auto install script
2020-05-27 20:35:52 -04:00
Anmol Sethi
69ad52907e v3.4.0 2020-05-27 19:21:15 -04:00
Anmol Sethi
fbd85649f9 Fix CI 2020-05-27 17:55:57 -04:00
Anmol Sethi
30e9c516e7 Further improve AUR installation 2020-05-27 17:46:55 -04:00
Anmol Sethi
af398c49fd Workaround lack of builtin in /bin/sh 2020-05-27 17:01:33 -04:00
Anmol Sethi
29e5c4a293 Clarify npm docs in install.md 2020-05-27 16:48:11 -04:00
Anmol Sethi
f71d8875d0 Rename binary release to standalone 2020-05-27 16:39:17 -04:00
Anmol Sethi
06c26a22cd Improve aur installation clarity 2020-05-27 16:01:06 -04:00
Anmol Sethi
fa45fd0e31 Rename static releases to binary releases
More clear as discussed in PR.
2020-05-27 15:57:18 -04:00
Anmol Sethi
665ca017a1 Fixes from @code-asher's godly review 2020-05-27 15:48:22 -04:00
Anmol Sethi
33bca2d141 Adjust nfpm config for bindir removal
See https://github.com/goreleaser/nfpm/pull/142#issuecomment-634427333
2020-05-27 15:48:22 -04:00
Anmol Sethi
eb17a293e5 Document Microsoft's Remote extensions
Closes #1681
2020-05-27 15:48:22 -04:00
Anmol Sethi
c51d94d8a9 Document PWA
Closes #1726
2020-05-27 15:48:22 -04:00
Anmol Sethi
e9101a2421 Improve formatting 2020-05-27 15:48:22 -04:00
Anmol Sethi
7ef82d8422 Improved install.sh flags 2020-05-27 15:48:22 -04:00
Anmol Sethi
42b5152888 Further documentation cleanup 2020-05-27 15:48:22 -04:00
Anmol Sethi
7dcfde7329 Documentation fixes 2020-05-27 15:48:22 -04:00
Anmol Sethi
15cd727b96 Replace gif with screenshot 2020-05-27 15:48:22 -04:00
Anmol Sethi
e55d3e49e1 Bundle in libicu on macOS
Closes #1710

Also reported in #1640
2020-05-27 15:48:22 -04:00
Anmol Sethi
ac9b57c07e Properly bundle in libstdc++ 2020-05-27 15:48:22 -04:00
Anmol Sethi
f117475970 install.md: Add https://github.com/linuxserver/docker-code-server
See https://github.com/cdr/code-server/issues/1337#issuecomment-632131384
2020-05-27 15:48:22 -04:00
Anmol Sethi
a40dabbd22 Add install script to docs 2020-05-27 15:48:22 -04:00
Anmol Sethi
e0172d0953 Minor fixes for install.sh and bundle in libstdc++
Closes #1706
2020-05-27 15:48:22 -04:00
Anmol Sethi
c80b2748e1 install.sh: Fixes from @code-asher's review 2020-05-27 15:48:22 -04:00
Anmol Sethi
510d84898c install.sh: Add our own flag parser
Fully supports long opts!
2020-05-27 15:48:22 -04:00
Anmol Sethi
0129e002e8 Add install.sh into README.md 2020-05-27 15:48:22 -04:00
Anmol Sethi
3b11733bd8 Add auto install script 2020-05-27 15:48:22 -04:00
Anmol Sethi
96eeb9fea0 Fix install instructions for SUSE
Closes #1699
2020-05-27 15:48:22 -04:00
Anmol Sethi
2ba8fee605 Merge pull request #1727 from cdr/ext
Fix extension install path for CLI
2020-05-27 15:48:00 -04:00
Anmol Sethi
9d0dcf3c44 Fix extension install path for CLI
Closes #1713
2020-05-27 14:28:40 -04:00
Anmol Sethi
3140a0cb15 Merge pull request #1719 from cucumberbob123/patch-1
Fixed grammar error
2020-05-24 23:20:07 -04:00
Cucumberbob
9a8c06d09d Fixed grammar error
Removed apostrophe used for pluralization

server's -> servers
2020-05-24 15:08:05 +01:00
Anmol Sethi
c8b58a0f70 CONTRIBUTING.md update 2020-05-21 12:57:29 -04:00
Anmol Sethi
c9afd01b60 Update FAQ for extension request template 2020-05-21 11:59:04 -04:00
Anmol Sethi
81a4dd7d89 Merge pull request #1707 from cdr/issue_templates
Add issue template for extension request
2020-05-21 11:58:03 -04:00
Anmol Sethi
3445a55c2b Add issue template for extension request
Closes #1696
2020-05-21 11:56:41 -04:00
Anmol Sethi
0559d4d870 Merge pull request #1705 from oonqt/patch-2
Update CONTRIBUTING.md
2020-05-21 11:38:01 -04:00
Rechigo
608cefa8cd Update CONTRIBUTING.md
It is not mentioned that the "release" script requires "jq" to be installed on the system to build code-server releases
2020-05-20 15:26:23 -07:00
Anmol Sethi
521ac7d91f Merge pull request #1697 from cdr/aur-docs
Update AUR docs
2020-05-20 11:45:57 -04:00
Anmol Sethi
08f5760718 Merge pull request #1703 from cdr/auto-update
Make automatic updates on v3.2.0 work again
2020-05-20 11:45:24 -04:00
Anmol Sethi
3ddf242c65 Make automatic updates on v3.2.0 work again
Only for linux amd64 users which is the majority of our userbase.
2020-05-20 10:37:31 -04:00
Anmol Sethi
0d207f4f9a Update AUR docs
Closes #1634
2020-05-20 09:21:22 -04:00
Anmol Sethi
2064e88e06 Merge pull request #1694 from cdr/arch-instructions
Document AUR install instructions
2020-05-19 23:46:39 -04:00
Colin Adler
37d287199f Document AUR install instructions 2020-05-19 22:41:28 -05:00
Anmol Sethi
1ee407bf0c Clarify language in issue template 2020-05-19 22:47:34 -04:00
Anmol Sethi
971217c4e6 Add Architecture to issue template 2020-05-19 22:44:45 -04:00
Anmol Sethi
078571d267 Merge pull request #1688 from cdr/fix-typos
Improve clarity in guide and fix typo in FAQ
2020-05-19 18:20:06 -04:00
Anmol Sethi
25ea76ebc9 Improve clarity in guide and fix typo in FAQ 2020-05-19 13:46:19 -04:00
Anmol Sethi
f4c97abe91 Merge pull request #1678 from cdr/faq
Add blank screen problem with iPad to FAQ
2020-05-19 13:08:54 -04:00
Anmol Sethi
073317394b Merge pull request #1679 from cdr/fix-config
Allow user-data-dir and extension-dir in config.yaml
2020-05-19 13:08:47 -04:00
Anmol Sethi
cf887ab10a Merge branch 'master' into faq 2020-05-19 13:08:39 -04:00
Anmol Sethi
6421206732 Merge pull request #1677 from cdr/yarn
Mention yarn as another installation option
2020-05-19 13:08:29 -04:00
Asher
288e7a5fb5 Merge pull request #1684 from SuperSandro2000/typos
Fix typos
2020-05-19 11:47:04 -05:00
Sandro Jäckel
5ec8a6efbd Fix typos 2020-05-19 12:19:09 +02:00
Anmol Sethi
8053ec6872 Allow user-data-dir and extension-dir in config.yaml
Closes #1676
2020-05-19 00:41:27 -04:00
Anmol Sethi
520d8e66a8 Add blank screen problem with iPad to FAQ 2020-05-19 00:24:49 -04:00
Anmol Sethi
9858af9014 Mention yarn as another installation option 2020-05-19 00:17:23 -04:00
Anmol Sethi
aa87270148 Fixes for CI 2020-05-18 22:43:31 -04:00
Anmol Sethi
8b329caf0e Merge pull request #1672 from cdr/v3.3.1
Release v3.3.1
2020-05-18 22:36:46 -04:00
Anmol Sethi
6f1309795e Rebuild all node_modules on npm install
Stuff like ripgrep needs to be refetched so we cannot bundle
node_modules at all.
2020-05-18 21:37:16 -04:00
Anmol Sethi
5f94d5a687 Release v3.3.1
This release fixes bugs introduced with the release of v3.3.0

- We've reverted to VS Code 1.45.1 due to bugs in 1.46 #1667
- Accessing code-server from a web browser on Windows has been fixed #1642
- Search in project has been fixed #1665
- The glibc requirement on static releases has been lowered to v2.19 #1656
2020-05-18 18:11:52 -04:00
Anmol Sethi
d6b1d0c7ff Merge pull request #1671 from cdr/pin-vscode
Pin to vscode 1.45.1
2020-05-18 18:09:44 -04:00
Anmol Sethi
ce9d14d55e Pin to vscode 1.45.1
1.46 isn't released yet and has bugs.

Closes #1667
2020-05-18 17:34:22 -04:00
Anmol Sethi
eccee53142 Merge pull request #1670 from cdr/windows
Fix paths from Windows client to non-Windows server
2020-05-18 16:14:48 -04:00
Asher
f7f11ad6c2 Fix paths from Windows client to non-Windows server
Fixes #1659
Fixes #1642
2020-05-18 15:06:11 -05:00
Anmol Sethi
0c2381f4ff Merge pull request #1668 from cdr/fix-ci
Workaround for GH Actions stripping permissions
2020-05-18 15:32:11 -04:00
Anmol Sethi
e4ddffd0e2 Workaround for GH Actions stripping permissions
Closes #1665
2020-05-18 13:56:53 -04:00
Anmol Sethi
f5ac262a2f Indicat how to get version in issue template 2020-05-18 13:20:13 -04:00
Anmol Sethi
04cd74cad0 Add more info to issue template
Closes #1655
2020-05-18 13:14:29 -04:00
Anmol Sethi
7b1edd5ad4 Merge pull request #1657 from cdr/downgrade-CI
Downgrade CI to Debian 8 for glibc 2.19
2020-05-18 01:54:46 -04:00
Anmol Sethi
47d50c9163 Downgrade CI to Debian 8 for glibc 2.19
Closes #1656
2020-05-18 00:38:55 -04:00
Anmol Sethi
8a3466e86f More grammar fixes for FAQ 2020-05-17 22:47:23 -04:00
Anmol Sethi
59f0128d27 Shorten static release script horizontally 2020-05-17 21:11:52 -04:00
Anmol Sethi
761c2035c7 Fix grammar in README 2020-05-17 20:47:33 -04:00
Anmol Sethi
094ca2ad97 Link to all other docs in guide.md
I expect people will frequently link to it.
2020-05-17 20:39:45 -04:00
Anmol Sethi
c33a4651c9 Improve static install script 2020-05-17 20:36:10 -04:00
Anmol Sethi
783dfe0a14 Fix typo in installation docs 2020-05-17 20:32:04 -04:00
Anmol Sethi
b9f43c3542 Fix grammar in FAQ 2020-05-17 20:29:00 -04:00
Anmol Sethi
65325eef89 Clarify location of config file in FAQ 2020-05-17 20:27:45 -04:00
Anmol Sethi
2421cab479 Fix grammar in README 2020-05-17 20:25:33 -04:00
Anmol Sethi
ec1c74c146 Add release-images to clean.sh 2020-05-17 19:52:59 -04:00
79 changed files with 1995 additions and 1983 deletions

28
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Bug report
about: Report a bug and help us improve
title: ""
labels: ""
assignees: ""
---
<!--
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.
If you can reproduce the issue on vanilla VS Code,
please file the issue at the VS Code repository instead.
Provide screenshots if applicable.
Please fill in the issue template and try to be as detailed
and clear as possible!
-->
- Web Browser:
- Local OS:
- Remote OS:
- Remote Architecture:
- `code-server --version`:

View File

@@ -0,0 +1,18 @@
---
name: Extension request
about: Request an extension missing from the code-server marketplace
title: ""
labels: extension-request
assignees: cmoog
---
<!--
Details on the code-server extension marketplace are at
https://github.com/cdr/code-server/blob/master/doc/FAQ.md#whats-the-deal-with-extensions
Please fill in the issue template!
-->
- [ ] Extension name:
- [ ] Extension GitHub or homepage:

View File

@@ -0,0 +1,13 @@
---
name: Feature request
about: Suggest an idea
title: ""
labels: feature
assignees: ""
---
<!--
Please search for existing issues before filing.
Please describe the feature as clearly as possible!
-->

View File

@@ -1,11 +1,4 @@
<!--
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.
The issue tracker is only for bugs and features.
-->

37
.github/lock.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 90
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

View File

@@ -1,6 +1,6 @@
name: ci
on: [push, pull_request]
on: [push]
jobs:
fmt:
@@ -8,7 +8,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/fmt.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/fmt.sh
@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/lint.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/lint.sh
@@ -26,7 +26,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/test.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/test.sh
@@ -35,14 +35,14 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/release.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/release.sh
- name: Upload npm package artifact
uses: actions/upload-artifact@v2
with:
name: npm-package
path: ./release
path: ./release-npm-package
linux-amd64:
needs: release
@@ -53,11 +53,11 @@ jobs:
uses: actions/download-artifact@v2
with:
name: npm-package
path: ./release
- name: Run ./ci/steps/release-static.sh
uses: ./ci/container
path: ./release-npm-package
- name: Run ./ci/steps/release-packages.sh
uses: ./ci/images/centos7
with:
args: ./ci/steps/release-static.sh
args: ./ci/steps/release-packages.sh
- name: Upload release artifacts
uses: actions/upload-artifact@v2
with:
@@ -73,11 +73,11 @@ jobs:
uses: actions/download-artifact@v2
with:
name: npm-package
path: ./release
- name: Run ./ci/steps/release-static.sh
uses: ./ci/container
path: ./release-npm-package
- name: Run ./ci/steps/release-packages.sh
uses: ./ci/images/centos7
with:
args: ./ci/steps/release-static.sh
args: ./ci/steps/release-packages.sh
- name: Upload release artifacts
uses: actions/upload-artifact@v2
with:
@@ -93,10 +93,8 @@ jobs:
uses: actions/download-artifact@v2
with:
name: npm-package
path: ./release
- run: brew unlink node@12
- run: brew install node
- run: ./ci/steps/release-static.sh
path: ./release-npm-package
- run: ./ci/steps/release-packages.sh
env:
# Otherwise we get rate limited when fetching the ripgrep binary.
# For whatever reason only MacOS needs it.
@@ -118,7 +116,7 @@ jobs:
name: release-packages
path: ./release-packages
- name: Run ./ci/steps/build-docker-image.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/build-docker-image.sh
- name: Upload release image
@@ -138,7 +136,7 @@ jobs:
name: release-packages
path: ./release-packages
- name: Run ./ci/steps/build-docker-image.sh
uses: ./ci/container
uses: ./ci/images/centos7
with:
args: ./ci/steps/build-docker-image.sh
- name: Upload release image

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/publish-npm.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/publish-npm.sh
env:
@@ -22,7 +22,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run ./ci/steps/push-docker-manifest.sh
uses: ./ci/container
uses: ./ci/images/debian8
with:
args: ./ci/steps/push-docker-manifest.sh
env:

4
.gitignore vendored
View File

@@ -3,8 +3,10 @@
dist*
out*
release/
release-static/
release-npm-package/
release-standalone/
release-packages/
release-gcp/
release-images/
node_modules
node-*

1
.ignore Normal file
View File

@@ -0,0 +1 @@
lib

112
README.md
View File

@@ -2,94 +2,45 @@
Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and access it in the browser.
- **Code everywhere:** 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 tasks runs on your server.
Make use of a spare computer you have lying around and turn it into a full development environment.
![Screenshot](./doc/assets/screenshot.png)
![Example gif](./doc/assets/code-server.gif)
## Highlights
## Getting started
- **Code everywhere**
- Code on your Chromebook, tablet, and laptop with a consistent development 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 as all intensive tasks runs on your server.
- Make use of a spare computer you have lying around and turn it into a full development environment.
## Getting Started
For a full setup and walkthrough, please see [./doc/guide.md](./doc/guide.md).
### Debian, Ubuntu
### Quick Install
We have a [script](./install.sh) to install code-server for Linux, macOS and FreeBSD.
It tries to use the system package manager if possible.
First run to print out the install process:
```bash
curl -sSOL https://github.com/cdr/code-server/releases/download/3.3.0/code-server_3.3.0_amd64.deb
sudo dpkg -i code-server_3.3.0_amd64.deb
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run
```
### Fedora, Red Hat, SUSE
Now to actually install:
```bash
curl -sSOL https://github.com/cdr/code-server/releases/download/3.3.0/code-server-3.3.0-amd64.rpm
sudo yum install -y code-server-3.3.0-amd64.rpm
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
curl -fsSL https://code-server.dev/install.sh | sh
```
### npm
The install script will print out how to run and start using code-server.
We recommend installing from `npm` if we don't have a precompiled release for your machine's
platform or architecture.
### Manual Install
**note:** Installing via `npm` builds native modules on install and so requires C dependencies.
See [./doc/npm.md](./doc/npm.md) for installing these dependencies.
You will need at least node v12 installed. See [#1633](https://github.com/cdr/code-server/issues/1633).
```bash
npm install -g code-server
code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
### macOS
```bash
brew install code-server
brew services start code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
### Docker
```bash
# This will start a code-server container and expose it at http://127.0.0.1:8080.
# It will also mount your current directory into the container as `/home/coder/project`
# and forward your UID/GID so that all file system operations occur as your user outside
# the container.
docker run -it -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-u "$(id -u):$(id -g)" \
codercom/code-server:latest
```
### Static releases
We publish self contained `.tar.gz` archives for every release on [github](https://github.com/cdr/code-server/releases).
They bundle the node binary and compiled native modules.
1. Download the latest release archive for your system from [github](https://github.com/cdr/code-server/releases).
2. Unpack the release.
3. You can run code-server by executing `./bin/code-server`.
Add the code-server `bin` directory to your `$PATH` to easily execute `code-server` without the full path every time.
Here is an example script for installing and using a static `code-server` release on Linux:
```bash
curl -sSL https://github.com/cdr/code-server/releases/download/3.3.0/code-server-3.3.0-linux-amd64.tar.gz | sudo tar -C /usr/local -xz
sudo mv /usr/local/code-server-3.3.0-linux-amd64 /usr/local/code-server
PATH="$PATH:/usr/local/code-server/bin"
code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
Docs on the install script, manual installation and docker image are at [./doc/install.md](./doc/install.md).
## FAQ
@@ -99,7 +50,16 @@ See [./doc/FAQ.md](./doc/FAQ.md).
See [./doc/CONTRIBUTING.md](./doc/CONTRIBUTING.md).
## Enterprise
## Hiring
Visit [our website](https://coder.com) for more information about our
enterprise offerings.
We ([@cdr](https://github.com/cdr)) are looking for a engineers to help maintain
code-server, innovate on open source and streamline dev workflows.
Our main office is in Austin, Texas. Remote is ok as long as
you're in North America or Europe.
Please get in [touch](mailto:jobs@coder.com) with your resume/github if interested.
## For Organizations
Visit [our website](https://coder.com) for more information about remote development for your organization or enterprise.

22
ThirdPartyNotices.txt Normal file
View File

@@ -0,0 +1,22 @@
code-server
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
Do Not Translate or Localize
1. Microsoft/vscode version 1.47.0 (https://github.com/Microsoft/vscode)
%% Microsoft/vscode NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) 2015 - present Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -14,26 +14,35 @@ Any file or directory in this subdirectory should be documented here.
Make sure you have `$GITHUB_TOKEN` set and [hub](https://github.com/github/hub) installed.
1. Update the version of code-server in `package.json` and README.md/guide.md install examples and push a commit.
1. Update the version of code-server and make a PR.
1. Update in `package.json`
2. Update in [./doc/install.md](../doc/install.md)
2. GitHub actions will generate the `npm-package`, `release-packages` and `release-images` artifacts.
3. Run `yarn release:github-draft` to create a GitHub draft release from the template with
the updated version.
1. Summarize the major changes in the release notes and link to the relevant issues.
4. Wait for the artifacts in step 2 to build.
5. Run `yarn release:github-assets` to download the `release-packages` artifact and then
5. Run `yarn release:github-assets` to download the `release-packages` artifact and
upload them to the draft release.
6. Run some basic sanity tests on one of the released packages.
7. Publish the release.
7. Make sure the github release tag is the commit with the artifacts. This is a bug in
`hub` where uploading assets in step 5 will break the tag.
8. Publish the release and merge the PR.
1. CI will automatically grab the artifacts and then:
1. Publish the NPM package from `npm-package`.
2. Publish the Docker Hub image from `release-images`.
8. Update the homebrew and AUR packages.
9. Update the AUR package.
- Instructions on updating the AUR package are at [cdr/code-server-aur](https://github.com/cdr/code-server-aur).
10. Wait for the npm package to be published.
11. Update the homebrew package.
- Send a pull request to [homebrew-core](https://github.com/Homebrew/homebrew-core) with the URL in the [formula](https://github.com/Homebrew/homebrew-core/blob/master/Formula/code-server.rb) updated.
12. Make sure to add a release without the `v` prefix for autoupdate from `3.2.0`.
## dev
This directory contains scripts used for the development of code-server.
- [./ci/dev/container](./dev/container)
- [./ci/dev/image](./dev/image)
- See [./doc/CONTRIBUTING.md](../doc/CONTRIBUTING.md) for docs on the development container.
- [./ci/dev/fmt.sh](./dev/fmt.sh) (`yarn fmt`)
- Runs formatters.
@@ -67,24 +76,24 @@ You can disable minification by setting `MINIFY=`.
- Builds vscode into `./lib/vscode/out-vscode`.
- [./ci/build/build-release.sh](./build/build-release.sh) (`yarn release`)
- Bundles the output of the above two scripts into a single node module at `./release`.
- [./ci/build/build-static-release.sh](./build/build-static-release.sh) (`yarn release:static`)
- [./ci/build/build-standalone-release.sh](./build/build-standalone-release.sh) (`yarn release:standalone`)
- Requires a node module already built into `./release` with the above script.
- Will build a static release with node and native modules bundled into `./release-static`.
- Will build a standalone release with node and node_modules bundled into `./release-standalone`.
- [./ci/build/clean.sh](./build/clean.sh) (`yarn clean`)
- Removes all build artifacts.
- Will also `git reset --hard lib/vscode`.
- Useful to do a clean build.
- [./ci/build/code-server.sh](./build/code-server.sh)
- Copied into static releases to run code-server with the bundled node binary.
- [./ci/build/test-static-release.sh](./build/test-static-release.sh) (`yarn test:static-release`)
- Ensures code-server in the `./release-static` directory works by installing an extension.
- Copied into standalone releases to run code-server with the bundled node binary.
- [./ci/build/test-standalone-release.sh](./build/test-standalone-release.sh) (`yarn test:standalone-release`)
- Ensures code-server in the `./release-standalone` directory works by installing an extension.
- [./ci/build/build-packages.sh](./build/build-packages.sh) (`yarn package`)
- Packages `./release-static` into a `.tar.gz` archive in `./release-packages`.
- Packages `./release-standalone` into a `.tar.gz` archive in `./release-packages`.
- If on linux, [nfpm](https://github.com/goreleaser/nfpm) is used to generate `.deb` and `.rpm`.
- [./ci/build/nfpm.yaml](./build/nfpm.yaml)
- Used to configure [nfpm](https://github.com/goreleaser/nfpm) to generate `.deb` and `.rpm`.
- [./ci/build/code-server-nfpm.sh](./build/code-server-nfpm.sh)
- Entrypoint script for code-server for `.deb` and .rpm`.
- Entrypoint script for code-server for `.deb` and `.rpm`.
- [./ci/build/code-server.service](./build/code-server.service)
- systemd user service packaged into the `.deb` and `.rpm`.
- [./ci/build/release-github-draft.sh](./build/release-github-draft.sh) (`yarn release:github-draft`)
@@ -97,17 +106,17 @@ You can disable minification by setting `MINIFY=`.
- Post install script for the npm package.
- Bundled by`yarn release`.
## release-container
## release-image
This directory contains the release docker container.
This directory contains the release docker container image.
- [./release-container/build.sh](./release-container/build.sh)
- [./release-image/build.sh](./release-image/build.sh)
- Builds the release container with the tag `codercom/code-server-$ARCH:$VERSION`.
- Assumes debian releases are ready in `./release-packages`.
## container
## images
This directory contains the container for CI.
This directory contains the images for CI.
## steps
@@ -123,9 +132,9 @@ Helps avoid clobbering the CI configuration.
- [./steps/release.sh](./steps/release.sh)
- Runs the release process.
- Generates the npm package at `./release`.
- [./steps/release-static.sh](./steps/release-static.sh)
- Takes the output of the previous script and generates a static release and
release packages into `release-packages`.
- [./steps/release-packages.sh](./steps/release-packages.sh)
- Takes the output of the previous script and generates a standalone release and
release packages into `./release-packages`.
- [./steps/publish-npm.sh](./steps/publish-npm.sh)
- Grabs the `npm-package` release artifact for the current commit and publishes it on npm.
- [./steps/build-docker-image.sh](./steps/build-docker-image.sh)

View File

@@ -9,7 +9,7 @@ MINIFY=${MINIFY-true}
main() {
cd "$(dirname "${0}")/../.."
npx tsc --outDir out --tsBuildInfoFile .cache/out.tsbuildinfo
tsc --outDir out --tsBuildInfoFile .cache/out.tsbuildinfo
# If out/node/entry.js does not already have the shebang,
# we make sure to add it and make it executable.
if ! grep -q -m1 "^#!/usr/bin/env node" out/node/entry.js; then
@@ -17,11 +17,10 @@ main() {
chmod +x out/node/entry.js
fi
npx parcel build \
parcel build \
--public-url "/static/$(git rev-parse HEAD)/dist" \
--out-dir dist \
$([[ $MINIFY ]] || echo --no-minify) \
src/browser/pages/app.ts \
src/browser/register.ts \
src/browser/serviceWorker.ts
}

View File

@@ -2,28 +2,47 @@
set -euo pipefail
# Packages code-server for the current OS and architecture into ./release-packages.
# This script assumes that a static release is built already into ./release-static.
# This script assumes that a standalone release is built already into ./release-standalone
main() {
cd "$(dirname "${0}")/../.."
source ./ci/lib.sh
local release_name="code-server-$VERSION-$OS-$ARCH"
mkdir -p release-packages
release_archive
# Will stop the auto update issues and allow people to upgrade their scripts
# for the new release structure.
if [[ $ARCH == "amd64" ]]; then
if [[ $OS == "linux" ]]; then
ARCH=x86_64 release_archive
elif [[ $OS == "macos" ]]; then
OS=darwin ARCH=x86_64 release_archive
fi
fi
if [[ $OS == "linux" ]]; then
tar -czf "release-packages/$release_name.tar.gz" --transform "s/^\.\/release-static/$release_name/" ./release-static
release_nfpm
fi
}
release_archive() {
local release_name="code-server-$VERSION-$OS-$ARCH"
if [[ $OS == "linux" ]]; then
tar -czf "release-packages/$release_name.tar.gz" --transform "s/^\.\/release-standalone/$release_name/" ./release-standalone
elif [[ $OS == "darwin" && $ARCH == "x86_64" ]]; then
# Just exists to make autoupdating from 3.2.0 work again.
mv ./release-standalone "./$release_name"
zip -r "release-packages/$release_name.zip" "./$release_name"
mv "./$release_name" ./release-standalone
return
else
tar -czf "release-packages/$release_name.tar.gz" -s "/^release-static/$release_name/" release-static
tar -czf "release-packages/$release_name.tar.gz" -s "/^release-standalone/$release_name/" release-standalone
fi
echo "done (release-packages/$release_name)"
release_gcp
if [[ $OSTYPE == linux* ]]; then
release_nfpm
fi
}
release_gcp() {
@@ -36,10 +55,10 @@ release_gcp() {
# Generates deb and rpm packages.
release_nfpm() {
local nfpm_config
nfpm_config=$(envsubst < ./ci/build/nfpm.yaml)
nfpm_config="$(envsubst < ./ci/build/nfpm.yaml)"
# The underscores are convention for .deb.
nfpm pkg -f <(echo "$nfpm_config") --target "release-packages/code-server_${VERSION}_${ARCH}.deb"
nfpm pkg -f <(echo "$nfpm_config") --target "release-packages/code-server_${VERSION}_$ARCH.deb"
nfpm pkg -f <(echo "$nfpm_config") --target "release-packages/code-server-$VERSION-$ARCH.rpm"
}

View File

@@ -49,11 +49,14 @@ EOF
bundle_vscode() {
mkdir -p "$VSCODE_OUT_PATH"
rsync "$VSCODE_SRC_PATH/package.json" "$VSCODE_OUT_PATH"
rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH"
rsync "$VSCODE_SRC_PATH/node_modules" "$VSCODE_OUT_PATH"
rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY+-min}/" "$VSCODE_OUT_PATH/out"
rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions"
rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules"
rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions"
rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions"
rsync "$VSCODE_SRC_PATH/extensions/postinstall.js" "$VSCODE_OUT_PATH/extensions"
mkdir -p "$VSCODE_OUT_PATH/resources/linux"
rsync "$VSCODE_SRC_PATH/resources/linux/code.png" "$VSCODE_OUT_PATH/resources/linux/code.png"
@@ -68,26 +71,10 @@ bundle_vscode() {
EOF
) > "$VSCODE_OUT_PATH/product.json"
pushd "$VSCODE_OUT_PATH"
yarn --production --frozen-lockfile --ignore-scripts
popd
# We clear any native module builds.
local native_modules
mapfile -t native_modules < <(find "$VSCODE_OUT_PATH/node_modules" -name "binding.gyp" -exec dirname {} \;)
local nm
for nm in "${native_modules[@]}"; do
rm -R "$nm/build"
done
# We have to rename node_modules to node_modules.bundled to avoid them being ignored by yarn.
local node_modules
mapfile -t node_modules < <(find "$VSCODE_OUT_PATH" -depth -name "node_modules")
local nm
for nm in "${node_modules[@]}"; do
rm -Rf "$nm.bundled"
mv "$nm" "$nm.bundled"
done
# We remove the scripts field so that later on we can run
# yarn to fetch node_modules if necessary without build scripts running.
# We cannot use --no-scripts because we still want dependent package scripts to run.
jq 'del(.scripts)' < "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json"
}
main "$@"

View File

@@ -5,8 +5,8 @@ main() {
cd "$(dirname "${0}")/../.."
source ./ci/lib.sh
rsync "$RELEASE_PATH/" "$RELEASE_PATH-static"
RELEASE_PATH+=-static
rsync "$RELEASE_PATH/" "$RELEASE_PATH-standalone"
RELEASE_PATH+=-standalone
# We cannot find the path to node from $PATH because yarn shims a script to ensure
# we use the same version it's using so we instead run a script with yarn that
@@ -18,6 +18,9 @@ main() {
rsync ./ci/build/code-server.sh "$RELEASE_PATH/bin/code-server"
rsync "$node_path" "$RELEASE_PATH/lib/node"
ln -s "./bin/code-server" "$RELEASE_PATH/code-server"
ln -s "./lib/node" "$RELEASE_PATH/node"
cd "$RELEASE_PATH"
yarn --production --frozen-lockfile
}

View File

@@ -8,9 +8,10 @@ main() {
rm -Rf \
out \
release \
release-static \
release-standalone \
release-packages \
release-gcp \
release-images/ \
dist \
.tsbuildinfo \
.cache/out.tsbuildinfo

View File

@@ -1,20 +1,36 @@
#!/usr/bin/env sh
#!/bin/sh
set -eu
# This script is intended to be bundled into the static releases.
# Runs code-server with the bundled Node binary.
# This script is intended to be bundled into the standalone releases.
# 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
bin_dir() {
# We read the symlink, which may be relative from $0.
dst="$(readlink "$0")"
# We cd into the $0 directory.
cd "$(dirname "$0")" || exit 1
# Now we can cd into the dst directory.
cd "$(dirname "$dst")" || exit 1
# Finally we use pwd -P to print the absolute path of the directory of $dst.
pwd -P || exit 1
_realpath() {
# See https://github.com/cdr/code-server/issues/1537 on why no realpath or readlink -f.
script="$1"
cd "$(dirname "$script")"
while [ -L "$(basename "$script")" ]; do
if [ -L "./node" ] && [ -L "./code-server" ] &&
[ -f "package.json" ] &&
cat package.json | grep -q '^ "name": "code-server",$'; then
echo "***** Please use the script in bin/code-server instead!" >&2
echo "***** This script will soon be removed!" >&2
echo "***** See the release notes at https://github.com/cdr/code-server/releases/tag/v3.4.0" >&2
fi
script="$(readlink "$(basename "$script")")"
cd "$(dirname "$script")"
done
echo "$PWD/$(basename "$script")"
}
BIN_DIR=$(bin_dir)
exec "$BIN_DIR/../lib/node" "$BIN_DIR/.." "$@"
root() {
script="$(_realpath "$0")"
bin_dir="$(dirname "$script")"
dirname "$bin_dir"
}
ROOT="$(root)"
exec "$ROOT/lib/node" "$ROOT" "$@"

View File

@@ -10,8 +10,7 @@ description: |
vendor: "Coder"
homepage: "https://github.com/cdr/code-server"
license: "MIT"
bindir: "/usr/bin"
files:
./ci/build/code-server-nfpm.sh: /usr/bin/code-server
./ci/build/code-server.service: /usr/lib/systemd/user/code-server.service
./release-static/**/*: "/usr/lib/code-server/"
./release-standalone/**/*: "/usr/lib/code-server/"

View File

@@ -24,24 +24,18 @@ main() {
;;
esac
cd lib/vscode
# We have to rename node_modules.bundled to node_modules.
# The bundled modules were renamed originally to avoid being ignored by yarn.
node_modules="$(find . -depth -name "node_modules.bundled")"
for nm in $node_modules; do
rm -Rf "${nm%.bundled}"
mv "$nm" "${nm%.bundled}"
done
# $npm_config_global makes npm rebuild return without rebuilding.
unset npm_config_global
# Rebuilds native modules.
if ! npm rebuild; then
if ! vscode_yarn; then
echo "You may not have the required dependencies to build the native modules."
echo "Please see https://github.com/cdr/code-server/blob/master/doc/npm.md"
exit 1
fi
}
vscode_yarn() {
cd lib/vscode
yarn --production --frozen-lockfile
cd extensions
yarn --production --frozen-lockfile
}
main "$@"

View File

@@ -11,7 +11,7 @@ main() {
source ./ci/lib.sh
download_artifact release-packages ./release-packages
local assets=(./release-packages/*)
local assets=(./release-packages/code-server*"$VERSION"*{.tar.gz,.zip,.deb,.rpm})
for i in "${!assets[@]}"; do
assets[$i]="--attach=${assets[$i]}"
done

View File

@@ -9,6 +9,7 @@ main() {
hub release create \
--file - \
-t "$(git rev-parse HEAD)" \
--draft "${assets[@]}" "v$VERSION" << EOF
v$VERSION

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
# Makes sure the release works.
# This is to make sure we don't have Node version errors or any other
# compilation-related errors.
main() {
cd "$(dirname "${0}")/../.."
local EXTENSIONS_DIR
EXTENSIONS_DIR="$(mktemp -d)"
echo "Testing standalone release."
./release-standalone/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --install-extension ms-python.python
local installed_extensions
installed_extensions="$(./release-standalone/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --list-extensions 2>&1)"
if [[ $installed_extensions != *"info Using config file ~/.config/code-server/config.yaml
ms-python.python" ]]; then
echo "Unexpected output from listing extensions:"
echo "$installed_extensions"
exit 1
fi
echo "Standalone release works correctly."
}
main "$@"

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Makes sure the release works.
# This is to make sure we don't have Node version errors or any other
# compilation-related errors.
main() {
cd "$(dirname "${0}")/../.."
local EXTENSIONS_DIR
EXTENSIONS_DIR="$(mktemp -d)"
echo "Testing static release"
./release-static/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --install-extension ms-python.python
local installed_extensions
installed_extensions="$(./release-static/bin/code-server --extensions-dir "$EXTENSIONS_DIR" --list-extensions 2>&1)"
if [[ $installed_extensions != "ms-python.python" ]]; then
echo "Unexpected output from listing extensions:"
echo "$installed_extensions"
exit 1
fi
echo "Static release works correctly"
}
main "$@"

View File

@@ -1,43 +0,0 @@
FROM debian
RUN apt-get update
# Needed for debian repositories added below.
RUN apt-get install -y curl gnupg
# Installs node.
RUN curl -sSL https://deb.nodesource.com/setup_14.x | bash - && \
apt-get install -y nodejs
# Installs yarn.
RUN curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn
# Installs VS Code build deps.
RUN apt-get install -y build-essential \
libsecret-1-dev \
libx11-dev \
libxkbfile-dev
# Installs envsubst.
RUN apt-get install -y gettext-base
# Misc build dependencies.
RUN apt-get install -y jq git rsync
# Installs shellcheck.
RUN curl -sSL https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.$(uname -m).tar.xz | \
tar -xJ && \
mv shellcheck*/shellcheck /usr/local/bin && \
rm -R shellcheck*
# Install Go dependencies
RUN ARCH="$(dpkg --print-architecture)" && \
curl -sSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz
ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH
ENV GO111MODULE=on
RUN go get mvdan.cc/sh/v3/cmd/shfmt
RUN go get github.com/goreleaser/nfpm/cmd/nfpm
RUN curl -fsSL https://get.docker.com | sh

View File

@@ -4,7 +4,7 @@ set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
shfmt -i 2 -w -s -sr $(git ls-files "*.sh")
shfmt -i 2 -w -sr $(git ls-files "*.sh")
local prettierExts
prettierExts=(
@@ -23,6 +23,9 @@ main() {
doctoc --title '# FAQ' doc/FAQ.md > /dev/null
doctoc --title '# Setup Guide' doc/guide.md > /dev/null
doctoc --title '# Install' doc/install.md > /dev/null
doctoc --title '# npm Install Requirements' doc/npm.md > /dev/null
doctoc --title '# Contributing' doc/CONTRIBUTING.md > /dev/null
if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then
echo "Files need generation or are formatted incorrectly:"

View File

@@ -42,7 +42,7 @@ run() {
build() {
echo "--- Building $container_name"
docker build -t $container_name ./ci/dev/container > /dev/null
docker build -t $container_name ./ci/dev/image > /dev/null
}
main "$@"

View File

@@ -7,7 +7,10 @@ main() {
eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js")
stylelint $(git ls-files "*.css")
tsc --noEmit
shellcheck -e SC2046,SC2164,SC2154 $(git ls-files "*.sh")
# See comment in ./ci/image/debian8
if [[ ! ${CI-} ]]; then
shellcheck -e SC2046,SC2164,SC2154,SC1091,SC1090,SC2002 $(git ls-files "*.sh")
fi
}
main "$@"

File diff suppressed because it is too large Load Diff

View File

@@ -144,11 +144,7 @@ class Watcher {
private createBundler(out = "dist"): 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"),
],
[path.join(this.rootPath, "src/browser/register.ts"), path.join(this.rootPath, "src/browser/serviceWorker.ts")],
{
outDir: path.join(this.rootPath, out),
cacheDir: path.join(this.rootPath, ".cache"),

View File

@@ -0,0 +1,26 @@
FROM centos:7
RUN ARCH="$(uname -m | sed 's/86_64/64/; s/aarch64/arm64/')" && \
curl -fsSL "https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-$ARCH.tar.xz" | tar -C /usr/local -xJ && \
mv /usr/local/node-v14.4.0-linux-$ARCH /usr/local/node-v14.4.0
ENV PATH=/usr/local/node-v14.4.0/bin:$PATH
RUN npm install -g yarn
RUN yum groupinstall -y 'Development Tools'
RUN yum install -y python2 libsecret-devel libX11-devel libxkbfile-devel
RUN npm config set python python2
RUN yum install -y epel-release && yum install -y jq
RUN yum install -y rsync
# Copied from ../debian8/Dockerfile
# Install Go dependencies
RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \
curl -fsSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz
ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH
ENV GO111MODULE=on
RUN go get mvdan.cc/sh/v3/cmd/shfmt
RUN go get github.com/goreleaser/nfpm/cmd/nfpm
RUN curl -fsSL https://get.docker.com | sh

View File

@@ -0,0 +1,53 @@
FROM debian:8
RUN apt-get update
# Needed for debian repositories added below.
RUN apt-get install -y curl gnupg
# Installs node.
RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - && \
apt-get install -y nodejs
# Installs yarn.
RUN curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn
# Installs VS Code build deps.
RUN apt-get install -y build-essential \
libsecret-1-dev \
libx11-dev \
libxkbfile-dev
# Installs envsubst.
RUN apt-get install -y gettext-base
# Misc build dependencies.
RUN apt-get install -y git rsync unzip
# We need latest jq from debian buster for date support.
RUN ARCH="$(dpkg --print-architecture)" && \
curl -fsSOL http://http.us.debian.org/debian/pool/main/libo/libonig/libonig5_6.9.1-1_$ARCH.deb && \
dpkg -i libonig*.deb && \
curl -fsSOL http://http.us.debian.org/debian/pool/main/j/jq/libjq1_1.5+dfsg-2+b1_$ARCH.deb && \
dpkg -i libjq*.deb && \
curl -fsSOL http://http.us.debian.org/debian/pool/main/j/jq/jq_1.5+dfsg-2+b1_$ARCH.deb && \
dpkg -i jq*.deb && rm *.deb
# Installs shellcheck.
# Unfortunately coredumps on debian:8 so disabled for now.
#RUN curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.$(uname -m).tar.xz | \
# tar -xJ && \
# mv shellcheck*/shellcheck /usr/local/bin && \
# rm -R shellcheck*
# Install Go dependencies
RUN ARCH="$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && \
curl -fsSL "https://dl.google.com/go/go1.14.3.linux-$ARCH.tar.gz" | tar -C /usr/local -xz
ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH
ENV GO111MODULE=on
RUN go get mvdan.cc/sh/v3/cmd/shfmt
RUN go get github.com/goreleaser/nfpm/cmd/nfpm
RUN curl -fsSL https://get.docker.com | sh

View File

@@ -56,14 +56,14 @@ curl() {
# This will contain the artifacts we want.
# https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs
get_artifacts_url() {
curl -sSL 'https://api.github.com/repos/cdr/code-server/actions/workflows/ci.yaml/runs?status=success&event=push' | jq -r ".workflow_runs[] | select(.head_sha == \"$(git rev-parse HEAD)\") | .artifacts_url" | head -n 1
curl -fsSL 'https://api.github.com/repos/cdr/code-server/actions/workflows/ci.yaml/runs?status=success&event=push' | jq -r ".workflow_runs[] | select(.head_sha == \"$(git rev-parse HEAD)\") | .artifacts_url" | head -n 1
}
# Grabs the artifact's download url.
# https://developer.github.com/v3/actions/artifacts/#list-workflow-run-artifacts
get_artifact_url() {
local artifact_name="$1"
curl -sSL "$(get_artifacts_url)" | jq -r ".artifacts[] | select(.name == \"$artifact_name\") | .archive_download_url" | head -n 1
curl -fsSL "$(get_artifacts_url)" | jq -r ".artifacts[] | select(.name == \"$artifact_name\") | .archive_download_url" | head -n 1
}
# Uses the above two functions to download a artifact into a directory.
@@ -74,7 +74,7 @@ download_artifact() {
local tmp_file
tmp_file="$(mktemp)"
curl -sSL "$(get_artifact_url "$artifact_name")" > "$tmp_file"
curl -fsSL "$(get_artifact_url "$artifact_name")" > "$tmp_file"
unzip -q -o "$tmp_file" -d "$dst"
rm "$tmp_file"
}

View File

@@ -28,7 +28,7 @@ RUN adduser --gecos '' --disabled-password coder && \
echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
RUN ARCH="$(dpkg --print-architecture)" && \
curl -sSL "https://github.com/boxboat/fixuid/releases/download/v0.4.1/fixuid-0.4.1-linux-$ARCH.tar.gz" | tar -C /usr/local/bin -xzf - && \
curl -fsSL "https://github.com/boxboat/fixuid/releases/download/v0.4.1/fixuid-0.4.1-linux-$ARCH.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 && \

View File

@@ -5,7 +5,7 @@ main() {
cd "$(dirname "$0")/../.."
source ./ci/lib.sh
docker build -t "codercom/code-server-$ARCH:$VERSION" -f ./ci/release-container/Dockerfile .
docker build -t "codercom/code-server-$ARCH:$VERSION" -f ./ci/release-image/Dockerfile .
}
main "$@"

View File

@@ -5,7 +5,7 @@ main() {
cd "$(dirname "$0")/../.."
source ./ci/lib.sh
./ci/release-container/build.sh
./ci/release-image/build.sh
mkdir -p release-images
docker save "codercom/code-server-$ARCH:$VERSION" > "release-images/code-server-$ARCH-$VERSION.tar"

View File

@@ -9,9 +9,9 @@ main() {
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
fi
download_artifact npm-package ./release
download_artifact npm-package ./release-npm-package
# https://github.com/actions/upload-artifact/issues/38
chmod +x $(grep -rl '^#!/.*' release)
tar -xzf release-npm-package/package.tar.gz
yarn publish --non-interactive release
}

20
ci/steps/release-packages.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
if [[ $OSTYPE == darwin* ]]; then
curl -L https://nodejs.org/dist/v14.4.0/node-v14.4.0-darwin-x64.tar.gz | tar -xz
PATH="$PWD/node-v14.4.0-darwin-x64/bin:$PATH"
fi
# https://github.com/actions/upload-artifact/issues/38
tar -xzf release-npm-package/package.tar.gz
yarn release:standalone
yarn test:standalone-release
yarn package
}
main "$@"

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
# https://github.com/actions/upload-artifact/issues/38
chmod +x $(grep -rl '^#!/.*' release)
yarn release:static
yarn test:static-release
yarn package
}
main "$@"

View File

@@ -9,6 +9,10 @@ main() {
yarn build
yarn build:vscode
yarn release
# https://github.com/actions/upload-artifact/issues/38
mkdir -p release-npm-package
tar -czf release-npm-package/package.tar.gz release
}
main "$@"

View File

@@ -1,5 +1,15 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
# Contributing
- [Requirements](#requirements)
- [Development Workflow](#development-workflow)
- [Build](#build)
- [Structure](#structure)
- [VS Code Patch](#vs-code-patch)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
- [Detailed CI and build process docs](../ci)
## Requirements
@@ -9,8 +19,9 @@ Please refer to [VS Code's prerequisites](https://github.com/Microsoft/vscode/wi
Differences:
- We require a minimum of node v12 but later versions should work.
- We use [fnpm](https://github.com/goreleaser/nfpm) to build `.deb` and `.rpm` packages.
- The [CI container](../ci/container/Dockerfile) is a useful reference for all our dependencies.
- We use [nfpm](https://github.com/goreleaser/nfpm) to build `.deb` and `.rpm` packages.
- We use [jq](https://stedolan.github.io/jq/) to build code-server releases.
- The [CI container](../ci/images/debian8/Dockerfile) is a useful reference for all our dependencies.
## Development Workflow
@@ -24,7 +35,7 @@ yarn watch
To develop inside of an isolated docker container:
```shell
./ci/dev/container/exec.sh
./ci/dev/image/exec.sh
root@12345:/code-server# yarn
root@12345:/code-server# yarn vscode
@@ -50,14 +61,14 @@ yarn --production
node .
```
Now you can make it static and build packages with:
Now you can build release packages with:
```
yarn release:static
yarn test:static-release
yarn release:standalone
# The standalone release is in ./release-standalone
yarn test:standalone-release
yarn package
# The static release is in ./release-static
# .deb, .rpm and the static archive are in ./release-packages
# .deb, .rpm and the standalone archive are in ./release-packages
```
## Structure

View File

@@ -3,21 +3,27 @@
# FAQ
- [Questions?](#questions)
- [What's the deal with extensions?](#whats-the-deal-with-extensions)
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
- [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url)
- [Where are extensions stored?](#where-are-extensions-stored)
- [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces)
- [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet)
- [How do I securely access web services?](#how-do-i-securely-access-web-services)
- [Sub-domains](#sub-domains)
- [Sub-paths](#sub-paths)
- [Sub-domains](#sub-domains)
- [Multi-tenancy](#multi-tenancy)
- [Docker in code-server docker container?](#docker-in-code-server-docker-container)
- [Collaboration](#collaboration)
- [Docker in code-server container?](#docker-in-code-server-container)
- [How can I disable telemetry?](#how-can-i-disable-telemetry)
- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open)
- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server)
- [Heartbeat file](#heartbeat-file)
- [Heartbeat File](#heartbeat-file)
- [How does the config file work?](#how-does-the-config-file-work)
- [Blank screen on iPad?](#blank-screen-on-ipad)
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work)
- [Differences compared to Theia?](#differences-compared-to-theia)
- [Enterprise](#enterprise)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -26,25 +32,47 @@
Please file all questions and support requests at https://www.reddit.com/r/codeserver/.
The issue tracker is **only** for bugs.
The issue tracker is **only** for bugs and features.
## What's the deal with extensions?
## How can I reuse my VS Code configuration?
Unfortunately, the Microsoft VS Code Marketplace license prohibits use with any non Microsoft
product.
The very popular [Settings Sync](https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync) extension works.
See https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf
You can also pass `--user-data-dir ~/.vscode` to reuse your existing VS Code extensions and configuration.
Or copy `~/.vscode` into `~/.local/share/code-server`.
## Differences compared to VS Code?
`code-server` takes the open source core of VS Code and allows you to run it in the browser.
However, it is not entirely equivalent to Microsoft's VS Code.
While the core of VS Code is open source, the marketplace and many published Microsoft extensions are not.
Furthermore, Microsoft prohibits the use of any non-Microsoft VS Code from accessing their marketplace.
See the [TOS](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.
As a result, we cannot offer any extensions on the Microsoft marketplace. Instead,
we have created our 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.
These are the closed source extensions presently unavailable:
1. [Live Share](https://visualstudio.microsoft.com/services/live-share)
- We may implement something similar, see [#33](https://github.com/cdr/code-server/issues/33)
1. [Remote Extensions (SSH, Containers, WSL)](https://github.com/microsoft/vscode-remote-release)
- We may reimplement these at some point, see [#1315](https://github.com/cdr/code-server/issues/1315)
For more about the closed source parts of VS Code, see [vscodium/vscodium](https://github.com/VSCodium/vscodium#why-does-this-exist).
## How can I request a missing extension?
Please open a new issue and select the `Extension request` template.
If an extension is not available or does not work, you can grab its VSIX from its Github releases or
build it yourself. Then run the `Extensions: Install from VSIX` command in the Command Palette and
@@ -52,10 +80,26 @@ point to the .vsix file.
See below for installing an extension from the cli.
Feel free to file an issue to add a missing extension to the marketplace.
## How do I configure the marketplace URL?
If you have your own custom marketplace, it is possible to point code-server to it by setting
`$SERVICE_URL` and `$ITEM_URL` to point to it.
If you have your own marketplace that implements the VS Code Extension Gallery API, it is possible to
point code-server to it by setting `$SERVICE_URL` and `$ITEM_URL`. These correspond directly
to `serviceUrl` and `itemUrl` in VS Code's `product.json`.
e.g. to use [open-vsx.org](https://open-vsx.org):
```bash
export SERVICE_URL=https://open-vsx.org/vscode/gallery
export ITEM_URL=https://open-vsx.org/vscode/item
```
While you can technically use Microsoft's marketplace with these, please do not do so as it
is against their terms of use. See [above](#differences-compared-to-vs-code) and this
discussion regarding the use of the Microsoft URLs in forks:
https://github.com/microsoft/vscode/issues/31168#issue-244533026
These variables are most valuable to our enterprise customers for whom we have a self hosted marketplace product.
## Where are extensions stored?
@@ -79,11 +123,11 @@ code-server --install-extension downloaded-ms-python.python.vsix
VS Code Codespaces is a closed source and paid service by Microsoft. It also allows you to access
VS Code via the browser.
However, code-server is free, open source and can be ran on any machine without any limitations.
However, code-server is free, open source and can be run on any machine without any limitations.
While you can self host environments with VS Code Codespaces, you still need to an Azure billing
account and you access VS Code via the Codespaces web dashboard instead of directly connecting to
your instance.
While you can self host environments with VS Code Codespaces, you still need an Azure billing
account and you have to access VS Code via the Codespaces web dashboard instead of directly
connecting to your instance.
## How should I expose code-server to the internet?
@@ -94,20 +138,20 @@ code-server only supports password authentication natively.
**note**: code-server will rate limit password authentication attempts at 2 a minute and 12 an hour.
If you want to use external authentication (i.e sign in with Google) you should handle this
with a reverse proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy).
with a reverse proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy)
or [Cloudflare Access](https://teams.cloudflare.com/access).
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`.
the 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.
requests and will redirect all HTTP requests to HTTPS.
You can use [Let's Encrypt](https://letsencrypt.org/) to get an SSL certificate
You can use [Let's Encrypt](https://letsencrypt.org/) to get a TLS certificate
for free.
Again, Please follow [./guide.md](./guide.md) for our recommendations on setting up and using code-server.
Again, please follow [./guide.md](./guide.md) for our recommendations on setting up and using code-server.
## How do I securely access web services?
@@ -115,6 +159,10 @@ 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-paths
Just browse to `/proxy/<port>/`.
### Sub-domains
You will need a DNS entry that points to your server for each port you want to
@@ -134,35 +182,22 @@ 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>/`.
## Multi-tenancy
If you want to run multiple code-server's on shared infrastructure, we recommend using virtual
If you want to run multiple code-servers 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.
user a virtual machine instead of just a container.
## Docker in code-server docker container?
## Docker in code-server 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.
If you'd like to access docker inside of code-server, mount the docker socket in from `/var/run/docker.sock`.
Install the docker CLI in the code-server container and you should be able to access the daemon!
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
We understand the high demand but the team is swamped right now.
You can follow progress at [#33](https://github.com/cdr/code-server/issues/33).
You can even make volume mounts work. Lets say you want to run a container and mount in
`/home/coder/myproject` into it from inside the `code-server` container. You need to make sure
the docker daemon's `/home/coder/myproject` is the same as the one mounted inside the `code-server`
container and the mount will just work.
## How can I disable telemetry?
@@ -192,20 +227,20 @@ 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 `~/.local/share/code-server/logs` directory
2. The most recently created directory in the `~/.local/share/code-server/logs` directory.
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.
## Heartbeat file
## Heartbeat File
`code-server` touches `~/.local/share/code-server/heartbeat` once a minute as long
as there is an active browser connection.
If you want to shutdown `code-server` if there hasn't been an active connection in X minutes
you can do so by continuously checking the last modified time on the heartbeat file and if it is
older than X minutes, you should kill `code-server`.
older than X minutes, kill `code-server`.
[#1636](https://github.com/cdr/code-server/issues/1636) will make the experience here better.
@@ -229,6 +264,47 @@ and no TLS. Any flags passed to `code-server` will take priority over the config
The `--config` flag or `$CODE_SERVER_CONFIG` can be used to change the config file's location.
The default location also respects `$XDG_CONFIG_HOME`.
## Blank screen on iPad?
Unfortunately at the moment self signed certificates cause a blank screen on iPadOS
There does seem to be a way to get it to work if you create your own CA and create a
certificate using the CA and then import the CA onto your iPad.
See [#1566](https://github.com/cdr/code-server/issues/1566#issuecomment-623159434).
## Isn't an install script piped into sh insecure?
Please give
[this wonderful blogpost](https://sandstorm.io/news/2015-09-24-is-curl-bash-insecure-pgp-verified-install) by
[sandstorm.io](https://sandstorm.io) a read.
## How do I make my keyboard shortcuts work?
Many shortcuts will not work by default as they'll be caught by the browser.
If you use Chrome you can get around this by installing the PWA.
Once you've entered the editor, click the "plus" icon present in the URL toolbar area.
This will install a Chrome PWA and now all keybindings will work!
For other browsers you'll have to remap keybindings unfortunately.
## Differences compared to Theia?
[Theia](https://github.com/eclipse-theia/theia) is a browser IDE loosely based on VS Code. It uses the same
text editor library named [Monaco](https://github.com/Microsoft/monaco-editor) and the same
extension API but everything else is very different. It also uses [open-vsx.org](https://open-vsx.org)
for extensions which has an order of magnitude less extensions than our marketplace.
See [#1473](https://github.com/cdr/code-server/issues/1473).
You can't just use your VS Code config in Theia like you can with code-server.
To summarize, code-server is a patched fork of VS Code to run in the browser whereas
Theia takes some parts of VS Code but is an entirely different editor.
## Enterprise
Visit [our enterprise page](https://coder.com) for more information about our

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

BIN
doc/assets/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

View File

@@ -9,23 +9,31 @@
- [3. Expose code-server](#3-expose-code-server)
- [SSH forwarding](#ssh-forwarding)
- [Let's Encrypt](#lets-encrypt)
- [NGINX](#nginx)
- [Self Signed Certificate](#self-signed-certificate)
- [Change the password?](#change-the-password)
- [How do I securely access development web services?](#how-do-i-securely-access-development-web-services)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
This guide demonstrates how to setup and use code-server.
To reiterate, code-server lets you run VS Code on a remote server and then access it via a browser.
This guide demonstrates how to setup and use `code-server`.
To reiterate, `code-server` lets you run VS Code on a remote server and then access it via a browser.
See [README.md](../README.md) for a general overview and [FAQ.md](./FAQ.md) for further user docs.
Further docs are at:
We'll walk you through acquiring a remote machine to run code-server on and then exposing `code-server` so you can
securely access it.
- [README](../README.md) for a general overview
- [INSTALL](../doc/install.md) for installation
- [FAQ](./FAQ.md) for common questions.
- [CONTRIBUTING](../doc/CONTRIBUTING.md) for development docs
We highly recommend reading the [FAQ](./FAQ.md) on the [Differences compared to VS Code](./FAQ.md#differences-compared-to-vs-code) before beginning.
We'll walk you through acquiring a remote machine to run `code-server` on
and then exposing `code-server` so you can securely access it.
## 1. Acquire a remote machine
First, you need a machine to run code-server on. You can use a physical
First, you need a machine to run `code-server` on. You can use a physical
machine you have lying around or use a VM on GCP/AWS.
### Requirements
@@ -56,64 +64,71 @@ Once you've signed up and created a GCP project, create a new Compute Engine VM
- Change the type to custom and set at least 2 cores and 2 GB of ram.
- Add more vCPUs and memory as you prefer, you can edit after creating the instance as well.
- https://cloud.google.com/compute/docs/machine-types#general_purpose
7. We highly recommend switching the persistent disk to a SSD of at least 32 GB.
7. We highly recommend switching the persistent disk to an SSD of at least 32 GB.
- Click `Change` under `Boot Disk` and change the type to `SSD Persistent Disk` and the size
to `32`.
- You can always grow your disk later.
- The default OS of Debian 10 is fine.
8. Navigate to `Networking -> Network interfaces` and edit the existing interface
to use a static external IP.
- Click done to save network interface changes.
9. If you do not have a [project wide SSH key](https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys#project-wide), navigate to `Security - > SSH Keys` and add your public key there.
9. If you do not have a [project wide SSH key](https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys#project-wide), navigate to `Security -> SSH Keys` and add your public key there.
10. Click create!
Remember, you can shutdown your server when not in use to lower costs.
We highly recommend learning to use the [`gcloud`](https://cloud.google.com/sdk/gcloud) cli
to avoid the slow dashboard.
## 2. Install code-server
SSH into your instance and run the appropriate commands documented in [README.md](../README.md).
We have a [script](../install.sh) to install `code-server` for Linux, macOS and FreeBSD.
Assuming Debian:
It tries to use the system package manager if possible.
First run to print out the install process:
```bash
curl -sSOL https://github.com/cdr/code-server/releases/download/3.3.0/code-server_3.3.0_amd64.deb
sudo dpkg -i code-server_3.3.0_amd64.deb
systemctl --user enable --now code-server
# Now code-server is running at http://127.0.0.1:8080
# Your password is in ~/.config/code-server/config.yaml
curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run
```
Now to actually install:
```bash
curl -fsSL https://code-server.dev/install.sh | sh
```
The install script will print out how to run and start using `code-server`.
Docs on the install script, manual installation and docker image are at [./install.md](./install.md).
## 3. Expose code-server
**Never**, **ever** expose `code-server` directly to the internet without some form of authentication
and encryption as someone can completely takeover your machine with the terminal.
There are several approaches to securely operating and exposing code-server.
By default, `code-server` will enable password authentication which will require you to copy the
password from the`code-server`config file to login. It will listen on`localhost` to avoid exposing
itself to the world. This is fine for testing but will not work if you want to access `code-server`
from a different machine.
By default, code-server will enable password authentication which will
require you to copy the password from the code-server config file to login. You
can also set a custom password with `$PASSWORD`.
There are several approaches to securely operating and exposing `code-server`.
**tip**: You can list the full set of code-server options with `code-server --help`
**tip**: You can list the full set of `code-server` options with `code-server --help`
### SSH forwarding
We highly recommend this approach for not requiring any additional setup, you just need an
SSH server on your remote machine. The downside is you won't be able to access `code-server`
without an SSH client like an iPad. If that's important to you, skip to [Let's Encrypt](#lets-encrypt).
on any machine without an SSH client like on iPad. If that's important to you, skip to [Let's Encrypt](#lets-encrypt).
Recommended reading: https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding.
First, ssh into your instance and edit your code-server config file to disable password authentication.
First, ssh into your instance and edit your `code-server` config file to disable password authentication.
```bash
# Replaces "auth: password" with "auth: none" in the code-server config.
sed -i.bak 's/auth: password/auth: none/' ~/.config/code-server/config.yaml
```
Restart code-server with (assuming you followed the guide):
Restart `code-server` with (assuming you followed the guide):
```bash
systemctl --user restart code-server
@@ -121,18 +136,20 @@ systemctl --user restart code-server
Now forward local port 8080 to `127.0.0.1:8080` on the remote instance.
Recommended reading: https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding.
```bash
# -N disables executing a remote shell
ssh -N -L 8080:127.0.0.1:8080 <instance-ip>
```
Now if you access http://127.0.0.1:8080 locally, you should see code-server!
Now if you access http://127.0.0.1:8080 locally, you should see `code-server`!
If you want to make the SSH port forwarding persistent we recommend using
[mutagen](https://mutagen.io/documentation/introduction/installation).
```
# Same as the above SSH command but runs in the background continously.
# Same as the above SSH command but runs in the background continuously.
# Add `mutagen daemon start` to your ~/.bashrc to start the mutagen daemon when you open a shell.
mutagen forward create --name=code-server tcp:127.0.0.1:8080 <instance-ip>:tcp:127.0.0.1:8080
```
@@ -145,16 +162,16 @@ ServerAliveInterval 5
ExitOnForwardFailure yes
```
You can also forward your SSH key and GPG agent to the instance to securely access GitHub
and sign commits without copying your keys onto the instance.
You can also forward your SSH and GPG agent to the instance to securely access GitHub
and sign commits without copying your keys.
1. https://developer.github.com/v3/guides/using-ssh-agent-forwarding/
2. https://wiki.gnupg.org/AgentForwarding
### Let's Encrypt
[Let's Encrypt](https://letsencrypt.org) is a great option if you want to access code-server on an iPad
or do not want to use SSH forwarding. This does require that the remote machine is exposed to the internet.
[Let's Encrypt](https://letsencrypt.org) is a great option if you want to access `code-server` on an iPad
or do not want to use SSH forwarding. This does require that the remote machine be exposed to the internet.
Assuming you have been following the guide, edit your instance and checkmark the allow HTTP/HTTPS traffic options.
@@ -177,31 +194,74 @@ mydomain.com
reverse_proxy 127.0.0.1:8080
```
Remember to replace `mydomain.com` with your domain name!
5. Reload caddy with:
```bash
sudo systemctl reload caddy
```
Visit `https://<your-domain-name>` to access code-server. Congratulations!
Visit `https://<your-domain-name>` to access `code-server`. Congratulations!
In a future release we plan to integrate Let's Encrypt directly with code-server to avoid
In a future release we plan to integrate Let's Encrypt directly with `code-server` to avoid
the dependency on caddy.
#### NGINX
If you prefer to use NGINX instead of Caddy then please follow steps 1-2 above and then:
3. Install `nginx`:
```bash
sudo apt update
sudo apt install -y nginx certbot python-certbot-nginx
```
4. Put the following config into `/etc/nginx/sites-available/code-server` with sudo:
```nginx
server {
listen 80;
listen [::]:80;
server_name mydomain.com;
location / {
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;
}
}
```
Remember to replace `mydomain.com` with your domain name!
5. Enable the config:
```bash
sudo ln -s ../sites-available/code-server /etc/nginx/sites-enabled/code-server
sudo certbot --non-interactive --redirect --agree-tos --nginx -d mydomain.com -m me@example.com
```
Make sure to substitute `me@example.com` with your actual email.
Visit `https://<your-domain-name>` to access `code-server`. Congratulations!
### Self Signed Certificate
**note:** Self signed certificates do not work with iPad and will cause a blank page. You'll
have to use [Let's Encrypt](#lets-encrypt) instead.
have to use [Let's Encrypt](#lets-encrypt) instead. See the [FAQ](./FAQ.md#blank-screen-on-ipad).
Recommended reading: https://security.stackexchange.com/a/8112.
We recommend this as a last resort as self signed certificates do not work with iPads and can
cause other bizarre issues. Not to mention all the warnings when you access code-server.
We recommend this as a last resort because self signed certificates do not work with iPads and can
cause other bizarre issues. Not to mention all the warnings when you access `code-server`.
Only use this if:
1. You do not want to buy a domain.
2. You cannot expose the remote machine to the internet.
3. You do not want to use SSH forwarding.
1. You do not want to buy a domain or you cannot expose the remote machine to the internet.
2. You do not want to use SSH forwarding.
ssh into your instance and edit your code-server config file to use a randomly generated self signed certificate:
@@ -214,7 +274,7 @@ sed -i.bak 's/bind-addr: 127.0.0.1:8080/bind-addr: 0.0.0.0:443/' ~/.config/code-
sudo setcap cap_net_bind_service=+ep /usr/lib/code-server/lib/node
```
Assuming you have been following the guide, restart code-server with:
Assuming you have been following the guide, restart `code-server` with:
```bash
systemctl --user restart code-server
@@ -222,17 +282,17 @@ systemctl --user restart code-server
Edit your instance and checkmark the allow HTTPS traffic option.
Visit `https://<your-instance-ip>` to access code-server.
Visit `https://<your-instance-ip>` to access `code-server`.
You'll get a warning when accessing but if you click through you should be good.
To avoid the warnings, you can use [mkcert](https://mkcert.dev) to create a self signed certificate
trusted by your OS and then pass it into code-server via the `cert` and `cert-key` config
trusted by your OS and then pass it into `code-server` via the `cert` and `cert-key` config
fields.
### Change the password?
Edit the code-server config file at `~/.config/code-server/config.yaml` and then restart
code-server with:
Edit the `password` field in the `code-server` config file at `~/.config/code-server/config.yaml`
and then restart `code-server` with:
```bash
systemctl --user restart code-server
@@ -240,6 +300,6 @@ systemctl --user restart code-server
### How do I securely access development web services?
If you're working on a web service and want to access it locally, code-server can proxy it for you.
If you're working on a web service and want to access it locally, `code-server` can proxy it for you.
See [FAQ.md](https://github.com/cdr/code-server/blob/master/doc/FAQ.md#how-do-i-securely-access-web-services).
See the [FAQ](./FAQ.md#how-do-i-securely-access-web-services).

187
doc/install.md Normal file
View File

@@ -0,0 +1,187 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
# Install
- [install.sh](#installsh)
- [Flags](#flags)
- [Detection Reference](#detection-reference)
- [Debian, Ubuntu](#debian-ubuntu)
- [Fedora, CentOS, RHEL, SUSE](#fedora-centos-rhel-suse)
- [Arch Linux](#arch-linux)
- [yarn, npm](#yarn-npm)
- [macOS](#macos)
- [Standalone Releases](#standalone-releases)
- [Docker](#docker)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
This document demonstrates how to install `code-server` on
various distros and operating systems.
## install.sh
We have a [script](../install.sh) to install code-server for Linux, macOS and FreeBSD.
It tries to use the system package manager if possible.
First run to print out the install process:
```bash
curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run
```
Now to actually install:
```bash
curl -fsSL https://code-server.dev/install.sh | sh
```
The script will print out how to run and start using code-server.
If you believe an install script used with `curl | sh` is insecure, please give
[this wonderful blogpost](https://sandstorm.io/news/2015-09-24-is-curl-bash-insecure-pgp-verified-install) by
[sandstorm.io](https://sandstorm.io) a read.
If you'd still prefer manual installation despite the below [detection reference](#detection-reference) and `--dry-run`
then continue on for docs on manual installation. The [`install.sh`](../install.sh) script runs the _exact_ same
commands presented in the rest of this document.
### Flags
- `--dry-run` to echo the commands for the install process without running them.
- `--method` to choose the installation method.
- `--method=detect` to detect the package manager but fallback to `--method=standalone`.
- `--method=standalone` to install a standalone release archive into `~/.local`.
- `--prefix=/usr/local` to install a standalone release archive system wide.
- `--version=X.X.X` to install version `X.X.X` instead of latest.
- `--help` to see full usage docs.
### Detection Reference
- For Debian, Ubuntu and Raspbian it will install the latest deb package.
- For Fedora, CentOS, RHEL and openSUSE it will install the latest rpm package.
- For Arch Linux it will install the AUR package.
- For any unrecognized Linux operating system it will install the latest standalone release into `~/.local`.
- Add `~/.local/bin` to your `$PATH` to run code-server.
- For macOS it will install the Homebrew package.
- If Homebrew is not installed it will install the latest standalone release into `~/.local`.
- Add `~/.local/bin` to your `$PATH` to run code-server.
- For FreeBSD, it will install the [npm package](#yarn-npm) with `yarn` or `npm`.
- If ran on an architecture with no releases, it will install the [npm package](#yarn-npm) with `yarn` or `npm`.
- We only have releases for amd64 and arm64 presently.
- The [npm package](#yarn-npm) builds the native modules on postinstall.
## Debian, Ubuntu
```bash
curl -fOL https://github.com/cdr/code-server/releases/download/v3.4.1/code-server_3.4.1_amd64.deb
sudo dpkg -i code-server_3.4.1_amd64.deb
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## Fedora, CentOS, RHEL, SUSE
```bash
curl -fOL https://github.com/cdr/code-server/releases/download/v3.4.1/code-server-3.4.1-amd64.rpm
sudo rpm -i code-server-3.4.1-amd64.rpm
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## Arch Linux
```bash
# Installs code-server from the AUR using yay.
yay -S code-server
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
```bash
# Installs code-server from the AUR with plain makepkg.
git clone https://aur.archlinux.org/code-server.git
cd code-server
makepkg -si
systemctl --user enable --now code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## yarn, npm
We recommend installing with `yarn` or `npm` when:
1. You aren't on `amd64` or `arm64`.
2. If you're on Linux with glibc < v2.17 or glibcxx < v3.4.18
**note:** Installing via `yarn` or `npm` builds native modules on install and so requires C dependencies.
See [./npm.md](./npm.md) for installing these dependencies.
You will need at least node v12 installed. See [#1633](https://github.com/cdr/code-server/issues/1633).
```bash
yarn global add code-server
# Or: npm install -g code-server
code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## macOS
```bash
brew install code-server
brew services start code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## Standalone Releases
We publish self contained `.tar.gz` archives for every release on [github](https://github.com/cdr/code-server/releases).
They bundle the node binary and `node_modules`.
These are created from the [npm package](#yarn-npm) and the rest of the releases are created from these.
Only requirement is glibc >= 2.17 && glibcxx >= v3.4.18 on Linux and for macOS there is no minimum system requirement.
1. Download the latest release archive for your system from [github](https://github.com/cdr/code-server/releases).
2. Unpack the release.
3. You can run code-server by executing `./bin/code-server`.
You can add the code-server `bin` directory to your `$PATH` to easily execute `code-server`
without the full path every time.
Here is an example script for installing and using a standalone `code-server` release on Linux:
```bash
mkdir -p ~/.local/lib ~/.local/bin
curl -fL https://github.com/cdr/code-server/releases/download/v3.4.1/code-server-3.4.1-linux-amd64.tar.gz \
| tar -C ~/.local/lib -xz
mv ~/.local/lib/code-server-3.4.1-linux-amd64 ~/.local/lib/code-server-3.4.1
ln -s ~/.local/lib/code-server-3.4.1/bin/code-server ~/.local/bin/code-server
PATH="~/.local/bin:$PATH"
code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
## Docker
```bash
# This will start a code-server container and expose it at http://127.0.0.1:8080.
# It will also mount your current directory into the container as `/home/coder/project`
# and forward your UID/GID so that all file system operations occur as your user outside
# the container.
docker run -it -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-u "$(id -u):$(id -g)" \
codercom/code-server:latest
```
Our official image supports `amd64` and `arm64`.
For `arm32` support there is a popular community maintained alternative:
https://hub.docker.com/r/linuxserver/code-server

View File

@@ -1,5 +1,13 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
# npm Install Requirements
- [Ubuntu, Debian](#ubuntu-debian)
- [Fedora, CentOS, RHEL](#fedora-centos-rhel)
- [macOS](#macos)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
If you're installing the npm module you'll need certain dependencies to build
the native modules used by VS Code.
@@ -16,11 +24,11 @@ sudo apt-get install -y \
libsecret-1-dev
```
## Fedora, Red Hat, SUSE
## Fedora, CentOS, RHEL
```bash
sudo yum groupinstall -y 'Development Tools'
sudo yum config-manager --set-enabled PowerTools
sudo yum config-manager --set-enabled PowerTools # unnecessary on CentOS 7
sudo yum install -y python2 libsecret-devel libX11-devel libxkbfile-devel
npm config set python python2
```

503
install.sh Executable file
View File

@@ -0,0 +1,503 @@
#!/bin/sh
set -eu
# code-server's automatic install script.
# See https://github.com/cdr/code-server/blob/master/doc/install.md
usage() {
arg0="$0"
if [ "$0" = sh ]; then
arg0="curl -fsSL https://code-server.dev/install.sh | sh -s --"
else
not_curl_usage="The latest script is available at https://code-server.dev/install.sh
"
fi
cath << EOF
Installs code-server for Linux, macOS and FreeBSD.
It tries to use the system package manager if possible.
After successful installation it explains how to start using code-server.
${not_curl_usage-}
Usage:
$arg0 [--dry-run] [--version X.X.X] [--method detect] [--prefix ~/.local]
--dry-run
Echo the commands for the install process without running them.
--version X.X.X
Install a specific version instead of the latest.
--method [detect | standalone]
Choose the installation method. Defaults to detect.
- detect detects the system package manager and tries to use it.
Full reference on the process is further below.
- standalone installs a standalone release archive into ~/.local
Add ~/.local/bin to your \$PATH to use it.
--prefix <dir>
Sets the prefix used by standalone release archives. Defaults to ~/.local
The release is unarchived into ~/.local/lib/code-server-X.X.X
and the binary symlinked into ~/.local/bin/code-server
To install system wide pass ---prefix=/usr/local
- For Debian, Ubuntu and Raspbian it will install the latest deb package.
- For Fedora, CentOS, RHEL and openSUSE it will install the latest rpm package.
- For Arch Linux it will install the AUR package.
- For any unrecognized Linux operating system it will install the latest standalone
release into ~/.local
- For macOS it will install the Homebrew package.
- If Homebrew is not installed it will install the latest standalone release
into ~/.local
- For FreeBSD, it will install the npm package with yarn or npm.
- If ran on an architecture with no releases, it will install the
npm package with yarn or npm.
- We only have releases for amd64 and arm64 presently.
- The npm package builds the native modules on postinstall.
It will cache all downloaded assets into ~/.cache/code-server
More installation docs are at https://github.com/cdr/code-server/blob/master/doc/install.md
EOF
}
echo_latest_version() {
# https://gist.github.com/lukechilds/a83e1d7127b78fef38c2914c4ececc3c#gistcomment-2758860
version="$(curl -fsSLI -o /dev/null -w "%{url_effective}" https://github.com/cdr/code-server/releases/latest)"
version="${version#https://github.com/cdr/code-server/releases/tag/}"
version="${version#v}"
echo "$version"
}
echo_standalone_postinstall() {
echoh
cath << EOF
Standalone release has been installed into $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION
Please extend your path to use code-server:
PATH="$STANDALONE_INSTALL_PREFIX/bin:\$PATH"
Then you can run:
code-server
EOF
}
echo_systemd_postinstall() {
echoh
cath << EOF
To have systemd start code-server now and restart on boot:
systemctl --user enable --now code-server
Or, if you don't want/need a background service you can run:
code-server
EOF
}
main() {
if [ "${TRACE-}" ]; then
set -x
fi
unset \
DRY_RUN \
METHOD \
STANDALONE_INSTALL_PREFIX \
VERSION \
OPTIONAL
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
DRY_RUN=1
;;
--method)
METHOD="$(parse_arg "$@")"
shift
;;
--method=*)
METHOD="$(parse_arg "$@")"
;;
--prefix)
STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
shift
;;
--prefix=*)
STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
;;
--version)
VERSION="$(parse_arg "$@")"
shift
;;
--version=*)
VERSION="$(parse_arg "$@")"
;;
-h | --h | -help | --help)
usage
exit 0
;;
*)
echoerr "Unknown flag $1"
echoerr "Run with --help to see usage."
exit 1
;;
esac
shift
done
VERSION="${VERSION-$(echo_latest_version)}"
METHOD="${METHOD-detect}"
if [ "$METHOD" != detect ] && [ "$METHOD" != standalone ]; then
echoerr "Unknown install method \"$METHOD\""
echoerr "Run with --help to see usage."
exit 1
fi
STANDALONE_INSTALL_PREFIX="${STANDALONE_INSTALL_PREFIX-$HOME/.local}"
OS="$(os)"
if [ ! "$OS" ]; then
echoerr "Unsupported OS $(uname)."
exit 1
fi
distro_name
ARCH="$(arch)"
if [ ! "$ARCH" ]; then
if [ "$METHOD" = standalone ]; then
echoerr "No precompiled releases for $(uname -m)."
echoerr 'Please rerun without the "--method standalone" flag to install from npm.'
exit 1
fi
echoh "No precompiled releases for $(uname -m)."
install_npm
return
fi
if [ "$OS" = "freebsd" ]; then
if [ "$METHOD" = standalone ]; then
echoerr "No precompiled releases available for $OS."
echoerr 'Please rerun without the "--method standalone" flag to install from npm.'
exit 1
fi
echoh "No precompiled releases available for $OS."
install_npm
return
fi
CACHE_DIR="$(echo_cache_dir)"
if [ "$METHOD" = standalone ]; then
install_standalone
return
fi
case "$(distro)" in
macos)
install_macos
;;
ubuntu | debian | raspbian)
install_deb
;;
centos | fedora | rhel | opensuse)
install_rpm
;;
arch)
install_aur
;;
*)
echoh "Unsupported package manager."
install_standalone
;;
esac
}
parse_arg() {
case "$1" in
*=*)
# Remove everything after first equal sign.
opt="${1%%=*}"
# Remove everything before first equal sign.
optarg="${1#*=}"
if [ ! "$optarg" ] && [ ! "${OPTIONAL-}" ]; then
echoerr "$opt requires an argument"
echoerr "Run with --help to see usage."
exit 1
fi
echo "$optarg"
return
;;
esac
case "${2-}" in
"" | -*)
if [ ! "${OPTIONAL-}" ]; then
echoerr "$1 requires an argument"
echoerr "Run with --help to see usage."
exit 1
fi
;;
*)
echo "$2"
return
;;
esac
}
fetch() {
URL="$1"
FILE="$2"
if [ -e "$FILE" ]; then
echoh "+ Reusing $FILE"
return
fi
sh_c mkdir -p "$CACHE_DIR"
sh_c curl \
-#fL \
-o "$FILE.incomplete" \
-C - \
"$URL"
sh_c mv "$FILE.incomplete" "$FILE"
}
install_macos() {
if command_exists brew; then
echoh "Installing from Homebrew."
echoh
sh_c brew install code-server
return
fi
echoh "Homebrew not installed."
install_standalone
}
install_deb() {
echoh "Installing v$VERSION deb package from GitHub releases."
echoh
fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server_${VERSION}_$ARCH.deb" \
"$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
sudo_sh_c dpkg -i "$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
echo_systemd_postinstall
}
install_rpm() {
echoh "Installing v$VERSION rpm package from GitHub releases."
echoh
fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server-$VERSION-$ARCH.rpm" \
"$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
sudo_sh_c rpm -i "$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
echo_systemd_postinstall
}
install_aur() {
echoh "Installing from the AUR."
echoh
sh_c mkdir -p "$CACHE_DIR/code-server-aur"
sh_c "curl -#fsSL https://aur.archlinux.org/cgit/aur.git/snapshot/code-server.tar.gz | tar -xzC $CACHE_DIR/code-server-aur --strip-components 1"
echo "+ cd $CACHE_DIR/code-server-aur"
if [ ! "${DRY_RUN-}" ]; then
cd "$CACHE_DIR/code-server-aur"
fi
sh_c makepkg -si
echo_systemd_postinstall
}
install_standalone() {
echoh "Installing standalone release archive v$VERSION from GitHub releases."
echoh
fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server-$VERSION-$OS-$ARCH.tar.gz" \
"$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
sh_c="sh_c"
if [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then
sh_c="sudo_sh_c"
fi
if [ -e "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" ]; then
echoh
echoh "code-server-$VERSION is already installed at $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
echoh "Remove it to reinstall."
exit 0
fi
"$sh_c" mkdir -p "$STANDALONE_INSTALL_PREFIX/lib" "$STANDALONE_INSTALL_PREFIX/bin"
"$sh_c" tar -C "$STANDALONE_INSTALL_PREFIX/lib" -xzf "$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
"$sh_c" mv -f "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION-$OS-$ARCH" "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
"$sh_c" ln -fs "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION/bin/code-server" "$STANDALONE_INSTALL_PREFIX/bin/code-server"
echo_standalone_postinstall
}
install_npm() {
if command_exists yarn; then
sh_c="sh_c"
if [ ! -w "$(yarn global bin)" ]; then
sh_c="sudo_sh_c"
fi
echoh "Installing with yarn."
echoh
"$sh_c" yarn global add code-server --unsafe-perm
return
elif command_exists npm; then
sh_c="sh_c"
if [ ! -w "$(npm config get prefix)" ]; then
sh_c="sudo_sh_c"
fi
echoh "Installing with npm."
echoh
"$sh_c" npm install -g code-server --unsafe-perm
return
fi
echoh
echoerr "Please install npm or yarn to install code-server!"
echoerr "You will need at least node v12 and a few C dependencies."
echoerr "See the docs https://github.com/cdr/code-server#yarn-npm"
exit 1
}
os() {
case "$(uname)" in
Linux)
echo linux
;;
Darwin)
echo macos
;;
FreeBSD)
echo freebsd
;;
esac
}
# distro prints the detected operating system including linux distros.
#
# Example outputs:
# - macos
# - debian, ubuntu, raspbian
# - centos, fedora, rhel, opensuse
# - alpine
# - arch
# - freebsd
#
# Inspired by https://github.com/docker/docker-install/blob/26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c/install.sh#L111-L120.
distro() {
if [ "$OS" = "macos" ] || [ "$OS" = "freebsd" ]; then
echo "$OS"
return
fi
if [ -f /etc/os-release ]; then
(
. /etc/os-release
case "$ID" in opensuse-*)
# opensuse's ID's look like opensuse-leap and opensuse-tumbleweed.
echo "opensuse"
return
;;
esac
echo "$ID"
)
return
fi
}
# os_name prints a pretty human readable name for the OS/Distro.
distro_name() {
if [ "$(uname)" = "Darwin" ]; then
echo "macOS v$(sw_vers -productVersion)"
return
fi
if [ -f /etc/os-release ]; then
(
. /etc/os-release
echo "$PRETTY_NAME"
)
return
fi
# Prints something like: Linux 4.19.0-9-amd64
uname -sr
}
arch() {
case "$(uname -m)" in
aarch64)
echo arm64
;;
x86_64)
echo amd64
;;
amd64) # FreeBSD.
echo amd64
;;
esac
}
command_exists() {
command -v "$@" > /dev/null 2>&1
}
sh_c() {
echoh "+ $*"
if [ ! "${DRY_RUN-}" ]; then
sh -c "$*"
fi
}
sudo_sh_c() {
if [ "$(id -u)" = 0 ]; then
sh_c "$@"
elif command_exists sudo; then
sh_c "sudo $*"
elif command_exists su; then
sh_c "su -c '$*'"
else
echoh
echoerr "This script needs to run the following command as root."
echoerr " $*"
echoerr "Please install sudo or su."
exit 1
fi
}
echo_cache_dir() {
if [ "${XDG_CACHE_HOME-}" ]; then
echo "$XDG_CACHE_HOME/code-server"
elif [ "${HOME-}" ]; then
echo "$HOME/.cache/code-server"
else
echo "/tmp/code-server-cache"
fi
}
echoh() {
echo "$@" | humanpath
}
cath() {
humanpath
}
echoerr() {
echoh "$@" >&2
}
# humanpath replaces all occurances of " $HOME" with " ~"
# and all occurances of '"$HOME' with the literal '"$HOME'.
humanpath() {
sed "s# $HOME# ~#g; s#\"$HOME#\"\$HOME#g"
}
main "$@"

View File

@@ -1,7 +1,7 @@
{
"name": "code-server",
"license": "MIT",
"version": "3.3.0",
"version": "3.4.1",
"description": "Run VS Code on a remote server.",
"homepage": "https://github.com/cdr/code-server",
"bugs": {
@@ -16,10 +16,10 @@
"build": "./ci/build/build-code-server.sh",
"build:vscode": "./ci/build/build-vscode.sh",
"release": "./ci/build/build-release.sh",
"release:static": "./ci/build/build-static-release.sh",
"release:standalone": "./ci/build/build-standalone-release.sh",
"release:github-draft": "./ci/build/release-github-draft.sh",
"release:github-assets": "./ci/build/release-github-assets.sh",
"test:static-release": "./ci/build/test-static-release.sh",
"test:standalone-release": "./ci/build/test-standalone-release.sh",
"package": "./ci/build/build-packages.sh",
"_____": "",
"fmt": "./ci/dev/fmt.sh",
@@ -30,7 +30,6 @@
},
"main": "out/node/entry.js",
"devDependencies": {
"@types/adm-zip": "^0.4.32",
"@types/fs-extra": "^8.0.1",
"@types/http-proxy": "^1.17.4",
"@types/js-yaml": "^3.12.3",
@@ -65,8 +64,7 @@
"vfile-message": "^2.0.2"
},
"dependencies": {
"@coder/logger": "1.1.11",
"adm-zip": "^0.4.14",
"@coder/logger": "1.1.16",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
"http-proxy": "^1.18.0",
@@ -74,6 +72,7 @@
"js-yaml": "^3.13.1",
"limiter": "^1.1.5",
"pem": "^1.14.2",
"rotating-file-stream": "^2.1.1",
"safe-compare": "^1.1.4",
"semver": "^7.1.3",
"tar": "^6.0.1",

View File

@@ -1,28 +0,0 @@
<!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>

View File

@@ -1,37 +0,0 @@
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

@@ -18,7 +18,7 @@
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" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/register.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>

View File

@@ -1,51 +0,0 @@
.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

@@ -1,59 +0,0 @@
<!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

@@ -18,7 +18,7 @@
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" />
<link href="{{BASE}}/static/{{COMMIT}}/dist/register.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" />
</head>
<body>

View File

@@ -1,43 +0,0 @@
<!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

@@ -84,9 +84,10 @@
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`,
"iconv-lite-umd": `${staticBase}/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
jschardet: `${staticBase}/node_modules/jschardet/dist/jschardet.min.js`,
},
"vs/nls": nlsConfig,
}

View File

@@ -2,13 +2,17 @@ import { getOptions, normalize } from "../common/util"
const options = getOptions()
import "./pages/error.css"
import "./pages/global.css"
import "./pages/login.css"
if ("serviceWorker" in navigator) {
const path = normalize(`${options.base}/static/${options.commit}/dist/serviceWorker.js`)
navigator.serviceWorker
.register(path, {
scope: options.base || "/",
})
.then(function () {
.then(() => {
console.log("[Service Worker] registered")
})
}

View File

@@ -1,60 +0,0 @@
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
}

View File

@@ -9,16 +9,8 @@ export enum HttpCode {
}
export class HttpError extends Error {
public constructor(message: string, public readonly code: number) {
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",
}

View File

@@ -33,6 +33,13 @@ export const normalize = (url: string, keepTrailing = false): string => {
return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "")
}
/**
* Remove leading and trailing slashes.
*/
export const trimSlashes = (url: string): string => {
return url.replace(/^\/+|\/+$/g, "")
}
/**
* Get options embedded in the HTML or query params.
*/
@@ -75,3 +82,17 @@ export const getOptions = <T extends Options>(): T => {
return options
}
/**
* Wrap the value in an array if it's not already an array. If the value is
* undefined return an empty array.
*/
export const arrayify = <T>(value?: T | T[]): T[] => {
if (Array.isArray(value)) {
return value
}
if (typeof value === "undefined") {
return []
}
return [value]
}

View File

@@ -1,312 +0,0 @@
import { field, logger } from "@coder/logger"
import * as cp from "child_process"
import * as fs from "fs-extra"
import * as http from "http"
import * as net from "net"
import * as path from "path"
import * as url from "url"
import * as WebSocket from "ws"
import {
Application,
ApplicationsResponse,
ClientMessage,
RecentResponse,
ServerMessage,
SessionError,
SessionResponse,
} from "../../common/api"
import { ApiEndpoint, HttpCode, HttpError } from "../../common/http"
import { HttpProvider, HttpProviderOptions, HttpResponse, HttpServer, Route } from "../http"
import { findApplications, findWhitelistedApplications, Vscode } from "./bin"
import { VscodeHttpProvider } from "./vscode"
interface VsRecents {
[key: string]: (string | { configURIPath: string })[]
}
type VsSettings = [string, string][]
/**
* API HTTP provider.
*/
export class ApiHttpProvider extends HttpProvider {
private readonly ws = new WebSocket.Server({ noServer: true })
public constructor(
options: HttpProviderOptions,
private readonly server: HttpServer,
private readonly vscode: VscodeHttpProvider,
private readonly dataDir?: string,
) {
super(options)
}
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
this.ensureAuthenticated(request)
if (!this.isRoot(route)) {
throw new HttpError("Not found", HttpCode.NotFound)
}
switch (route.base) {
case ApiEndpoint.applications:
this.ensureMethod(request)
return {
mime: "application/json",
content: {
applications: await this.applications(),
},
} as HttpResponse<ApplicationsResponse>
case ApiEndpoint.process:
return this.process(request)
case ApiEndpoint.recent:
this.ensureMethod(request)
return {
mime: "application/json",
content: await this.recent(),
} as HttpResponse<RecentResponse>
}
throw new HttpError("Not found", HttpCode.NotFound)
}
public async handleWebSocket(
route: Route,
request: http.IncomingMessage,
socket: net.Socket,
head: Buffer,
): Promise<void> {
if (!this.authenticated(request)) {
throw new Error("not authenticated")
}
switch (route.base) {
case ApiEndpoint.status:
return this.handleStatusSocket(request, socket, head)
case ApiEndpoint.run:
return this.handleRunSocket(route, request, socket, head)
}
throw new HttpError("Not found", HttpCode.NotFound)
}
private async handleStatusSocket(request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise<void> {
const getMessageResponse = async (event: "health"): Promise<ServerMessage> => {
switch (event) {
case "health":
return { event, connections: await this.server.getConnections() }
default:
throw new Error("unexpected message")
}
}
await new Promise<WebSocket>((resolve) => {
this.ws.handleUpgrade(request, socket, head, (ws) => {
const send = (event: ServerMessage): void => {
ws.send(JSON.stringify(event))
}
ws.on("message", (data) => {
logger.trace("got message", field("message", data))
try {
const message: ClientMessage = JSON.parse(data.toString())
getMessageResponse(message.event).then(send)
} catch (error) {
logger.error(error.message, field("message", data))
}
})
resolve()
})
})
}
/**
* A socket that connects to the process.
*/
private async handleRunSocket(
_route: Route,
request: http.IncomingMessage,
socket: net.Socket,
head: Buffer,
): Promise<void> {
logger.debug("connecting to process")
const ws = await new Promise<WebSocket>((resolve, reject) => {
this.ws.handleUpgrade(request, socket, head, (socket) => {
socket.binaryType = "arraybuffer"
socket.on("error", (error) => {
socket.close(SessionError.FailedToStart)
logger.error("got error while connecting socket", field("error", error))
reject(error)
})
resolve(socket as WebSocket)
})
})
logger.debug("connected to process")
// Send ready message.
ws.send(
Buffer.from(
JSON.stringify({
protocol: "TODO",
}),
),
)
}
/**
* Return whitelisted applications.
*/
public async applications(): Promise<ReadonlyArray<Application>> {
return findWhitelistedApplications()
}
/**
* Return installed applications.
*/
public async installedApplications(): Promise<ReadonlyArray<Application>> {
return findApplications()
}
/**
* Handle /process endpoint.
*/
private async process(request: http.IncomingMessage): Promise<HttpResponse> {
this.ensureMethod(request, ["DELETE", "POST"])
const data = await this.getData(request)
if (!data) {
throw new HttpError("No data was provided", HttpCode.BadRequest)
}
const parsed: Application = JSON.parse(data)
switch (request.method) {
case "DELETE":
if (parsed.pid) {
await this.killProcess(parsed.pid)
} else if (parsed.path) {
await this.killProcess(parsed.path)
} else {
throw new Error("No pid or path was provided")
}
return {
mime: "application/json",
code: HttpCode.Ok,
}
case "POST": {
if (!parsed.exec) {
throw new Error("No exec was provided")
}
return {
mime: "application/json",
content: {
created: true,
pid: await this.spawnProcess(parsed.exec),
},
} as HttpResponse<SessionResponse>
}
}
throw new HttpError("Not found", HttpCode.NotFound)
}
/**
* Kill a process identified by pid or path if a web app.
*/
public async killProcess(pid: number | string): Promise<void> {
if (typeof pid === "string") {
switch (pid) {
case Vscode.path:
await this.vscode.dispose()
break
default:
throw new Error(`Process "${pid}" does not exist`)
}
} else {
process.kill(pid)
}
}
/**
* Spawn a process and return the pid.
*/
public async spawnProcess(exec: string): Promise<number> {
const proc = cp.spawn(exec, {
shell: process.env.SHELL || true,
env: {
...process.env,
},
})
proc.on("error", (error) => logger.error("process errored", field("pid", proc.pid), field("error", error)))
proc.on("exit", () => logger.debug("process exited", field("pid", proc.pid)))
logger.debug("started process", field("pid", proc.pid))
return proc.pid
}
/**
* Return VS Code's recent paths.
*/
public async recent(): Promise<RecentResponse> {
try {
if (!this.dataDir) {
throw new Error("data directory is not set")
}
const state: VsSettings = JSON.parse(await fs.readFile(path.join(this.dataDir, "User/state/global.json"), "utf8"))
const setting = Array.isArray(state) && state.find((item) => item[0] === "recently.opened")
if (!setting) {
return { paths: [], workspaces: [] }
}
const pathPromises: { [key: string]: Promise<string> } = {}
const workspacePromises: { [key: string]: Promise<string> } = {}
Object.values(JSON.parse(setting[1]) as VsRecents).forEach((recents) => {
recents.forEach((recent) => {
try {
const target = typeof recent === "string" ? pathPromises : workspacePromises
const pathname = url.parse(typeof recent === "string" ? recent : recent.configURIPath).pathname
if (pathname && !target[pathname]) {
target[pathname] = new Promise<string>((resolve) => {
fs.stat(pathname)
.then(() => resolve(pathname))
.catch(() => resolve())
})
}
} catch (error) {
logger.debug("invalid path", field("path", recent))
}
})
})
const [paths, workspaces] = await Promise.all([
Promise.all(Object.values(pathPromises)),
Promise.all(Object.values(workspacePromises)),
])
return {
paths: paths.filter((p) => !!p),
workspaces: workspaces.filter((p) => !!p),
}
} catch (error) {
if (error.code !== "ENOENT") {
throw error
}
}
return { paths: [], workspaces: [] }
}
/**
* For these, just return the error message since they'll be requested as
* JSON.
*/
public async getErrorRoot(_route: Route, _title: string, _header: string, error: string): Promise<HttpResponse> {
return {
mime: "application/json",
content: JSON.stringify({ error }),
}
}
}

View File

@@ -1,30 +0,0 @@
import * as fs from "fs"
import * as path from "path"
import { Application } from "../../common/api"
const getVscodeVersion = (): string => {
try {
return require(path.resolve(__dirname, "../../../lib/vscode/package.json")).version
} catch (error) {
return "unknown"
}
}
export const Vscode: Application = {
categories: ["Editor"],
icon: fs.readFileSync(path.resolve(__dirname, "../../../lib/vscode/resources/linux/code.png")).toString("base64"),
installed: true,
name: "VS Code",
path: "/",
version: getVscodeVersion(),
}
export const findApplications = async (): Promise<ReadonlyArray<Application>> => {
const apps: Application[] = [Vscode]
return apps.sort((a, b): number => a.name.localeCompare(b.name))
}
export const findWhitelistedApplications = async (): Promise<ReadonlyArray<Application>> => {
return [Vscode]
}

View File

@@ -1,147 +0,0 @@
import * as http from "http"
import * as querystring from "querystring"
import { Application } from "../../common/api"
import { HttpCode, HttpError } from "../../common/http"
import { normalize } from "../../common/util"
import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http"
import { ApiHttpProvider } from "./api"
import { UpdateHttpProvider } from "./update"
/**
* Dashboard HTTP provider.
*/
export class DashboardHttpProvider extends HttpProvider {
public constructor(
options: HttpProviderOptions,
private readonly api: ApiHttpProvider,
private readonly update: UpdateHttpProvider,
) {
super(options)
}
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
if (!this.isRoot(route)) {
throw new HttpError("Not found", HttpCode.NotFound)
}
switch (route.base) {
case "/spawn": {
this.ensureAuthenticated(request)
this.ensureMethod(request, "POST")
const data = await this.getData(request)
const app = data ? querystring.parse(data) : {}
if (app.path) {
return { redirect: Array.isArray(app.path) ? app.path[0] : app.path }
}
if (!app.exec) {
throw new Error("No exec was provided")
}
this.api.spawnProcess(Array.isArray(app.exec) ? app.exec[0] : app.exec)
return { redirect: this.options.base }
}
case "/app":
case "/": {
this.ensureMethod(request)
if (!this.authenticated(request)) {
return { redirect: "/login", query: { to: this.options.base } }
}
return route.base === "/" ? this.getRoot(route) : this.getAppRoot(route)
}
}
throw new HttpError("Not found", HttpCode.NotFound)
}
public async getRoot(route: Route): Promise<HttpResponse> {
const base = this.base(route)
const apps = await this.api.installedApplications()
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/home.html")
response.content = response.content
.replace(/{{UPDATE:NAME}}/, await this.getUpdate(base))
.replace(
/{{APP_LIST:EDITORS}}/,
this.getAppRows(
base,
apps.filter((app) => app.categories && app.categories.includes("Editor")),
),
)
.replace(
/{{APP_LIST:OTHER}}/,
this.getAppRows(
base,
apps.filter((app) => !app.categories || !app.categories.includes("Editor")),
),
)
return this.replaceTemplates(route, response)
}
public async getAppRoot(route: Route): Promise<HttpResponse> {
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/app.html")
return this.replaceTemplates(route, response)
}
private getAppRows(base: string, apps: ReadonlyArray<Application>): string {
return apps.length > 0
? apps.map((app) => this.getAppRow(base, app)).join("\n")
: `<div class="none">No applications found.</div>`
}
private getAppRow(base: string, app: Application): string {
return `<form class="block-row${app.exec ? " -x11" : ""}" method="post" action="${normalize(
`${base}${this.options.base}/spawn`,
)}">
<button class="item -row -link">
<input type="hidden" name="path" value="${app.path || ""}">
<input type="hidden" name="exec" value="${app.exec || ""}">
${
app.icon
? `<img class="icon" src="data:image/png;base64,${app.icon}"></img>`
: `<span class="icon -missing"></span>`
}
<span class="name">${app.name}</span>
</button>
</form>`
}
private async getUpdate(base: string): Promise<string> {
if (!this.update.enabled) {
return `<div class="block-row"><div class="item"><div class="sub">Updates are disabled</div></div></div>`
}
const humanize = (time: number): string => {
const d = new Date(time)
const pad = (t: number): string => (t < 10 ? "0" : "") + t
return (
`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +
` ${pad(d.getHours())}:${pad(d.getMinutes())}`
)
}
const update = await this.update.getUpdate()
if (this.update.isLatestVersion(update)) {
return `<div class="block-row">
<div class="item">
Latest: ${update.version}
<div class="sub">Up to date</div>
</div>
<div class="item">
${humanize(update.checked)}
<a class="sub -link" href="${base}/update/check?to=${this.options.base}">Check now</a>
</div>
<div class="item" >Current: ${this.update.currentVersion}</div>
</div>`
}
return `<div class="block-row">
<div class="item">
Latest: ${update.version}
<div class="sub">Out of date</div>
</div>
<div class="item">
${humanize(update.checked)}
<a class="sub -link" href="${base}/update?to=${this.options.base}">Update now</a>
</div>
<div class="item" >Current: ${this.update.currentVersion}</div>
</div>`
}
}

View File

@@ -24,7 +24,7 @@ export class ProxyHttpProvider extends HttpProvider {
const port = route.base.replace(/^\//, "")
return {
proxy: {
base: `${this.options.base}/${port}`,
strip: `${route.providerBase}/${port}`,
port,
},
}
@@ -35,7 +35,7 @@ export class ProxyHttpProvider extends HttpProvider {
const port = route.base.replace(/^\//, "")
return {
proxy: {
base: `${this.options.base}/${port}`,
strip: `${route.providerBase}/${port}`,
port,
},
}

View File

@@ -5,6 +5,7 @@ import { Readable } from "stream"
import * as tarFs from "tar-fs"
import * as zlib from "zlib"
import { HttpProvider, HttpResponse, Route } from "../http"
import { pathToFsPath } from "../util"
/**
* Static file HTTP provider. Regular static requests (the path is the request
@@ -18,7 +19,7 @@ export class StaticHttpProvider extends HttpProvider {
if (typeof route.query.tar === "string") {
this.ensureAuthenticated(request)
return this.getTarredResource(request, route.query.tar)
return this.getTarredResource(request, pathToFsPath(route.query.tar))
}
const response = await this.getReplacedResource(route)

View File

@@ -1,22 +1,12 @@
import { field, logger } from "@coder/logger"
import zip from "adm-zip"
import * as cp from "child_process"
import * as fs from "fs-extra"
import * as http from "http"
import * as https from "https"
import * as os from "os"
import * as path from "path"
import * as semver from "semver"
import { Readable, Writable } from "stream"
import * as tar from "tar-fs"
import * as url from "url"
import * as util from "util"
import * as zlib from "zlib"
import { HttpCode, HttpError } from "../../common/http"
import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http"
import { settings as globalSettings, SettingsProvider, UpdateSettings } from "../settings"
import { tmpdir } from "../util"
import { ipcMain } from "../wrapper"
export interface Update {
checked: number
@@ -28,7 +18,7 @@ export interface LatestResponse {
}
/**
* Update HTTP provider.
* HTTP provider for checking updates (does not download/install them).
*/
export class UpdateHttpProvider extends HttpProvider {
private update?: Promise<Update>
@@ -42,12 +32,6 @@ export class UpdateHttpProvider extends HttpProvider {
* that fulfills `LatestResponse`.
*/
private readonly latestUrl = "https://api.github.com/repos/cdr/code-server/releases/latest",
/**
* The URL for downloading a version of code-server. {{VERSION}} and
* {{RELEASE_NAME}} will be replaced (for example 2.1.0 and
* code-server-2.1.0-linux-x86_64.tar.gz).
*/
private readonly downloadUrl = "https://github.com/cdr/code-server/releases/download/{{VERSION}}/{{RELEASE_NAME}}",
/**
* Update information will be stored here. If not provided, the global
* settings will be used.
@@ -65,66 +49,30 @@ export class UpdateHttpProvider extends HttpProvider {
throw new HttpError("Not found", HttpCode.NotFound)
}
switch (route.base) {
case "/check":
this.getUpdate(true)
if (route.query && route.query.to) {
return {
redirect: Array.isArray(route.query.to) ? route.query.to[0] : route.query.to,
query: { to: undefined },
}
}
return this.getRoot(route, request)
case "/apply":
return this.tryUpdate(route, request)
case "/":
return this.getRoot(route, request)
if (!this.enabled) {
throw new Error("update checks are disabled")
}
throw new HttpError("Not found", HttpCode.NotFound)
}
public async getRoot(
route: Route,
request: http.IncomingMessage,
errorOrUpdate?: Update | Error,
): Promise<HttpResponse> {
if (request.headers["content-type"] === "application/json") {
if (!this.enabled) {
switch (route.base) {
case "/check":
case "/": {
const update = await this.getUpdate(route.base === "/check")
return {
content: {
isLatest: true,
...update,
isLatest: this.isLatestVersion(update),
},
}
}
const update = await this.getUpdate()
return {
content: {
...update,
isLatest: this.isLatestVersion(update),
},
}
}
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/update.html")
response.content = response.content
.replace(
/{{UPDATE_STATUS}}/,
errorOrUpdate && !(errorOrUpdate instanceof Error)
? `Updated to ${errorOrUpdate.version}`
: await this.getUpdateHtml(),
)
.replace(/{{ERROR}}/, errorOrUpdate instanceof Error ? `<div class="error">${errorOrUpdate.message}</div>` : "")
return this.replaceTemplates(route, response)
throw new HttpError("Not found", HttpCode.NotFound)
}
/**
* Query for and return the latest update.
*/
public async getUpdate(force?: boolean): Promise<Update> {
if (!this.enabled) {
throw new Error("updates are not enabled")
}
// Don't run multiple requests at a time.
if (!this.update) {
this.update = this._getUpdate(force)
@@ -172,166 +120,6 @@ export class UpdateHttpProvider extends HttpProvider {
}
}
private async getUpdateHtml(): Promise<string> {
if (!this.enabled) {
return "Updates are disabled"
}
const update = await this.getUpdate()
if (this.isLatestVersion(update)) {
return "No update available"
}
return `<button type="submit" class="apply -button">Update to ${update.version}</button>`
}
public async tryUpdate(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
try {
const update = await this.getUpdate()
if (!this.isLatestVersion(update)) {
await this.downloadAndApplyUpdate(update)
return this.getRoot(route, request, update)
}
return this.getRoot(route, request)
} catch (error) {
// For JSON requests propagate the error. Otherwise catch it so we can
// show the error inline with the update button instead of an error page.
if (request.headers["content-type"] === "application/json") {
throw error
}
return this.getRoot(route, error)
}
}
public async downloadAndApplyUpdate(update: Update, targetPath?: string): Promise<void> {
const releaseName = await this.getReleaseName(update)
const url = this.downloadUrl.replace("{{VERSION}}", update.version).replace("{{RELEASE_NAME}}", releaseName)
let downloadPath = path.join(tmpdir, "updates", releaseName)
fs.mkdirp(path.dirname(downloadPath))
const response = await this.requestResponse(url)
try {
if (downloadPath.endsWith(".tar.gz")) {
downloadPath = await this.extractTar(response, downloadPath)
} else {
downloadPath = await this.extractZip(response, downloadPath)
}
logger.debug("Downloaded update", field("path", downloadPath))
// The archive should have a directory inside at the top level with the
// same name as the archive.
const directoryPath = path.join(downloadPath, path.basename(downloadPath))
await fs.stat(directoryPath)
if (!targetPath) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
targetPath = path.resolve(__dirname, "../../../")
}
// Move the old directory to prevent potential data loss.
const backupPath = path.resolve(targetPath, `../${path.basename(targetPath)}.${Date.now().toString()}`)
logger.debug("Replacing files", field("target", targetPath), field("backup", backupPath))
await fs.move(targetPath, backupPath)
// Move the new directory.
await fs.move(directoryPath, targetPath)
await fs.remove(downloadPath)
if (process.send) {
ipcMain().relaunch(update.version)
}
} catch (error) {
response.destroy(error)
throw error
}
}
private async extractTar(response: Readable, downloadPath: string): Promise<string> {
downloadPath = downloadPath.replace(/\.tar\.gz$/, "")
logger.debug("Extracting tar", field("path", downloadPath))
response.pause()
await fs.remove(downloadPath)
const decompress = zlib.createGunzip()
response.pipe(decompress as Writable)
response.on("error", (error) => decompress.destroy(error))
response.on("close", () => decompress.end())
const destination = tar.extract(downloadPath)
decompress.pipe(destination)
decompress.on("error", (error) => destination.destroy(error))
decompress.on("close", () => destination.end())
await new Promise((resolve, reject) => {
destination.on("finish", resolve)
destination.on("error", reject)
response.resume()
})
return downloadPath
}
private async extractZip(response: Readable, downloadPath: string): Promise<string> {
logger.debug("Downloading zip", field("path", downloadPath))
response.pause()
await fs.remove(downloadPath)
const write = fs.createWriteStream(downloadPath)
response.pipe(write)
response.on("error", (error) => write.destroy(error))
response.on("close", () => write.end())
await new Promise((resolve, reject) => {
write.on("error", reject)
write.on("close", resolve)
response.resume
})
const zipPath = downloadPath
downloadPath = downloadPath.replace(/\.zip$/, "")
await fs.remove(downloadPath)
logger.debug("Extracting zip", field("path", zipPath))
await new Promise((resolve, reject) => {
new zip(zipPath).extractAllToAsync(downloadPath, true, (error) => {
return error ? reject(error) : resolve()
})
})
await fs.remove(zipPath)
return downloadPath
}
/**
* Given an update return the name for the packaged archived.
*/
public async getReleaseName(update: Update): Promise<string> {
let target: string = os.platform()
if (target === "linux") {
const result = await util
.promisify(cp.exec)("ldd --version")
.catch((error) => ({
stderr: error.message,
stdout: "",
}))
if (/musl/.test(result.stderr) || /musl/.test(result.stdout)) {
target = "alpine"
}
}
let arch = os.arch()
if (arch === "x64") {
arch = "x86_64"
}
return `code-server-${update.version}-${target}-${arch}.${target === "darwin" ? "zip" : "tar.gz"}`
}
private async request(uri: string): Promise<Buffer> {
const response = await this.requestResponse(uri)
return new Promise((resolve, reject) => {

View File

@@ -14,10 +14,11 @@ import {
WorkbenchOptions,
} from "../../../lib/vscode/src/vs/server/ipc"
import { HttpCode, HttpError } from "../../common/http"
import { generateUuid } from "../../common/util"
import { arrayify, generateUuid } from "../../common/util"
import { Args } from "../cli"
import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http"
import { settings } from "../settings"
import { pathToFsPath } from "../util"
export class VscodeHttpProvider extends HttpProvider {
private readonly serverRootPath: string
@@ -130,7 +131,7 @@ export class VscodeHttpProvider extends HttpProvider {
if (!this.isRoot(route)) {
throw new HttpError("Not found", HttpCode.NotFound)
} else if (!this.authenticated(request)) {
return { redirect: "/login", query: { to: this.options.base } }
return { redirect: "/login", query: { to: route.providerBase } }
}
try {
return await this.getRoot(request, route)
@@ -151,7 +152,7 @@ export class VscodeHttpProvider extends HttpProvider {
case "/resource":
case "/vscode-remote-resource":
if (typeof route.query.path === "string") {
return this.getResource(route.query.path)
return this.getResource(pathToFsPath(route.query.path))
}
break
case "/webview":
@@ -182,11 +183,10 @@ export class VscodeHttpProvider extends HttpProvider {
}),
])
if (startPath) {
settings.write({
lastVisited: startPath,
})
}
settings.write({
lastVisited: startPath || lastVisited, // If startpath is undefined, then fallback to lastVisited
query: route.query,
})
if (!this.isDev) {
response.content = response.content.replace(/<!-- PROD_ONLY/g, "").replace(/END_PROD_ONLY -->/g, "")
@@ -223,8 +223,7 @@ export class VscodeHttpProvider extends HttpProvider {
}
for (let i = 0; i < startPaths.length; ++i) {
const startPath = startPaths[i]
const url =
startPath && (typeof startPath.url === "string" ? [startPath.url] : startPath.url || []).find((p) => !!p)
const url = arrayify(startPath && startPath.url).find((p) => !!p)
if (startPath && url) {
return {
url,

View File

@@ -152,12 +152,12 @@ export const optionDescriptions = (): string[] => {
)
}
export const parse = async (
export const parse = (
argv: string[],
opts?: {
configFile: string
},
): Promise<Args> => {
): Args => {
const error = (msg: string): Error => {
if (opts?.configFile) {
msg = `error reading ${opts.configFile}: ${msg}`
@@ -288,18 +288,28 @@ export const parse = async (
break
case LogLevel.Debug:
logger.level = Level.Debug
args.verbose = false
break
case LogLevel.Info:
logger.level = Level.Info
args.verbose = false
break
case LogLevel.Warn:
logger.level = Level.Warning
args.verbose = false
break
case LogLevel.Error:
logger.level = Level.Error
args.verbose = false
break
}
return args
}
export async function setDefaults(args: Args): Promise<Args> {
args = { ...args }
if (!args["user-data-dir"]) {
await copyOldMacOSDataDir()
args["user-data-dir"] = paths.data
@@ -338,7 +348,9 @@ export async function readConfigFile(configPath?: string): Promise<Args> {
logger.info(`Wrote default config file to ${humanPath(configPath)}`)
}
logger.info(`Using config file ${humanPath(configPath)}`)
if (!process.env.CODE_SERVER_PARENT_PID) {
logger.info(`Using config file ${humanPath(configPath)}`)
}
const configFile = await fs.readFile(configPath)
const config = yaml.safeLoad(configFile.toString(), {
@@ -353,7 +365,7 @@ export async function readConfigFile(configPath?: string): Promise<Args> {
}
return `--${optName}=${opt}`
})
const args = await parse(configFileArgv, {
const args = parse(configFileArgv, {
configFile: configPath,
})
return {

View File

@@ -2,14 +2,12 @@ import { field, logger } from "@coder/logger"
import * as cp from "child_process"
import * as path from "path"
import { CliMessage } from "../../lib/vscode/src/vs/server/ipc"
import { ApiHttpProvider } from "./app/api"
import { DashboardHttpProvider } from "./app/dashboard"
import { LoginHttpProvider } from "./app/login"
import { ProxyHttpProvider } from "./app/proxy"
import { StaticHttpProvider } from "./app/static"
import { UpdateHttpProvider } from "./app/update"
import { VscodeHttpProvider } from "./app/vscode"
import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile } from "./cli"
import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli"
import { AuthType, HttpServer, HttpServerOptions } from "./http"
import { generateCertificate, hash, open, humanPath } from "./util"
import { ipcMain, wrap } from "./wrapper"
@@ -31,11 +29,7 @@ try {
const version = pkg.version || "development"
const commit = pkg.commit || "development"
const main = async (cliArgs: Args): Promise<void> => {
const configArgs = await readConfigFile(cliArgs.config)
// This prioritizes the flags set in args over the ones in the config file.
let args = Object.assign(configArgs, cliArgs)
const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void> => {
if (!args.auth) {
args = {
...args,
@@ -77,15 +71,17 @@ const main = async (cliArgs: Args): Promise<void> => {
}
const httpServer = new HttpServer(options)
const vscode = httpServer.registerHttpProvider("/", VscodeHttpProvider, args)
const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, vscode, args["user-data-dir"])
const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, false)
httpServer.registerHttpProvider(["/", "/vscode"], VscodeHttpProvider, args)
httpServer.registerHttpProvider("/update", UpdateHttpProvider, false)
httpServer.registerHttpProvider("/proxy", ProxyHttpProvider)
httpServer.registerHttpProvider("/login", LoginHttpProvider, args.config!, envPassword)
httpServer.registerHttpProvider("/static", StaticHttpProvider)
httpServer.registerHttpProvider("/dashboard", DashboardHttpProvider, api, update)
ipcMain().onDispose(() => httpServer.dispose())
ipcMain().onDispose(() => {
httpServer.dispose().then((errors) => {
errors.forEach((error) => logger.error(error.message))
})
})
logger.info(`code-server ${version} ${commit}`)
const serverAddress = await httpServer.listen()
@@ -127,16 +123,21 @@ const main = async (cliArgs: Args): Promise<void> => {
}
async function entry(): Promise<void> {
const tryParse = async (): Promise<Args> => {
const tryParse = async (): Promise<[Args, Args, Args]> => {
try {
return await parse(process.argv.slice(2))
const cliArgs = parse(process.argv.slice(2))
const configArgs = await readConfigFile(cliArgs.config)
// This prioritizes the flags set in args over the ones in the config file.
let args = Object.assign(configArgs, cliArgs)
args = await setDefaults(args)
return [args, cliArgs, configArgs]
} catch (error) {
console.error(error.message)
process.exit(1)
}
}
const args = await tryParse()
const [args, cliArgs, configArgs] = await tryParse()
if (args.help) {
console.log("code-server", version, commit)
console.log("")
@@ -180,7 +181,7 @@ async function entry(): Promise<void> {
})
vscode.on("exit", (code) => process.exit(code || 0))
} else {
wrap(() => main(args))
wrap(() => main(args, cliArgs, configArgs))
}
}

View File

@@ -12,7 +12,7 @@ import { Readable } from "stream"
import * as tls from "tls"
import * as url from "url"
import { HttpCode, HttpError } from "../common/http"
import { normalize, Options, plural, split } from "../common/util"
import { arrayify, normalize, Options, plural, split, trimSlashes } from "../common/util"
import { SocketProxyProvider } from "./socket"
import { getMediaMime, paths } from "./util"
@@ -36,9 +36,13 @@ export type Query = { [key: string]: string | string[] | undefined }
export interface ProxyOptions {
/**
* A base path to strip from from the request before proxying if necessary.
* A path to strip from from the beginning of the request before proxying
*/
base?: string
strip?: string
/**
* A path to add to the beginning of the request before proxying.
*/
prepend?: string
/**
* The port to proxy.
*/
@@ -79,9 +83,8 @@ export interface HttpResponse<T = string | Buffer | object> {
*/
mime?: string
/**
* Redirect to this path. Will rewrite against the base path but NOT the
* provider endpoint so you must include it. This allows redirecting outside
* of your endpoint.
* Redirect to this path. This is constructed against the site base (not the
* provider's base).
*/
redirect?: string
/**
@@ -133,12 +136,16 @@ export interface HttpServerOptions {
export interface Route {
/**
* Base path part (in /test/path it would be "/test").
* Provider base path part (for /provider/base/path it would be /provider).
*/
providerBase: string
/**
* Base path part (for /provider/base/path it would be /base).
*/
base: string
/**
* Remaining part of the route (in /test/path it would be "/path"). It can be
* blank.
* Remaining part of the route after factoring out the base and provider base
* (for /provider/base/path it would be /path). It can be blank.
*/
requestPath: string
/**
@@ -161,7 +168,6 @@ interface ProviderRoute extends Route {
export interface HttpProviderOptions {
readonly auth: AuthType
readonly base: string
readonly commit: string
readonly password?: string
}
@@ -175,7 +181,7 @@ export abstract class HttpProvider {
public constructor(protected readonly options: HttpProviderOptions) {}
public dispose(): void {
public async dispose(): Promise<void> {
// No default behavior.
}
@@ -281,7 +287,7 @@ export abstract class HttpProvider {
* Helper to error on invalid methods (default GET).
*/
protected ensureMethod(request: http.IncomingMessage, method?: string | string[]): void {
const check = Array.isArray(method) ? method : [method || "GET"]
const check = arrayify(method || "GET")
if (!request.method || !check.includes(request.method)) {
throw new HttpError(`Unsupported method ${request.method}`, HttpCode.BadRequest)
}
@@ -502,9 +508,15 @@ export class HttpServer {
})
}
public dispose(): void {
/**
* Stop and dispose everything. Return an array of disposal errors.
*/
public async dispose(): Promise<Error[]> {
this.socketProvider.stop()
this.providers.forEach((p) => p.dispose())
const providers = Array.from(this.providers.values())
// Catch so all the errors can be seen rather than just the first one.
const responses = await Promise.all<Error | undefined>(providers.map((p) => p.dispose().catch((e) => e)))
return responses.filter<Error>((r): r is Error => typeof r !== "undefined")
}
public async getConnections(): Promise<number> {
@@ -518,41 +530,51 @@ export class HttpServer {
/**
* Register a provider for a top-level endpoint.
*/
public registerHttpProvider<T extends HttpProvider>(endpoint: string, provider: HttpProvider0<T>): T
public registerHttpProvider<A1, T extends HttpProvider>(endpoint: string, provider: HttpProvider1<A1, T>, a1: A1): T
public registerHttpProvider<T extends HttpProvider>(endpoint: string | string[], provider: HttpProvider0<T>): T
public registerHttpProvider<A1, T extends HttpProvider>(
endpoint: string | string[],
provider: HttpProvider1<A1, T>,
a1: A1,
): T
public registerHttpProvider<A1, A2, T extends HttpProvider>(
endpoint: string,
endpoint: string | string[],
provider: HttpProvider2<A1, A2, T>,
a1: A1,
a2: A2,
): T
public registerHttpProvider<A1, A2, A3, T extends HttpProvider>(
endpoint: string,
endpoint: string | string[],
provider: HttpProvider3<A1, A2, A3, T>,
a1: A1,
a2: A2,
a3: A3,
): T
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public registerHttpProvider(endpoint: string, provider: any, ...args: any[]): any {
endpoint = endpoint.replace(/^\/+|\/+$/g, "")
if (this.providers.has(`/${endpoint}`)) {
throw new Error(`${endpoint} is already registered`)
}
if (/\//.test(endpoint)) {
throw new Error(`Only top-level endpoints are supported (got ${endpoint})`)
}
public registerHttpProvider(endpoint: string | string[], provider: any, ...args: any[]): void {
const p = new provider(
{
auth: this.options.auth || AuthType.None,
base: `/${endpoint}`,
commit: this.options.commit,
password: this.options.password,
},
...args,
)
this.providers.set(`/${endpoint}`, p)
return p
const endpoints = arrayify(endpoint).map(trimSlashes)
endpoints.forEach((endpoint) => {
if (/\//.test(endpoint)) {
throw new Error(`Only top-level endpoints are supported (got ${endpoint})`)
}
const existingProvider = this.providers.get(`/${endpoint}`)
this.providers.set(`/${endpoint}`, p)
if (existingProvider) {
logger.debug(`Overridding existing /${endpoint} provider`)
// If the existing provider isn't registered elsewhere we can dispose.
if (!Array.from(this.providers.values()).find((p) => p === existingProvider)) {
logger.debug(`Disposing existing /${endpoint} provider`)
existingProvider.dispose()
}
}
})
}
/**
@@ -649,8 +671,10 @@ export class HttpServer {
if (request.headers["content-type"] === "application/json") {
write({
code,
mime: "application/json",
content: {
error: e.message,
...(e.details || {}),
},
})
} else {
@@ -759,7 +783,7 @@ export class HttpServer {
// that by shifting the next base out of the request path.
let provider = this.providers.get(base)
if (base !== "/" && provider) {
return { ...parse(requestPath), fullPath, query: parsedUrl.query, provider, originalPath }
return { ...parse(requestPath), providerBase: base, fullPath, query: parsedUrl.query, provider, originalPath }
}
// Fall back to the top-level provider.
@@ -767,7 +791,7 @@ export class HttpServer {
if (!provider) {
throw new Error(`No provider for ${base}`)
}
return { base, fullPath, requestPath, query: parsedUrl.query, provider, originalPath }
return { base, providerBase: "/", fullPath, requestPath, query: parsedUrl.query, provider, originalPath }
}
/**
@@ -806,10 +830,11 @@ export class HttpServer {
// sure how best to get this information to the `proxyRes` event handler.
// For now I'm sticking it on the request object which is passed through to
// the event.
;(request as ProxyRequest).base = options.base
;(request as ProxyRequest).base = options.strip
const isHttp = response instanceof http.ServerResponse
const path = options.base ? route.fullPath.replace(options.base, "") : route.fullPath
const base = options.strip ? route.fullPath.replace(options.strip, "") : route.fullPath
const path = normalize("/" + (options.prepend || "") + "/" + base, true)
const proxyOptions: proxy.ServerOptions = {
changeOrigin: true,
ignorePath: true,

View File

@@ -2,6 +2,7 @@ import * as fs from "fs-extra"
import * as path from "path"
import { extend, paths } from "./util"
import { logger } from "@coder/logger"
import { Route } from "./http"
export type Settings = { [key: string]: Settings | string | boolean | number }
@@ -29,11 +30,13 @@ export class SettingsProvider<T> {
/**
* Write settings combined with current settings. On failure log a warning.
* Objects will be merged and everything else will be replaced.
* Settings can be shallow or deep merged.
*/
public async write(settings: Partial<T>): Promise<void> {
public async write(settings: Partial<T>, shallow = true): Promise<void> {
try {
await fs.writeFile(this.settingsPath, JSON.stringify(extend(await this.read(), settings), null, 2))
const oldSettings = await this.read()
const nextSettings = shallow ? Object.assign({}, oldSettings, settings) : extend(oldSettings, settings)
await fs.writeFile(this.settingsPath, JSON.stringify(nextSettings, null, 2))
} catch (error) {
logger.warn(error.message)
}
@@ -55,6 +58,7 @@ export interface CoderSettings extends UpdateSettings {
url: string
workspace: boolean
}
query: Route["query"]
}
/**

View File

@@ -217,3 +217,51 @@ export function extend(...args: any[]): any {
}
return c
}
/**
* Taken from vs/base/common/charCode.ts. Copied for now instead of importing so
* we don't have to set up a `vs` alias to be able to import with types (since
* the alternative is to directly import from `out`).
*/
const enum CharCode {
Slash = 47,
A = 65,
Z = 90,
a = 97,
z = 122,
Colon = 58,
}
/**
* Compute `fsPath` for the given uri.
* Taken from vs/base/common/uri.ts. It's not imported to avoid also importing
* everything that file imports.
*/
export function pathToFsPath(path: string, keepDriveLetterCasing = false): string {
const isWindows = process.platform === "win32"
const uri = { authority: undefined, path, scheme: "file" }
let value: string
if (uri.authority && uri.path.length > 1 && uri.scheme === "file") {
// unc path: file://shares/c$/far/boo
value = `//${uri.authority}${uri.path}`
} else if (
uri.path.charCodeAt(0) === CharCode.Slash &&
((uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z) ||
(uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)) &&
uri.path.charCodeAt(2) === CharCode.Colon
) {
if (!keepDriveLetterCasing) {
// windows drive letter: file:///c:/far/boo
value = uri.path[1].toLowerCase() + uri.path.substr(2)
} else {
value = uri.path.substr(1)
}
} else {
// other path
value = uri.path
}
if (isWindows) {
value = value.replace(/\//g, "\\")
}
return value
}

View File

@@ -1,6 +1,9 @@
import { logger, field } from "@coder/logger"
import { field, logger } from "@coder/logger"
import * as cp from "child_process"
import * as path from "path"
import * as rfs from "rotating-file-stream"
import { Emitter } from "../common/emitter"
import { paths } from "./util"
interface HandshakeMessage {
type: "handshake"
@@ -140,8 +143,17 @@ export interface WrapperOptions {
export class WrapperProcess {
private process?: cp.ChildProcess
private started?: Promise<void>
private readonly logStdoutStream: rfs.RotatingFileStream
private readonly logStderrStream: rfs.RotatingFileStream
public constructor(private currentVersion: string, private readonly options?: WrapperOptions) {
const opts = {
size: "10M",
maxFiles: 10,
}
this.logStdoutStream = rfs.createStream(path.join(paths.data, "coder-logs", "code-server-stdout.log"), opts)
this.logStderrStream = rfs.createStream(path.join(paths.data, "coder-logs", "code-server-stderr.log"), opts)
ipcMain().onDispose(() => {
if (this.process) {
this.process.removeAllListeners()
@@ -176,6 +188,15 @@ export class WrapperProcess {
public start(): Promise<void> {
if (!this.started) {
this.started = this.spawn().then((child) => {
// Log both to stdout and to the log directory.
if (child.stdout) {
child.stdout.pipe(this.logStdoutStream)
child.stdout.pipe(process.stdout)
}
if (child.stderr) {
child.stderr.pipe(this.logStderrStream)
child.stderr.pipe(process.stderr)
}
logger.debug(`spawned inner process ${child.pid}`)
ipcMain()
.handshake(child)
@@ -205,7 +226,7 @@ export class WrapperProcess {
CODE_SERVER_PARENT_PID: process.pid.toString(),
NODE_OPTIONS: nodeOptions,
},
stdio: ["inherit", "inherit", "inherit", "ipc"],
stdio: ["ipc"],
})
}
}

View File

@@ -2,7 +2,6 @@ import { logger, Level } from "@coder/logger"
import * as assert from "assert"
import * as path from "path"
import { parse } from "../src/node/cli"
import { paths } from "../src/node/util"
describe("cli", () => {
beforeEach(() => {
@@ -12,17 +11,15 @@ describe("cli", () => {
// The parser will always fill these out.
const defaults = {
_: [],
"extensions-dir": path.join(paths.data, "extensions"),
"user-data-dir": paths.data,
}
it("should set defaults", async () => {
assert.deepEqual(await await parse([]), defaults)
it("should set defaults", () => {
assert.deepEqual(parse([]), defaults)
})
it("should parse all available options", async () => {
it("should parse all available options", () => {
assert.deepEqual(
await await parse([
parse([
"--bind-addr=192.169.0.1:8080",
"--auth",
"none",
@@ -84,8 +81,8 @@ describe("cli", () => {
)
})
it("should work with short options", async () => {
assert.deepEqual(await parse(["-vvv", "-v"]), {
it("should work with short options", () => {
assert.deepEqual(parse(["-vvv", "-v"]), {
...defaults,
log: "trace",
verbose: true,
@@ -95,17 +92,18 @@ describe("cli", () => {
assert.equal(logger.level, Level.Trace)
})
it("should use log level env var", async () => {
it("should use log level env var", () => {
process.env.LOG_LEVEL = "debug"
assert.deepEqual(await parse([]), {
assert.deepEqual(parse([]), {
...defaults,
log: "debug",
verbose: false,
})
assert.equal(process.env.LOG_LEVEL, "debug")
assert.equal(logger.level, Level.Debug)
process.env.LOG_LEVEL = "trace"
assert.deepEqual(await parse([]), {
assert.deepEqual(parse([]), {
...defaults,
log: "trace",
verbose: true,
@@ -116,23 +114,25 @@ describe("cli", () => {
it("should prefer --log to env var and --verbose to --log", async () => {
process.env.LOG_LEVEL = "debug"
assert.deepEqual(await parse(["--log", "info"]), {
assert.deepEqual(parse(["--log", "info"]), {
...defaults,
log: "info",
verbose: false,
})
assert.equal(process.env.LOG_LEVEL, "info")
assert.equal(logger.level, Level.Info)
process.env.LOG_LEVEL = "trace"
assert.deepEqual(await parse(["--log", "info"]), {
assert.deepEqual(parse(["--log", "info"]), {
...defaults,
log: "info",
verbose: false,
})
assert.equal(process.env.LOG_LEVEL, "info")
assert.equal(logger.level, Level.Info)
process.env.LOG_LEVEL = "warn"
assert.deepEqual(await parse(["--log", "info", "--verbose"]), {
assert.deepEqual(parse(["--log", "info", "--verbose"]), {
...defaults,
log: "trace",
verbose: true,
@@ -141,34 +141,31 @@ describe("cli", () => {
assert.equal(logger.level, Level.Trace)
})
it("should ignore invalid log level env var", async () => {
it("should ignore invalid log level env var", () => {
process.env.LOG_LEVEL = "bogus"
assert.deepEqual(await parse([]), defaults)
assert.deepEqual(parse([]), defaults)
})
it("should error if value isn't provided", async () => {
await assert.rejects(async () => await parse(["--auth"]), /--auth requires a value/)
await assert.rejects(async () => await parse(["--auth=", "--log=debug"]), /--auth requires a value/)
await assert.rejects(async () => await parse(["--auth", "--log"]), /--auth requires a value/)
await assert.rejects(async () => await parse(["--auth", "--invalid"]), /--auth requires a value/)
await assert.rejects(async () => await parse(["--bind-addr"]), /--bind-addr requires a value/)
it("should error if value isn't provided", () => {
assert.throws(() => parse(["--auth"]), /--auth requires a value/)
assert.throws(() => parse(["--auth=", "--log=debug"]), /--auth requires a value/)
assert.throws(() => parse(["--auth", "--log"]), /--auth requires a value/)
assert.throws(() => parse(["--auth", "--invalid"]), /--auth requires a value/)
assert.throws(() => parse(["--bind-addr"]), /--bind-addr requires a value/)
})
it("should error if value is invalid", async () => {
await assert.rejects(async () => await parse(["--port", "foo"]), /--port must be a number/)
await assert.rejects(async () => await parse(["--auth", "invalid"]), /--auth valid values: \[password, none\]/)
await assert.rejects(
async () => await parse(["--log", "invalid"]),
/--log valid values: \[trace, debug, info, warn, error\]/,
)
it("should error if value is invalid", () => {
assert.throws(() => parse(["--port", "foo"]), /--port must be a number/)
assert.throws(() => parse(["--auth", "invalid"]), /--auth valid values: \[password, none\]/)
assert.throws(() => parse(["--log", "invalid"]), /--log valid values: \[trace, debug, info, warn, error\]/)
})
it("should error if the option doesn't exist", async () => {
await assert.rejects(async () => await parse(["--foo"]), /Unknown option --foo/)
it("should error if the option doesn't exist", () => {
assert.throws(() => parse(["--foo"]), /Unknown option --foo/)
})
it("should not error if the value is optional", async () => {
assert.deepEqual(await parse(["--cert"]), {
it("should not error if the value is optional", () => {
assert.deepEqual(parse(["--cert"]), {
...defaults,
cert: {
value: undefined,
@@ -176,33 +173,30 @@ describe("cli", () => {
})
})
it("should not allow option-like values", async () => {
await assert.rejects(async () => await parse(["--socket", "--socket-path-value"]), /--socket requires a value/)
it("should not allow option-like values", () => {
assert.throws(() => parse(["--socket", "--socket-path-value"]), /--socket requires a value/)
// If you actually had a path like this you would do this instead:
assert.deepEqual(await parse(["--socket", "./--socket-path-value"]), {
assert.deepEqual(parse(["--socket", "./--socket-path-value"]), {
...defaults,
socket: path.resolve("--socket-path-value"),
})
await assert.rejects(
async () => await parse(["--cert", "--socket-path-value"]),
/Unknown option --socket-path-value/,
)
assert.throws(() => parse(["--cert", "--socket-path-value"]), /Unknown option --socket-path-value/)
})
it("should allow positional arguments before options", async () => {
assert.deepEqual(await parse(["foo", "test", "--auth", "none"]), {
it("should allow positional arguments before options", () => {
assert.deepEqual(parse(["foo", "test", "--auth", "none"]), {
...defaults,
_: ["foo", "test"],
auth: "none",
})
})
it("should support repeatable flags", async () => {
assert.deepEqual(await parse(["--proxy-domain", "*.coder.com"]), {
it("should support repeatable flags", () => {
assert.deepEqual(parse(["--proxy-domain", "*.coder.com"]), {
...defaults,
"proxy-domain": ["*.coder.com"],
})
assert.deepEqual(await parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), {
assert.deepEqual(parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), {
...defaults,
"proxy-domain": ["*.coder.com", "test.com"],
})

View File

@@ -1,43 +1,34 @@
import zip from "adm-zip"
import * as assert from "assert"
import * as fs from "fs-extra"
import * as http from "http"
import * as os from "os"
import * as path from "path"
import * as tar from "tar-fs"
import * as zlib from "zlib"
import { LatestResponse, UpdateHttpProvider } from "../src/node/app/update"
import { AuthType } from "../src/node/http"
import { SettingsProvider, UpdateSettings } from "../src/node/settings"
import { tmpdir } from "../src/node/util"
describe("update", () => {
const archivePath = path.join(tmpdir, "tests/updates/code-server-loose-source")
let version = "1.0.0"
let spy: string[] = []
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
if (!request.url) {
throw new Error("no url")
}
spy.push(request.url)
response.writeHead(200)
// Return the latest version.
if (request.url === "/latest") {
const latest: LatestResponse = {
name: version,
}
response.writeHead(200)
return response.end(JSON.stringify(latest))
}
const path = archivePath + (request.url.endsWith(".tar.gz") ? ".tar.gz" : ".zip")
const stream = fs.createReadStream(path)
stream.on("error", (error: NodeJS.ErrnoException) => {
response.writeHead(500)
response.end(error.message)
})
response.writeHead(200)
stream.on("close", () => response.end())
stream.pipe(response)
// Anything else is a 404.
response.writeHead(404)
response.end("not found")
})
const jsonPath = path.join(tmpdir, "tests/updates/update.json")
@@ -53,12 +44,10 @@ describe("update", () => {
_provider = new UpdateHttpProvider(
{
auth: AuthType.None,
base: "/update",
commit: "test",
},
true,
`http://${address.address}:${address.port}/latest`,
`http://${address.address}:${address.port}/download/{{VERSION}}/{{RELEASE_NAME}}`,
settings,
)
}
@@ -74,42 +63,8 @@ describe("update", () => {
host: "localhost",
})
})
const p = provider()
const archiveName = (await p.getReleaseName({ version: "9999999.99999.9999", checked: 0 })).replace(
/.tar.gz$|.zip$/,
"",
)
await fs.remove(path.join(tmpdir, "tests/updates"))
await fs.mkdirp(path.join(archivePath, archiveName))
await Promise.all([
fs.writeFile(path.join(archivePath, archiveName, "code-server"), `console.log("UPDATED")`),
fs.writeFile(path.join(archivePath, archiveName, "node"), `NODE BINARY`),
])
if (os.platform() === "darwin") {
await new Promise((resolve, reject) => {
const zipFile = new zip()
zipFile.addLocalFolder(archivePath)
zipFile.writeZip(archivePath + ".zip", (error) => {
return error ? reject(error) : resolve(error)
})
})
} else {
await new Promise((resolve, reject) => {
const write = fs.createWriteStream(archivePath + ".tar.gz")
const compress = zlib.createGzip()
compress.pipe(write)
compress.on("error", (error) => compress.destroy(error))
compress.on("close", () => write.end())
tar.pack(archivePath).pipe(compress)
write.on("close", reject)
write.on("finish", () => {
resolve()
})
})
}
await fs.mkdirp(path.join(tmpdir, "tests/updates"))
})
after(() => {
@@ -197,53 +152,15 @@ describe("update", () => {
assert.equal(p.isLatestVersion(update), true)
})
it("should download and apply an update", async () => {
version = "9999999.99999.9999"
const p = provider()
const update = await p.getUpdate(true)
// Create an existing version.
const destination = path.join(tmpdir, "tests/updates/code-server")
await fs.mkdirp(destination)
const entry = path.join(destination, "code-server")
await fs.writeFile(entry, `console.log("OLD")`)
assert.equal(`console.log("OLD")`, await fs.readFile(entry, "utf8"))
// Updating should replace the existing version.
await p.downloadAndApplyUpdate(update, destination)
assert.equal(`console.log("UPDATED")`, await fs.readFile(entry, "utf8"))
// There should be a backup.
const dir = (await fs.readdir(path.join(tmpdir, "tests/updates"))).filter((dir) => {
return dir.startsWith("code-server.")
})
assert.equal(dir.length, 1)
assert.equal(
`console.log("OLD")`,
await fs.readFile(path.join(tmpdir, "tests/updates", dir[0], "code-server"), "utf8"),
)
const archiveName = await p.getReleaseName(update)
assert.deepEqual(spy, ["/latest", `/download/${version}/${archiveName}`])
})
it("should not reject if unable to fetch", async () => {
const options = {
auth: AuthType.None,
base: "/update",
commit: "test",
}
let provider = new UpdateHttpProvider(options, true, "invalid", "invalid", settings)
let provider = new UpdateHttpProvider(options, true, "invalid", settings)
await assert.doesNotReject(() => provider.getUpdate(true))
provider = new UpdateHttpProvider(
options,
true,
"http://probably.invalid.dev.localhost/latest",
"http://probably.invalid.dev.localhost/download",
settings,
)
provider = new UpdateHttpProvider(options, true, "http://probably.invalid.dev.localhost/latest", settings)
await assert.doesNotReject(() => provider.getUpdate(true))
})
})

View File

@@ -792,10 +792,10 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@coder/logger@1.1.11":
version "1.1.11"
resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.11.tgz#e6f36dba9436ae61e66e3f66787d75c768617605"
integrity sha512-EEh1dqSU0AaqjjjMsVqumgZGbrZimKFKIb4t5E6o3FLfVUxJCReSME78Yj2N1xWUVAHMnqafDCxLostpuIotzw==
"@coder/logger@1.1.16":
version "1.1.16"
resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.16.tgz#ee5b1b188f680733f35c11b065bbd139d618c1e1"
integrity sha512-X6VB1++IkosYY6amRAiMvuvCf12NA4+ooX+gOuu5bJIkdjmh4Lz7QpJcWRdgxesvo1msriDDr9E/sDbIWf6vsQ==
"@iarna/toml@^2.2.0":
version "2.2.5"
@@ -910,13 +910,6 @@
traverse "^0.6.6"
unified "^6.1.6"
"@types/adm-zip@^0.4.32":
version "0.4.33"
resolved "https://registry.yarnpkg.com/@types/adm-zip/-/adm-zip-0.4.33.tgz#ea5b94f771443f655613b64f920c0555867200dd"
integrity sha512-WM0DCWFLjXtddl0fu0+iN2ZF+qz8RF9RddG5OSy/S90AQz01Fu8lHn/3oTIZDxvG8gVcnBLAHMHOdBLbV6m6Mw==
dependencies:
"@types/node" "*"
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -1130,11 +1123,6 @@ acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
adm-zip@^0.4.14:
version "0.4.14"
resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.14.tgz#2cf312bcc9f8875df835b0f6040bd89be0a727a9"
integrity sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
version "6.12.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
@@ -4258,9 +4246,9 @@ lodash.uniq@^4.5.0:
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
log-symbols@2.2.0, log-symbols@^2.2.0:
version "2.2.0"
@@ -6156,6 +6144,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rotating-file-stream@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/rotating-file-stream/-/rotating-file-stream-2.1.3.tgz#4b3cc8f56ae70b3e30ccdb4ee6b14d95e66b02bb"
integrity sha512-zZ4Tkngxispo7DgiTqX0s4ChLtM3qET6iYsDA9tmgDEqJ3BFgRq/ZotsKEDAYQt9pAn9JwwqT27CSwQt3CTxNg==
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"