diff --git a/README.md b/README.md
index 70a1c2ffc..f4956b077 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
# code-server
-
[!["Open Issues"](https://img.shields.io/github/issues-raw/cdr/code-server.svg)](https://github.com/cdr/code-server/issues)
[!["Latest Release"](https://img.shields.io/github/release/cdr/code-server.svg)](https://github.com/cdr/code-server/releases/latest)
[![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/cdr/code-server/blob/master/LICENSE)
@@ -23,18 +22,14 @@ docker run -it -p 127.0.0.1:8443:8443 -p 127.0.0.1:8444:8444 -v "$PWD:/home/code
![Screenshot](/doc/assets/ide.png)
## Getting Started
-
### Run over SSH
-
Use [sshcode](https://github.com/codercom/sshcode) for a simple setup.
### Docker
-
See docker oneliner mentioned above. Dockerfile is at
[/Dockerfile](/Dockerfile).
### Binaries
-
1. [Download a binary](https://github.com/cdr/code-server/releases) (Linux and
OS X supported. Windows coming soon)
2. Start the binary with the project directory as the first argument
@@ -65,28 +60,15 @@ How to [secure your setup](/doc/security/ssl.md).
compile the build directory as well.
- For now `@coder/nbin` is a global dependency.
- Run `yarn build ${codeServerVersion} ${vscodeVersion} ${target} ${arch}` in
- this directory (for example: `yarn build development 1.35.0 linux x64`).
+ this directory (for example: `yarn build development 1.36.0 linux x64`).
+- If you target the same VS Code version our Travis builds do everything will
+ work but if you target some other version it might not (we have to do some
+ patching to VS Code so different versions aren't always compatible).
- You can run the built code with `node path/to/build/out/vs/server/main.js` or run
`yarn binary` with the same arguments in the previous step to package the
code into a single binary.
-### Development
-
-```fish
-git clone https://github.com/microsoft/vscode
-cd vscode
-git clone https://github.com/cdr/code-server src/vs/server
-cd src/vs/server
-yarn patch:apply
-yarn
-yarn watch
-# Wait for the initial compilation to complete (it will say "Finished compilation").
-yarn start --allow-http --no-auth
-# Visit http://localhost:8443
-```
-
## Known Issues
-
- Creating custom VS Code extensions and debugging them doesn't work.
- To debug Golang using
[ms-vscode-go extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go),
@@ -102,31 +84,48 @@ yarn start --allow-http --no-auth
- Run VS Code unit tests against our builds to ensure features work as expected.
## Extensions
-
At the moment we can't use the official VSCode Marketplace. We've created a
custom extension marketplace focused around open-sourced extensions. However,
if you have access to the `.vsix` file, you can manually install the extension.
## Telemetry
+Use the `--disable-telemetry` flag to completely disable telemetry.
-Set the `telemetry.enableTelemetry` user setting to false to disable telemetry.
-
-We use data collected to improve code-server.
+We use the data collected to improve code-server.
## Contributing
-Development guides are coming soon.
+### Development
+```fish
+git clone https://github.com/microsoft/vscode
+cd vscode
+git clone https://github.com/cdr/code-server src/vs/server
+cd src/vs/server
+yarn patch:apply
+yarn
+yarn watch
+# Wait for the initial compilation to complete (it will say "Finished compilation").
+yarn start --allow-http --no-auth
+# Visit http://localhost:8443
+```
+
+### Upgrading VS Code
+We have to patch VS Code to provide and fix some functionality. As the web
+portion of VS Code matures, we'll be able to shrink and maybe even entirely
+eliminate our patch. In the meantime, however, upgrading the VS Code version
+requires ensuring that the patch still applies and has the intended effects.
+
+To generate a new patch, **stage all the changes** you want to be included in
+the patch in the VS Code source, then run `yarn patch:generate` in this
+directory.
## License
-
[MIT](LICENSE)
## Enterprise
-
Visit [our enterprise page](https://coder.com/enterprise) for more information
about our enterprise offering.
## Commercialization
-
If you would like to commercialize code-server, please contact
contact@coder.com.
diff --git a/doc/self-hosted/index.md b/doc/self-hosted/index.md
index f3a867f1c..f95f92f9c 100644
--- a/doc/self-hosted/index.md
+++ b/doc/self-hosted/index.md
@@ -1,14 +1,21 @@
# Getting Started
-[code-server](https://coder.com) is used by developers at Azure, Google, Reddit, and more to give them access to VS Code in the browser.
+[code-server](https://coder.com) is used by developers at Azure, Google,
+Reddit, and more to give them access to VS Code in the browser.
## Quickstart Guide
-> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
+> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide),
+> [tweet (@coderhq)](https://twitter.com/coderhq) or
+> [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
-This document pertains to Coder specific implementations of VS Code. For documentation on how to use VS Code itself, please refer to the official [documentation for VS Code](https://code.visualstudio.com/docs)
+This document pertains to Coder-specific implementations of VS Code. For
+documentation on how to use VS Code itself, please refer to the official
+[documentation for VS Code](https://code.visualstudio.com/docs)
-It takes just a few minutes to get your own self-hosted server running. If you've got a machine running macOS, Windows, or Linux, you're ready to start the binary which listens on port `8443` by default.
+It takes just a few minutes to get your own self-hosted server running. If
+you've got a machine running macOS, Windows, or Linux, you're ready to start
+the binary which listens on ports `8443` and `8444` by default.
+1. Visit [the releases](https://github.com/cdr/code-server/releases) page and
+ download the latest cli for your operating system.
+2. Double click the executable to run in the current directory.
+3. Copy the password that appears in the CLI.
+4. In your browser navigate to `localhost:8443`.
+5. Paste the password from the cli into the login window.
-1. Visit [the releases](https://github.com/cdr/code-server/releases) page and download the latest cli for your operating system
-2. Double click the executable to run in the current directory
-3. Copy the password that appears in the cli
-4. In your browser navigate to `localhost:8443`
-5. Paste the password from the cli into the login window
-> NOTE: Be careful with your password as sharing it will grant those users access to your server's file system
+> NOTE: Be careful with your password as sharing it will grant those users
+> access to your server's file system
### Things To Know
-- When you visit the IP for your code-server instance, you will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.
+- When you visit the IP for your code-server instance, you will be greeted with
+ a page similar to the following screenshot. Code-server is using a
+ self-signed SSL certificate for easy setup. In Chrome/Chromium, click
+ **"Advanced"** then click **"proceed anyway"**. In Firefox, click
+ **Advanced**, then **Add Exception**, then finally **Confirm Security
+ Exception**.
## Usage
-
code-server --help
-
-code-server can be ran with a number of arguments to customize your working directory, host, port, and SSL certificate.
-
```
-Usage: code-server [options]
-
-Run VS Code on a remote server.
-
-Options:
- -V, --version output the version number
- --cert
- --cert-key
- -e, --extensions-dir Override the main default path for user extensions.
- --extra-extensions-dir [dir] Path to an extra user extension directory (repeatable). (default: [])
- --extra-builtin-extensions-dir [dir] Path to an extra built-in extension directory (repeatable). (default: [])
- -d, --user-data-dir Specifies the directory that user data is kept in, useful when running as root.
- -h, --host Customize the hostname. (default: "0.0.0.0")
- -o, --open Open in the browser on startup.
- -p, --port Port to bind on. (default: 8443)
- -N, --no-auth Start without requiring authentication.
- -H, --allow-http Allow http connections.
- --disable-telemetry Disables ALL telemetry.
- --socket Listen on a UNIX socket. Host and port will be ignored when set.
- --trust-proxy Trust the X-Forwarded-For header, useful when using a reverse proxy.
- --install-extension Install an extension by its ID.
- -h, --help output usage information
+code-server --help
```
- ### Data Directory
- Use `code-server -d (path/to/directory)` or `code-server --user-data-dir=(path/to/directory)`, excluding the parentheses to specify the root folder that VS Code will start in.
+code-server can be ran with a number of arguments to customize your working
+directory, host, port, and SSL certificate.
- ### Host
- By default, code-server will use `0.0.0.0` as its address. This can be changed by using `code-server -h` or `code-server --host=` followed by the address you want to use.
- > Example: `code-server -h 127.0.0.1`
+### Data Directory
+Use `code-server --user-data-dir path/to/directory` to specify the root folder
+that VS Code will start in.
- ### Open
- You can have the server automatically open the VS Code in your browser on startup by using the `code-server -o` or `code-server --open` flags
+### Host
+By default, code-server will use `127.0.0.1` for insecure connections and
+`0.0.0.0` for secure connections. This can be changed by using
+`code-server --host `.
- ### Port
- By default, code-server will use `8443` as its port. This can be changed by using `code-server -p` or `code-server --port=` followed by the port you want to use.
- > Example: `code-server -p 9000`
+> Example: `code-server --host 127.0.0.1`
- ### Telemetry
- Disable all telemetry with `code-server --disable-telemetry`.
+### Open
+You can have the server automatically open the VS Code in your browser on
+startup by using the `code-server -o` or `code-server --open` flags
- ### Cert and Cert Key
- To encrypt the traffic between the browser and server use `code-server --cert=` followed by the path to your `.cer` file. Additionally, you can use certificate keys with `code-server --cert-key` followed by the path to your `.key` file.
-> Example (certificate and key): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.cer --cert-key /etc/letsencrypt/live/example.com/fullchain.key`
-> Example (if you are using Letsencrypt or similar): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.pem --cert-key /etc/letsencrypt/live/example.com/privkey.key`
+### Port
+By default, code-server will use `8443` as its port. This can be changed by
+using `code-server -p` or `code-server --port=` followed by the port you want
+to use.
-> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../security/ssl.md)
+> Example: `code-server -p 9000`
- ### Nginx Reverse Proxy
- Below is a virtual host example that works with code-server. Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the proxy to connect. You can also use Let's Encrypt to get a SSL certificates for free.
- ```
- server {
- listen 80;
- listen [::]:80;
- server_name code.example.com code.example.org;
- location / {
- proxy_pass http://localhost:8443/;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection upgrade;
- proxy_set_header Accept-Encoding gzip;
- }
+### Cert and Cert Key
+To encrypt the traffic between the browser and server use `code-server --cert`
+followed by the path to your `.cer` file. Additionally, you can use certificate
+keys with `code-server --cert-key` followed by the path to your `.key` file.
+
+Example:
+```
+code-server --cert /path/to/certificate/fullchain.cer --cert-key /path/to/certificate/fullchain.key
+```
+
+Example for Let's Encrypt:
+```
+code-server --cert /etc/letsencrypt/live/example.com/fullchain.pem --cert-key /etc/letsencrypt/live/example.com/privkey.key
+```
+
+To ensure the connection between you and your server is encrypted view our
+guide on [securing your setup](../security/ssl.md).
+
+### Nginx Reverse Proxy
+Below is a virtual host example that works with code-server. Please also pass
+`--allow-http` and `--trust-proxy` to code-server to allow the proxy to
+connect. You can also use Let's Encrypt to get a SSL certificates for free.
+
+```
+server {
+ listen 80;
+ listen [::]:80;
+ server_name code.example.com code.example.org;
+ location / {
+ proxy_pass http://localhost:8443/;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection upgrade;
+ proxy_set_header Accept-Encoding gzip;
}
- ```
+}
+```
- ### Apache Reverse Proxy
- Example of a HTTPS virtualhost configuration for Apache as a reverse proxy. Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the proxy to connect. You can also use Let's Encrypt to get a SSL certificates for free.
- ```
-
- ServerName code.example.com
+### Apache Reverse Proxy
+Example of an HTTPS virtualhost configuration for Apache as a reverse proxy.
+Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the
+proxy to connect. You can also use Let's Encrypt to get a SSL certificates for
+free.
- RewriteEngine On
- RewriteCond %{HTTP:Upgrade} =websocket [NC]
- RewriteRule /(.*) ws://localhost:8443/$1 [P,L]
- RewriteCond %{HTTP:Upgrade} !=websocket [NC]
- RewriteRule /(.*) http://localhost:8443/$1 [P,L]
+```
+
+ ServerName code.example.com
- ProxyRequests off
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} =websocket [NC]
+ RewriteRule /(.*) ws://localhost:8443/$1 [P,L]
+ RewriteCond %{HTTP:Upgrade} !=websocket [NC]
+ RewriteRule /(.*) http://localhost:8443/$1 [P,L]
- RequestHeader set X-Forwarded-Proto https
- RequestHeader set X-Forwarded-Port 443
+ ProxyRequests off
- ProxyPass / http://localhost:8443/ nocanon
- ProxyPassReverse / http://localhost:8443/
+ RequestHeader set X-Forwarded-Proto https
+ RequestHeader set X-Forwarded-Port 443
-
- ```
- *Important:* For more details about Apache reverse proxy configuration checkout the [documentation](https://httpd.apache.org/docs/current/mod/mod_proxy.html) - especially the [Securing your Server](https://httpd.apache.org/docs/current/mod/mod_proxy.html#access) section
+ ProxyPass / http://localhost:8443/ nocanon
+ ProxyPassReverse / http://localhost:8443/
- ### Help
- Use `code-server --help` to view the usage for the CLI. This is also shown at the beginning of this section.
+
+```
+*Important:* For more details about Apache reverse proxy configuration checkout
+the [documentation](https://httpd.apache.org/docs/current/mod/mod_proxy.html) -
+especially the [Securing your Server](https://httpd.apache.org/docs/current/mod/mod_proxy.html#access)
+section.
+
+
+### Help
+Use `code-server --help` to view the usage for the CLI.
diff --git a/scripts/vscode.patch b/scripts/vscode.patch
index c9dcce8c3..e97e530ca 100644
--- a/scripts/vscode.patch
+++ b/scripts/vscode.patch
@@ -214,6 +214,103 @@ index e09049c5b9..d93ffa527a 100644
-}
\ No newline at end of file
+}
+diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts
+index 9f68b645b6..fe380bb6f8 100644
+--- a/src/vs/platform/log/common/logIpc.ts
++++ b/src/vs/platform/log/common/logIpc.ts
+@@ -26,6 +26,7 @@ export class LogLevelSetterChannel implements IServerChannel {
+ call(_: unknown, command: string, arg?: any): Promise {
+ switch (command) {
+ case 'setLevel': this.service.setLevel(arg); return Promise.resolve();
++ case 'getLevel': return Promise.resolve(this.service.getLevel());
+ }
+
+ throw new Error(`Call not found: ${command}`);
+@@ -40,6 +41,10 @@ export class LogLevelSetterChannelClient {
+ return this.channel.listen('onDidChangeLogLevel');
+ }
+
++ getLevel(): Promise {
++ return this.channel.call('getLevel');
++ }
++
+ setLevel(level: LogLevel): void {
+ this.channel.call('setLevel', level);
+ }
+@@ -56,4 +61,4 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi
+ setLevel(level: LogLevel): void {
+ this.master.setLevel(level);
+ }
+-}
+\ No newline at end of file
++}
+diff --git a/src/vs/platform/telemetry/node/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts
+index 8e1b68eb36..2b6a0d5b15 100644
+--- a/src/vs/platform/telemetry/node/telemetryIpc.ts
++++ b/src/vs/platform/telemetry/node/telemetryIpc.ts
+@@ -6,6 +6,9 @@
+ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+ import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
+ import { Event } from 'vs/base/common/event';
++import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
++import { ITelemetryData } from 'vs/base/common/actions';
++import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
+
+ export interface ITelemetryLog {
+ eventName: string;
+@@ -41,3 +44,52 @@ export class TelemetryAppenderClient implements ITelemetryAppender {
+ // TODO
+ }
+ }
++
++export class TelemetryChannel implements IServerChannel {
++
++ constructor(private service: ITelemetryService) {}
++
++ listen(_: unknown, event: string): Event {
++ throw new Error(`Invalid listen ${event}`);
++ }
++
++ call(_: unknown, command: string, args?: any): Promise {
++ switch (command) {
++ case 'publicLog': return this.service.publicLog(args[0], args[1], args[2]);
++ case 'publicLog2': return this.service.publicLog2(args[0], args[1], args[2]);
++ case 'setEnabled': return Promise.resolve(this.service.setEnabled(args[0]));
++ case 'getTelemetryInfo': return this.service.getTelemetryInfo();
++ }
++
++ throw new Error(`Invalid call ${command}`);
++ }
++}
++
++export class TelemetryChannelClient implements ITelemetryService {
++
++ _serviceBrand: any;
++
++ constructor(
++ private readonly channel: IChannel,
++ ) { }
++
++ public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise {
++ return this.channel.call('publicLog', [eventName, data, anonymizeFilePaths]);
++ }
++
++ public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck, anonymizeFilePaths?: boolean): Promise {
++ return this.channel.call('publicLog2', [eventName, data, anonymizeFilePaths]);
++ }
++
++ public setEnabled(value: boolean): void {
++ this.channel.call('setEnable', [value]);
++ }
++
++ public getTelemetryInfo(): Promise {
++ return this.channel.call('getTelemetryInfo');
++ }
++
++ public get isOptedIn(): boolean {
++ return true;
++ }
++}
diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts
index 1986fb6642..afbe385af6 100644
--- a/src/vs/workbench/browser/web.main.ts
@@ -236,21 +333,22 @@ index 1986fb6642..afbe385af6 100644
\ No newline at end of file
+}
diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts
-index b253e573ae..2e4dfb393a 100644
+index b253e573ae..bde667d045 100644
--- a/src/vs/workbench/browser/web.simpleservices.ts
+++ b/src/vs/workbench/browser/web.simpleservices.ts
-@@ -53,6 +53,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
+@@ -53,6 +53,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
import { IProcessEnvironment } from 'vs/base/common/platform';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc';
-+import { ExtensionGalleryChannelClient } from "vs/platform/extensionManagement/node/extensionGalleryIpc";
++import { ExtensionGalleryChannelClient } from 'vs/platform/extensionManagement/node/extensionGalleryIpc';
++import { TelemetryChannelClient } from 'vs/platform/telemetry/node/telemetryIpc';
+import { IProductService } from 'vs/platform/product/common/product';
//#region Backup File
-@@ -125,13 +129,11 @@ export class SimpleClipboardService implements IClipboardService {
+@@ -125,13 +130,11 @@ export class SimpleClipboardService implements IClipboardService {
writeText(text: string, type?: string): void { }
readText(type?: string): string {
@@ -266,7 +364,7 @@ index b253e573ae..2e4dfb393a 100644
}
writeFindText(text: string): void { }
-@@ -239,7 +241,17 @@ export class SimpleExtensionGalleryService implements IExtensionGalleryService {
+@@ -239,7 +242,17 @@ export class SimpleExtensionGalleryService implements IExtensionGalleryService {
}
}
@@ -285,7 +383,7 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
-@@ -262,7 +274,7 @@ export class SimpleExtensionsWorkbenchService implements IExtensionsWorkbenchSer
+@@ -262,7 +275,7 @@ export class SimpleExtensionsWorkbenchService implements IExtensionsWorkbenchSer
checkForUpdates: any;
allowedBadgeProviders: string[];
}
@@ -294,7 +392,7 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
//#region ICommentService
-@@ -375,7 +387,10 @@ export class SimpleExtensionTipsService implements IExtensionTipsService {
+@@ -375,7 +388,10 @@ export class SimpleExtensionTipsService implements IExtensionTipsService {
}
getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } {
@@ -306,7 +404,7 @@ index b253e573ae..2e4dfb393a 100644
}
}
-@@ -436,7 +451,16 @@ export class SimpleExtensionManagementService implements IExtensionManagementSer
+@@ -436,7 +452,16 @@ export class SimpleExtensionManagementService implements IExtensionManagementSer
}
}
@@ -324,7 +422,24 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
-@@ -1288,4 +1312,4 @@ class SimpleTunnelService implements ITunnelService {
+@@ -680,7 +705,15 @@ export class SimpleTelemetryService implements ITelemetryService {
+ }
+ }
+
+-registerSingleton(ITelemetryService, SimpleTelemetryService);
++// registerSingleton(ITelemetryService, SimpleTelemetryService);
++class TelemetryService extends TelemetryChannelClient {
++ public constructor(
++ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
++ ) {
++ super(remoteAgentService.getConnection()!.getChannel('telemetry'));
++ }
++}
++registerSingleton(ITelemetryService, TelemetryService);
+
+ //#endregion
+
+@@ -1288,4 +1321,4 @@ class SimpleTunnelService implements ITunnelService {
registerSingleton(ITunnelService, SimpleTunnelService);
@@ -911,6 +1026,20 @@ index c08a6e37c1..31640d7e66 100644
}
return this._extensionAllowedBadgeProviders;
}
+diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
+index 9235c739fb..32d203eb32 100644
+--- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts
++++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
+@@ -55,7 +55,8 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib
+ const connection = remoteAgentService.getConnection();
+ if (connection) {
+ const logLevelClient = new LogLevelSetterChannelClient(connection.getChannel('loglevel'));
+- logLevelClient.setLevel(logService.getLevel());
++ logLevelClient.getLevel().then((level) => logService.setLevel(level));
++ logLevelClient.onDidChangeLogLevel((level) => logService.setLevel(level));
+ this._register(logService.onDidChangeLogLevel(level => logLevelClient.setLevel(level)));
+ }
+ }
diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts
index 3525569601..a91a5fce7d 100644
--- a/src/vs/workbench/services/environment/browser/environmentService.ts
diff --git a/src/channel.ts b/src/channel.ts
index fa6e606f3..fbe61b3a3 100644
--- a/src/channel.ts
+++ b/src/channel.ts
@@ -15,6 +15,7 @@ import { ILogService } from "vs/platform/log/common/log";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
+import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider";
@@ -181,6 +182,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
public constructor(
private readonly environment: IEnvironmentService,
private readonly log: ILogService,
+ private readonly telemetry: ITelemetryService,
) {}
public listen(_: unknown, event: string): Event {
@@ -271,7 +273,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
throw new Error("not implemented");
}
- private disableTelemetry(): Promise {
- throw new Error("not implemented");
+ private async disableTelemetry(): Promise {
+ this.telemetry.setEnabled(false);
}
}
diff --git a/src/cli.ts b/src/cli.ts
index 5a7abe80b..05c3dd8b4 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -5,8 +5,8 @@ import { validatePaths } from "vs/code/node/paths";
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import { ParsedArgs } from "vs/platform/environment/common/environment";
import { buildHelpMessage, buildVersionMessage, options } from "vs/platform/environment/node/argv";
-import product from "vs/platform/product/node/product";
import pkg from "vs/platform/product/node/package";
+import product from "vs/platform/product/node/product";
import { MainServer, WebviewServer } from "vs/server/src/server";
import "vs/server/src/tar";
diff --git a/src/insights.ts b/src/insights.ts
index bb94c7378..031bd3cb3 100644
--- a/src/insights.ts
+++ b/src/insights.ts
@@ -1,25 +1,16 @@
-/**
- * Used by node
- */
import * as https from "https";
import * as os from "os";
-export const defaultClient = "filler";
+import * as appInsights from "applicationinsights";
+
+export class TelemetryClient implements appInsights.TelemetryClient {
+ public config: any = {};
-export class TelemetryClient {
public channel = {
setUseDiskRetryCaching: (): void => undefined,
};
- public constructor() {
- //
- }
-
- public trackEvent(options: {
- name: string;
- properties: object;
- measurements: object;
- }): void {
+ public trackEvent(options: appInsights.EventTelemetry): void {
if (!options.properties) {
options.properties = {};
}
@@ -29,41 +20,20 @@ export class TelemetryClient {
try {
const cpus = os.cpus();
- // tslint:disable-next-line:no-any
- (options.measurements as any).cpu = {
- model: cpus[0].model,
- cores: cpus.length,
- };
- } catch (ex) {
- // Nothin
- }
+ options.measurements.cores = cpus.length;
+ options.properties["common.cpuModel"] = cpus[0].model;
+ } catch (error) {}
try {
- // tslint:disable-next-line:no-any
- (options.measurements as any).memory = {
- virtual_free: os.freemem(),
- virtual_used: os.totalmem(),
- };
- } catch (ex) {
- //
- }
+ options.measurements.memoryFree = os.freemem();
+ options.measurements.memoryTotal = os.totalmem();
+ } catch (error) {}
try {
- // tslint:disable:no-any
- (options.properties as any)["common.shell"] = os.userInfo().shell;
- (options.properties as any)["common.release"] = os.release();
- (options.properties as any)["common.arch"] = os.arch();
- // tslint:enable:no-any
- } catch (ex) {
- //
- }
-
- try {
- // tslint:disable-next-line:no-any
- (options.properties as any)["common.machineId"] = machineIdSync();
- } catch (ex) {
- //
- }
+ options.properties["common.shell"] = os.userInfo().shell;
+ options.properties["common.release"] = os.release();
+ options.properties["common.arch"] = os.arch();
+ } catch (error) {}
try {
const request = https.request({
@@ -75,96 +45,15 @@ export class TelemetryClient {
"Content-Type": "application/json",
},
});
- request.on("error", () => {
- // Do nothing, we don"t really care
- });
+ request.on("error", () => { /* We don't care. */ });
request.write(JSON.stringify(options));
request.end();
- } catch (ex) {
- // Suppress all errs
+ } catch (error) {}
+ }
+
+ public flush(options: appInsights.FlushOptions): void {
+ if (options.callback) {
+ options.callback("");
}
}
-
- public flush(options: {
- readonly callback: () => void;
- }): void {
- options.callback();
- }
}
-
-// Taken from https://github.com/automation-stack/node-machine-id
-import { exec, execSync } from "child_process";
-import { createHash } from "crypto";
-
-const isWindowsProcessMixedOrNativeArchitecture = (): "" | "mixed" | "native" => {
- // detect if the node binary is the same arch as the Windows OS.
- // or if this is 32 bit node on 64 bit windows.
- if (process.platform !== "win32") {
- return "";
- }
- if (process.arch === "ia32" && process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432")) {
- return "mixed";
- }
-
- return "native";
-};
-
-let { platform } = process,
- win32RegBinPath = {
- native: "%windir%\\System32",
- mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32",
- "": "",
- },
- guid = {
- darwin: "ioreg -rd1 -c IOPlatformExpertDevice",
- win32: `${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG ` +
- "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography " +
- "/v MachineGuid",
- linux: "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :",
- freebsd: "kenv -q smbios.system.uuid || sysctl -n kern.hostuuid",
- // tslint:disable-next-line:no-any
- } as any;
-
-const hash = (guid: string): string => {
- return createHash("sha256").update(guid).digest("hex");
-};
-
-const expose = (result: string): string => {
- switch (platform) {
- case "darwin":
- return result
- .split("IOPlatformUUID")[1]
- .split("\n")[0].replace(/\=|\s+|\"/ig, "")
- .toLowerCase();
- case "win32":
- return result
- .toString()
- .split("REG_SZ")[1]
- .replace(/\r+|\n+|\s+/ig, "")
- .toLowerCase();
- case "linux":
- return result
- .toString()
- .replace(/\r+|\n+|\s+/ig, "")
- .toLowerCase();
- case "freebsd":
- return result
- .toString()
- .replace(/\r+|\n+|\s+/ig, "")
- .toLowerCase();
- default:
- throw new Error(`Unsupported platform: ${process.platform}`);
- }
-};
-
-let cachedMachineId: string | undefined;
-
-const machineIdSync = (): string => {
- if (cachedMachineId) {
- return cachedMachineId;
- }
- let id: string = expose(execSync(guid[platform]).toString());
- cachedMachineId = hash(id);
-
- return cachedMachineId;
-};
diff --git a/src/server.ts b/src/server.ts
index cafd76fdf..7653fbb37 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -11,6 +11,7 @@ import * as querystring from "querystring";
import { Emitter } from "vs/base/common/event";
import { sanitizeFilePath } from "vs/base/common/extpath";
import { UriComponents, URI } from "vs/base/common/uri";
+import { getMachineId } from 'vs/base/node/id';
import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc";
import { mkdirp } from "vs/base/node/pfs";
import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner";
@@ -34,19 +35,25 @@ import { getLogLevel, ILogService } from "vs/platform/log/common/log";
import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc";
import { SpdLogService } from "vs/platform/log/node/spdlogService";
import { IProductConfiguration } from "vs/platform/product/common/product";
+import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection";
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/platform/remote/common/remoteAgentFileSystemChannel";
import { IRequestService } from "vs/platform/request/node/request";
import { RequestService } from "vs/platform/request/node/requestService";
+import ErrorTelemetry from "vs/platform/telemetry/browser/errorTelemetry";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
-import { NullTelemetryService } from "vs/platform/telemetry/common/telemetryUtils";
+import { NullTelemetryService, LogAppender, combinedAppender } from "vs/platform/telemetry/common/telemetryUtils";
+import { TelemetryService, ITelemetryServiceConfig } from "vs/platform/telemetry/common/telemetryService";
+import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender";
+import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProperties";
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
-// import { TelemetryService } from "vs/workbench/services/telemetry/electron-browser/telemetryService";
+import { TelemetryChannel } from "vs/platform/telemetry/node/telemetryIpc";
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/server/src/connection";
import { ExtensionEnvironmentChannel, FileProviderChannel , } from "vs/server/src/channel";
+import { TelemetryClient } from "vs/server/src/insights";
import { Protocol } from "vs/server/src/protocol";
import { getMediaMime, getUriTransformer, useHttpsTransformer } from "vs/server/src/util";
@@ -363,6 +370,7 @@ export class MainServer extends Server {
private readonly connections = new Map>();
private readonly services = new ServiceCollection();
+ private readonly servicesPromise: Promise;
public constructor(
options: ServerOptions,
@@ -381,39 +389,7 @@ export class MainServer extends Server {
protocol.getSocket().dispose();
}
});
-
- const environmentService = new EnvironmentService(args, process.execPath);
- const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
- this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
-
- const router = new StaticRouter((context: any) => {
- return context.clientId === "renderer";
- });
-
- this.services.set(ILogService, logService);
- this.services.set(IEnvironmentService, environmentService);
- this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
- this.services.set(IRequestService, new SyncDescriptor(RequestService));
- this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
- this.services.set(ITelemetryService, NullTelemetryService); // TODO: telemetry
- this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
- this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
-
- const instantiationService = new InstantiationService(this.services);
-
- this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
-
- instantiationService.invokeFunction(() => {
- instantiationService.createInstance(LogsDataCleaner);
- this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
- this.ipc.registerChannel("remoteextensionsenvironment", new ExtensionEnvironmentChannel(environmentService, logService));
- const extensionsService = this.services.get(IExtensionManagementService) as IExtensionManagementService;
- const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
- this.ipc.registerChannel("extensions", extensionsChannel);
- const galleryService = this.services.get(IExtensionGalleryService) as IExtensionGalleryService;
- const galleryChannel = new ExtensionGalleryChannel(galleryService);
- this.ipc.registerChannel("gallery", galleryChannel);
- });
+ this.servicesPromise = this.initializeServices(args);
}
public async listen(): Promise {
@@ -456,7 +432,11 @@ export class MainServer extends Server {
const remoteAuthority = request.headers.host as string;
const transformer = getUriTransformer(remoteAuthority);
- await this.webviewServer.listen();
+ await Promise.all([
+ this.webviewServer.listen(),
+ this.servicesPromise,
+ ]);
+
const webviewEndpoint = this.webviewServer.address(request);
const cwd = process.env.VSCODE_CWD || process.cwd();
@@ -577,6 +557,69 @@ export class MainServer extends Server {
}
}
+ private async initializeServices(args: ParsedArgs): Promise {
+ const environmentService = new EnvironmentService(args, process.execPath);
+ const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
+ this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
+
+ const router = new StaticRouter((context: any) => {
+ return context.clientId === "renderer";
+ });
+
+ this.services.set(ILogService, logService);
+ this.services.set(IEnvironmentService, environmentService);
+ this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
+ this.services.set(IRequestService, new SyncDescriptor(RequestService));
+ this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
+ if (!environmentService.args["disable-telemetry"]) {
+ const version = `${(pkg as any).codeServerVersion || "development"}-vsc${pkg.version}`;
+ this.services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [{
+ appender: combinedAppender(
+ new AppInsightsAppender("code-server", null, () => new TelemetryClient(), logService),
+ new LogAppender(logService),
+ ),
+ commonProperties: resolveCommonProperties(
+ product.commit, version, await getMachineId(),
+ environmentService.installSourcePath, "code-server",
+ ),
+ piiPaths: [
+ environmentService.appRoot,
+ environmentService.extensionsPath,
+ ...environmentService.extraExtensionPaths,
+ ...environmentService.extraBuiltinExtensionPaths,
+ ],
+ } as ITelemetryServiceConfig]));
+ } else {
+ this.services.set(ITelemetryService, NullTelemetryService);
+ }
+ this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
+ this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
+
+ const instantiationService = new InstantiationService(this.services);
+
+ this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
+
+ return new Promise((resolve) => {
+ instantiationService.invokeFunction(() => {
+ instantiationService.createInstance(LogsDataCleaner);
+ this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
+ const telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
+ this.ipc.registerChannel("remoteextensionsenvironment", new ExtensionEnvironmentChannel(environmentService, logService, telemetryService));
+ const extensionsService = this.services.get(IExtensionManagementService) as IExtensionManagementService;
+ const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
+ this.ipc.registerChannel("extensions", extensionsChannel);
+ const galleryService = this.services.get(IExtensionGalleryService) as IExtensionGalleryService;
+ const galleryChannel = new ExtensionGalleryChannel(galleryService);
+ this.ipc.registerChannel("gallery", galleryChannel);
+ const telemetryChannel = new TelemetryChannel(telemetryService);
+ this.ipc.registerChannel("telemetry", telemetryChannel);
+ // tslint:disable-next-line no-unused-expression
+ new ErrorTelemetry(telemetryService);
+ resolve();
+ });
+ });
+ }
+
/**
* TODO: implement.
*/