Update VS Code to 1.37.0

This commit is contained in:
Asher 2019-08-09 18:50:05 -05:00
parent b257c60636
commit ba7285192c
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A
21 changed files with 644 additions and 1589 deletions

View File

@ -8,14 +8,14 @@ matrix:
- os: linux
dist: trusty
env:
- VSCODE_VERSION="1.36.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="linux"
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="linux"
- os: linux
dist: trusty
env:
- VSCODE_VERSION="1.36.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="alpine"
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="alpine"
- os: osx
env:
- VSCODE_VERSION="1.36.1" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER"
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER"
before_install:
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export MINIFY="true"; fi
script:

View File

@ -81,7 +81,7 @@ data collected to improve code-server.
```shell
git clone https://github.com/microsoft/vscode
cd vscode
git checkout 1.36.1
git checkout 1.37.0
git clone https://github.com/cdr/code-server src/vs/server
cd src/vs/server
yarn patch:apply
@ -109,23 +109,19 @@ directory.
Our changes include:
- Add a `code-server` schema.
- Make the extension sidebar work in the browser. Mostly involves removing
Node-specific code for the `extensions` channel client and adding a
`gallery` channel.
- Allow multiple extension directories (both user and built-in).
- Rewrite assets used in the CSS (like icons) or as images to use the base URL.
- Change the loader to use the base URL.
- Change the web socket to use the base URL and TLS if necessary.
- Set the favicon using a relative path.
- Modify the file service to support writing from an asynchronous stream (for
uploading files).
- Rewrite assets requested by the browser to use the base URL.
- Modify the loader to use the base URL.
- Modify the web socket to use the base URL and TLS if necessary.
- Send client-side telemetry through the server.
- Add a file prefix to ignore for temporary files created during upload.
- Insert our upload service for use in editor windows and explorer.
- Modify the log level to get its initial setting from the server.
- Get telemetry working by adding a channel for it.
- Change a regular expression used for mnemonics so it works on Firefox.
- Make it possible for us to load code on the client.
- Modify the build process to include our code.
- Fix a CSP issue within a webview.
- Fix an issue displaying extension contributions.
## License
[MIT](LICENSE)

58
scripts/build-json.js Normal file
View File

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

View File

@ -1,23 +0,0 @@
// This is used to merge JSON files (package.json and product.json) and delete a
// few entries we don't want. It's extremely simple, expects very specific
// input, and doesn't have any error handling.
const fs = require("fs");
const a = process.argv[2];
const b = process.argv[3];
const out = process.argv[4];
const json = JSON.parse(process.argv[5] || "{}");
const aJson = JSON.parse(fs.readFileSync(a));
const bJson = JSON.parse(fs.readFileSync(b));
delete aJson.scripts;
delete aJson.dependencies;
delete aJson.devDependencies;
delete aJson.optionalDependencies;
fs.writeFileSync(out, JSON.stringify({
...aJson,
...bJson,
...json,
}, null, 2));

View File

@ -56,9 +56,9 @@ function prepend-loader() {
# Copy code-server into VS Code then build it.
function build-code-server() {
copy-server
yarn gulp compile-build --max-old-space-size=32384
local min=""
export BUILD_SOURCEVERSION
BUILD_SOURCEVERSION=$(node -p "require('${sourcePath}/build/lib/git.js').getVersion('${rootPath}')")
if [[ -n "${minify}" ]] ; then
min="-min"
yarn gulp minify-vscode --max-old-space-size=32384
@ -74,11 +74,9 @@ function build-code-server() {
cd "${buildPath}" && yarn --production --force --build-from-source
rm "${buildPath}/"{package.json,yarn.lock,.yarnrc}
local packageJson="{\"codeServerVersion\": \"${codeServerVersion}-vsc${vscodeVersion}\"}"
cp -r "${sourcePath}/.build/extensions" "${buildPath}"
node "${rootPath}/scripts/merge.js" "${sourcePath}/package.json" "${rootPath}/scripts/package.json" "${buildPath}/package.json" "${packageJson}"
node "${rootPath}/scripts/merge.js" "${sourcePath}/.build/product.json" "${rootPath}/scripts/product.json" "${buildPath}/product.json"
cp -r "${sourcePath}/out-vscode${min}" "${buildPath}/out"
node "${rootPath}/scripts/build-json.js" "${sourcePath}" "${buildPath}" "${vscodeVersion}" "${codeServerVersion}"
# Only keep production dependencies for the server.
cp "${rootPath}/"{package.json,yarn.lock} "${buildPath}/out/vs/server"
@ -143,7 +141,7 @@ function build-task() {
log "Pre-built VS Code ${vscodeVersion} has no built extensions" "error"
exit 1
fi
yarn gulp extensions-build-package --max-old-space-size=32384
yarn gulp compile-extensions-build --max-old-space-size=32384
fi
build-code-server
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,34 @@
import * as vscode from "vscode";
import { createCSSRule } from "vs/base/browser/dom";
import { Emitter, Event } from "vs/base/common/event";
import { IDisposable } from "vs/base/common/lifecycle";
import { URI } from "vs/base/common/uri";
import { generateUuid } from "vs/base/common/uuid";
import { localize } from "vs/nls";
import { SyncActionDescriptor } from "vs/platform/actions/common/actions";
import { Registry } from "vs/platform/registry/common/platform";
import { IWorkbenchActionRegistry, Extensions as ActionExtensions} from "vs/workbench/common/actions";
import { CommandsRegistry, ICommandService } from "vs/platform/commands/common/commands";
import { IStat, IWatchOptions, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileChange, FileWriteOptions, FileSystemProviderCapabilities, IFileService, FileType, IFileSystemProvider } from "vs/platform/files/common/files";
import { IStorageService } from "vs/platform/storage/common/storage";
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
import { IContextMenuService } from "vs/platform/contextview/browser/contextView";
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions } from "vs/platform/files/common/files";
import { IInstantiationService, ServiceIdentifier } from "vs/platform/instantiation/common/instantiation";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { INotificationService } from "vs/platform/notification/common/notification";
import { Emitter, Event } from "vs/base/common/event";
import * as extHostTypes from "vs/workbench/api/common/extHostTypes";
import { ServiceIdentifier, IInstantiationService } from "vs/platform/instantiation/common/instantiation";
import { URI } from "vs/base/common/uri";
import { ITreeItem, ITreeViewDataProvider, IViewsRegistry, ITreeViewDescriptor, Extensions as ViewsExtensions, IViewContainersRegistry, TreeItemCollapsibleState } from "vs/workbench/common/views";
import { CustomTreeViewPanel, CustomTreeView } from "vs/workbench/browser/parts/views/customView";
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from "vs/workbench/browser/viewlet";
import { IExtensionService } from "vs/workbench/services/extensions/common/extensions";
import { ViewContainerViewlet } from "vs/workbench/browser/parts/views/viewsViewlet";
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
import { IWorkbenchLayoutService } from "vs/workbench/services/layout/browser/layoutService";
import { Registry } from "vs/platform/registry/common/platform";
import { IStorageService } from "vs/platform/storage/common/storage";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import { IEditorService } from "vs/workbench/services/editor/common/editorService";
import { IThemeService } from "vs/platform/theme/common/themeService";
import { IContextMenuService } from "vs/platform/contextview/browser/contextView";
import { IViewletService } from "vs/workbench/services/viewlet/browser/viewlet";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import * as extHostTypes from "vs/workbench/api/common/extHostTypes";
import { CustomTreeView, CustomTreeViewPanel } from "vs/workbench/browser/parts/views/customView";
import { ViewContainerViewlet } from "vs/workbench/browser/parts/views/viewsViewlet";
import { Extensions as ViewletExtensions, ShowViewletAction, ViewletDescriptor, ViewletRegistry } from "vs/workbench/browser/viewlet";
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from "vs/workbench/common/actions";
import { Extensions as ViewsExtensions, ITreeItem, ITreeViewDataProvider, ITreeViewDescriptor, IViewContainersRegistry, IViewsRegistry, TreeItemCollapsibleState } from "vs/workbench/common/views";
import { IEditorGroupsService } from "vs/workbench/services/editor/common/editorGroupsService";
import { createCSSRule } from "vs/base/browser/dom";
import { IDisposable } from "vs/base/common/lifecycle";
import { generateUuid } from "vs/base/common/uuid";
import { IEditorService } from "vs/workbench/services/editor/common/editorService";
import { IExtensionService } from "vs/workbench/services/extensions/common/extensions";
import { IWorkbenchLayoutService } from "vs/workbench/services/layout/browser/layoutService";
import { IViewletService } from "vs/workbench/services/viewlet/browser/viewlet";
import * as vscode from "vscode";
/**
* Client-side implementation of VS Code's API.

View File

@ -1,5 +1,4 @@
import * as path from "path";
import { VSBuffer } from "vs/base/common/buffer";
import { Emitter, Event } from "vs/base/common/event";
import { IDisposable } from "vs/base/common/lifecycle";
@ -9,18 +8,17 @@ import { transformOutgoingURIs } from "vs/base/common/uriIpc";
import { IServerChannel } from "vs/base/parts/ipc/common/ipc";
import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnosticsService";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { IExtensionDescription, ExtensionIdentifier } from "vs/platform/extensions/common/extensions";
import { FileDeleteOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions, FileOpenOptions } from "vs/platform/files/common/files";
import { ExtensionIdentifier, IExtensionDescription } from "vs/platform/extensions/common/extensions";
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions } from "vs/platform/files/common/files";
import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider";
import { ILogService } from "vs/platform/log/common/log";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider";
import { getTranslations } from "vs/server/src/nls";
import { getUriTransformer } from "vs/server/src/util";
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
/**
* Extend the file provider to allow unwatching.
@ -205,7 +203,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
appSettingsHome: this.environment.appSettingsHome,
settingsPath: this.environment.machineSettingsHome,
logsPath: URI.file(this.environment.logsPath),
extensionsPath: URI.file(this.environment.extensionsPath),
extensionsPath: URI.file(this.environment.extensionsPath!),
extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, "extension-host")),
globalStorageHome: URI.file(this.environment.globalStorageHome),
userHome: URI.file(this.environment.userHome),
@ -237,7 +235,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
};
const scanInstalled = async (): Promise<IExtensionDescription[][]> => {
return scanMultiple(false, true, [this.environment.extensionsPath, ...this.environment.extraExtensionPaths]);
return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]);
};
return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => {

View File

@ -1,27 +1,16 @@
import * as cp from "child_process";
import * as os from "os";
import { main as vsCli } from "vs/code/node/cliProcessMain";
import { validatePaths } from "vs/code/node/paths";
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import { buildHelpMessage, buildVersionMessage, options } from "vs/platform/environment/node/argv";
import { ParsedArgs } from "vs/platform/environment/common/environment";
import { buildHelpMessage, buildVersionMessage, Option as VsOption, options as vsOptions } from "vs/platform/environment/node/argv";
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { ipcMain } from "vs/server/src/ipc";
product.extensionsGallery = {
serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com",
itemUrl: process.env.ITEM_URL || "",
controlUrl: "",
recommendationsUrl: "",
...(product.extensionsGallery || {}),
};
import { enableCustomMarketplace } from "vs/server/src/marketplace";
import { MainServer } from "vs/server/src/server";
import { enableExtensionTars } from "vs/server/src/tar";
import { AuthType, buildAllowedMessage, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util";
import { AuthType, buildAllowedMessage, enumToArray, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util";
const { logger } = localRequire<typeof import("@coder/logger/out/index")>("@coder/logger/out/index");
@ -30,15 +19,19 @@ interface Args extends ParsedArgs {
"base-path"?: string;
cert?: string;
"cert-key"?: string;
"extra-builtin-extensions-dir"?: string;
"extra-extensions-dir"?: string;
host?: string;
open?: string;
port?: string;
socket?: string;
}
// @ts-ignore: Force `keyof Args` to work.
interface Option extends VsOption {
id: keyof Args;
}
const getArgs = (): Args => {
const options = vsOptions as Option[];
// The last item is _ which is like -- so our options need to come before it.
const last = options.pop()!;
@ -78,13 +71,7 @@ const getArgs = (): Args => {
options.push(last);
const args = validatePaths(parseMainProcessArgv(process.argv)) as Args;
["extra-extensions-dir", "extra-builtin-extensions-dir"].forEach((key) => {
if (typeof args[key] === "string") {
args[key] = [args[key]];
}
});
return args;
return validatePaths(parseMainProcessArgv(process.argv));
};
const startVscode = async (): Promise<void | void[]> => {
@ -100,7 +87,7 @@ const startVscode = async (): Promise<void | void[]> => {
password: process.env.PASSWORD,
};
if (options.auth && Object.keys(AuthType).filter((k) => AuthType[k] === options.auth).length === 0) {
if (options.auth && enumToArray(AuthType).filter((t) => t === options.auth).length === 0) {
throw new Error(`'${options.auth}' is not a valid authentication type.`);
} else if (options.auth && !options.password) {
options.password = await generatePassword();
@ -116,7 +103,7 @@ const startVscode = async (): Promise<void | void[]> => {
options.certKey = certKey;
}
enableExtensionTars();
enableCustomMarketplace();
const server = new MainServer({
...options,
@ -180,7 +167,7 @@ const startCli = (): boolean | Promise<void> => {
};
if (shouldSpawnCliProcess()) {
enableExtensionTars();
enableCustomMarketplace();
return vsCli(args);
}

View File

@ -1,8 +1,29 @@
import { registerSingleton } from "vs/platform/instantiation/common/extensions";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { ILocalizationsService } from "vs/platform/localizations/common/localizations";
import { LocalizationsService } from "vs/platform/localizations/electron-browser/localizationsService";
import { IUpdateService } from "vs/platform/update/common/update";
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
import { TelemetryChannelClient } from "vs/server/src/telemetry";
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
import { coderApi, vscodeApi } from "vs/server/src/api";
class TelemetryService extends TelemetryChannelClient {
public constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
) {
super(remoteAgentService.getConnection()!.getChannel("telemetry"));
}
}
registerSingleton(ILocalizationsService, LocalizationsService);
registerSingleton(IUpdateService, UpdateService);
registerSingleton(ITelemetryService, TelemetryService);
import "vs/workbench/contrib/update/electron-browser/update.contribution";
import "vs/css!./media/firefox";
import { coderApi, vscodeApi } from "vs/server/src/api";
/**
* This is called by vs/workbench/browser/web.main.ts after the workbench has
@ -13,7 +34,7 @@ export const initialize = async (services: ServiceCollection): Promise<void> =>
target.ide = coderApi(services);
target.vscode = vscodeApi(services);
const event = new CustomEvent('ide-ready');
const event = new CustomEvent("ide-ready");
(event as any).ide = target.ide;
(event as any).vscode = target.vscode;
window.dispatchEvent(event);

View File

@ -1,5 +1,4 @@
import * as cp from "child_process";
import { getPathFromAmdModule } from "vs/base/common/amd";
import { VSBuffer } from "vs/base/common/buffer";
import { Emitter } from "vs/base/common/event";
@ -7,11 +6,10 @@ import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { ILogService } from "vs/platform/log/common/log";
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
import { getNlsConfiguration } from "vs/server/src/nls";
import { Protocol } from "vs/server/src/protocol";
import { uriTransformerPath } from "vs/server/src/util";
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
export abstract class Connection {
protected readonly _onClose = new Emitter<void>();
@ -126,8 +124,8 @@ export class ExtensionHostConnection extends Connection {
proc.stderr.setEncoding("utf8").on("data", (d) => this.log.error("Extension host stderr", d));
proc.on("message", (event) => {
if (event && event.type === "__$console") {
const severity = this.log[event.severity] ? event.severity : "info";
this.log[severity]("Extension host", event.arguments);
const severity = (<any>this.log)[event.severity] ? event.severity : "info";
(<any>this.log)[severity]("Extension host", event.arguments);
}
});

View File

@ -1,8 +1,7 @@
import * as appInsights from "applicationinsights";
import * as https from "https";
import * as os from "os";
import * as appInsights from "applicationinsights";
export class TelemetryClient implements appInsights.TelemetryClient {
public config: any = {};

View File

@ -1,5 +1,4 @@
import * as cp from "child_process";
import { Emitter } from "vs/base/common/event";
enum ControlMessage {

View File

@ -2,11 +2,11 @@ import * as fs from "fs";
import * as path from "path";
import * as tarStream from "tar-stream";
import * as util from "util";
import * as nls from "vs/nls";
import * as vszip from "vs/base/node/zip";
import { CancellationToken } from "vs/base/common/cancellation";
import { mkdirp } from "vs/base/node/pfs";
import * as vszip from "vs/base/node/zip";
import * as nls from "vs/nls";
import product from "vs/platform/product/node/product";
// We will be overriding these, so keep a reference to the original.
const vszipExtract = vszip.extract;
@ -154,10 +154,18 @@ const extractTar = async (tarPath: string, targetPath: string, options: IExtract
};
/**
* Override original functionality so we can use extensions that are in a tar in
* addition to zips.
* Override original functionality so we can use a custom marketplace with
* either tars or zips.
*/
export const enableExtensionTars = (): void => {
export const enableCustomMarketplace = (): void => {
(<any>product).extensionsGallery = { // Use `any` to override readonly.
serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com",
itemUrl: process.env.ITEM_URL || "",
controlUrl: "",
recommendationsUrl: "",
...(product.extensionsGallery || {}),
};
const target = vszip as typeof vszip;
target.zip = tar;
target.extract = extract;

View File

@ -1,7 +1,6 @@
import * as path from "path";
import * as fs from "fs";
import * as path from "path";
import * as util from "util";
import { getPathFromAmdModule } from "vs/base/common/amd";
import * as lp from "vs/base/node/languagePacks";
import product from "vs/platform/product/node/product";

View File

@ -1,8 +1,7 @@
import * as net from "net";
import { VSBuffer } from "vs/base/common/buffer";
import { NodeSocket, WebSocketNodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { PersistentProtocol } from "vs/base/parts/ipc/common/ipc.net";
import { NodeSocket, WebSocketNodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from "vs/platform/remote/common/remoteAgentConnection";
export interface SocketOptions {

View File

@ -4,31 +4,34 @@ import * as http from "http";
import * as https from "https";
import * as net from "net";
import * as path from "path";
import * as tls from "tls";
import * as util from "util";
import * as url from "url";
import * as querystring from "querystring";
import * as tls from "tls";
import * as url from "url";
import * as util from "util";
import { Emitter } from "vs/base/common/event";
import { sanitizeFilePath } from "vs/base/common/extpath";
import { UriComponents, URI } from "vs/base/common/uri";
import { Schemas } from "vs/base/common/network";
import { URI, UriComponents } from "vs/base/common/uri";
import { generateUuid } from "vs/base/common/uuid";
import { getMachineId } from 'vs/base/node/id';
import { NLSConfiguration } from "vs/base/node/languagePacks";
import { mkdirp, rimraf } from "vs/base/node/pfs";
import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc";
import { ClientConnectionEvent, IPCServer, StaticRouter } from "vs/base/parts/ipc/common/ipc";
import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner";
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
import { ConfigurationService } from "vs/platform/configuration/node/configurationService";
import { ExtensionHostDebugBroadcastChannel } from "vs/platform/debug/common/extensionHostDebugIpc";
import { IDialogService } from "vs/platform/dialogs/common/dialogs";
import { DialogChannelClient } from "vs/platform/dialogs/node/dialogIpc";
import { IEnvironmentService, ParsedArgs } from "vs/platform/environment/common/environment";
import { EnvironmentService } from "vs/platform/environment/node/environmentService";
import { IExtensionManagementService, IExtensionGalleryService } from "vs/platform/extensionManagement/common/extensionManagement";
import { ExtensionGalleryChannel } from "vs/platform/extensionManagement/node/extensionGalleryIpc";
import { ExtensionGalleryService } from "vs/platform/extensionManagement/node/extensionGalleryService";
import { ExtensionManagementChannel } from "vs/platform/extensionManagement/node/extensionManagementIpc";
import { ExtensionGalleryService } from "vs/platform/extensionManagement/common/extensionGalleryService";
import { IExtensionGalleryService, IExtensionManagementService } from "vs/platform/extensionManagement/common/extensionManagement";
import { ExtensionManagementChannel } from "vs/platform/extensionManagement/common/extensionManagementIpc";
import { ExtensionManagementService } from "vs/platform/extensionManagement/node/extensionManagementService";
import { IFileService } from "vs/platform/files/common/files";
import { FileService } from "vs/platform/files/common/fileService";
import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider";
import { SyncDescriptor } from "vs/platform/instantiation/common/descriptors";
import { InstantiationService } from "vs/platform/instantiation/common/instantiationService";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
@ -38,31 +41,32 @@ import { LocalizationsChannel } from "vs/platform/localizations/node/localizatio
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 { IProductConfiguration, IProductService } from "vs/platform/product/common/product";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { ProductService } from "vs/platform/product/node/productService";
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 { IRequestService } from "vs/platform/request/common/request";
import { RequestChannel } from "vs/platform/request/common/requestIpc";
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, LogAppender, combinedAppender } from "vs/platform/telemetry/common/telemetryUtils";
import { TelemetryService, ITelemetryServiceConfig } from "vs/platform/telemetry/common/telemetryService";
import { ITelemetryServiceConfig, TelemetryService } from "vs/platform/telemetry/common/telemetryService";
import { combinedAppender, LogAppender, NullTelemetryService } from "vs/platform/telemetry/common/telemetryUtils";
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 { TelemetryChannel } from "vs/platform/telemetry/node/telemetryIpc";
import { UpdateChannel } from "vs/platform/update/node/updateIpc";
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 { ExtensionEnvironmentChannel, FileProviderChannel } from "vs/server/src/channel";
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/connection";
import { TelemetryClient } from "vs/server/src/insights";
import { getNlsConfiguration, getLocaleFromConfig } from "vs/server/src/nls";
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/nls";
import { Protocol } from "vs/server/src/protocol";
import { TelemetryChannel } from "vs/server/src/telemetry";
import { UpdateService } from "vs/server/src/update";
import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/util";
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
export enum HttpCode {
Ok = 200,
@ -192,9 +196,9 @@ export abstract class Server {
try {
const payload = await this.preHandleRequest(request);
response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, {
"Cache-Control": "max-age=86400", // TODO: ETag?
"Content-Type": getMediaMime(payload.filePath),
...(payload.redirect ? { Location: this.withBase(request, payload.redirect) } : {}),
...(request.headers["service-worker"] ? { "Service-Worker-Allowed": this.options.basePath + "/" } : {}),
...payload.headers,
});
response.end(payload.content);
@ -438,7 +442,7 @@ export class MainServer extends Server {
): Promise<Response> {
switch (base) {
case "/": return this.getRoot(request, parsedUrl);
case "/resources": return this.getResource(requestPath);
case "/vscode-resources": return this.getResource(requestPath);
case "/webview":
if (requestPath.indexOf("/vscode-resource") === 0) {
return this.getResource(requestPath.replace(/^\/vscode-resource/, ""));
@ -490,15 +494,19 @@ export class MainServer extends Server {
NLS_CONFIGURATION: await getNlsConfiguration(locale, environment.userDataPath),
};
Object.keys(options).forEach((key) => {
content = content.replace(`"{{${key}}}"`, `'${JSON.stringify(options[key])}'`);
});
for (const key in options) {
content = content.replace(`"{{${key}}}"`, `'${JSON.stringify(options[key as keyof Options])}'`);
}
content = content.replace('{{WEBVIEW_ENDPOINT}}', webviewEndpoint);
return { content, filePath };
}
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
if (product.commit && message.commit !== product.commit) {
throw new Error(`Version mismatch (${message.commit} instead of ${product.commit})`);
}
switch (message.desiredConnectionType) {
case ConnectionType.ExtensionHost:
case ConnectionType.Management:
@ -552,16 +560,25 @@ export class MainServer extends Server {
}
private async initializeServices(args: ParsedArgs): Promise<void> {
const router = new StaticRouter((ctx: any) => ctx.clientId === "renderer");
const environmentService = new EnvironmentService(args, process.execPath);
const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
const fileService = new FileService(logService);
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
const router = new StaticRouter((ctx: any) => ctx.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(IFileService, fileService);
this.services.set(IProductService, new SyncDescriptor(ProductService));
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
if (!environmentService.args["disable-telemetry"]) {
this.services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [{
appender: combinedAppender(
@ -582,8 +599,6 @@ export class MainServer extends Server {
} else {
this.services.set(ITelemetryService, NullTelemetryService);
}
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
await new Promise((resolve) => {
const instantiationService = new InstantiationService(this.services);
@ -592,19 +607,23 @@ export class MainServer extends Server {
this.ipc.registerChannel("localizations", new LocalizationsChannel(localizationService));
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 telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
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 extensionsEnvironmentChannel = new ExtensionEnvironmentChannel(environmentService, logService, telemetryService);
const fileChannel = new FileProviderChannel(environmentService, logService);
const requestChannel = new RequestChannel(this.services.get(IRequestService) as IRequestService);
const telemetryChannel = new TelemetryChannel(telemetryService);
this.ipc.registerChannel("telemetry", telemetryChannel);
const updateChannel = new UpdateChannel(instantiationService.createInstance(UpdateService));
this.ipc.registerChannel("extensions", extensionsChannel);
this.ipc.registerChannel("remoteextensionsenvironment", extensionsEnvironmentChannel);
this.ipc.registerChannel("request", requestChannel);
this.ipc.registerChannel("telemetry", telemetryChannel);
this.ipc.registerChannel("update", updateChannel);
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, fileChannel);
resolve(new ErrorTelemetry(telemetryService));
});
});

49
src/telemetry.ts Normal file
View File

@ -0,0 +1,49 @@
import { ITelemetryData } from "vs/base/common/actions";
import { Event } from "vs/base/common/event";
import { IChannel, IServerChannel } from "vs/base/parts/ipc/common/ipc";
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from "vs/platform/telemetry/common/gdprTypings";
import { ITelemetryInfo, ITelemetryService } from "vs/platform/telemetry/common/telemetry";
export class TelemetryChannel implements IServerChannel {
constructor(private service: ITelemetryService) {}
listen(_: unknown, event: string): Event<any> {
throw new Error(`Invalid listen ${event}`);
}
call(_: unknown, command: string, args?: any): Promise<any> {
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<void> {
return this.channel.call("publicLog", [eventName, data, anonymizeFilePaths]);
}
public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean): Promise<void> {
return this.channel.call("publicLog2", [eventName, data, anonymizeFilePaths]);
}
public setEnabled(value: boolean): void {
this.channel.call("setEnable", [value]);
}
public getTelemetryInfo(): Promise<ITelemetryInfo> {
return this.channel.call("getTelemetryInfo");
}
public get isOptedIn(): boolean {
return true;
}
}

View File

@ -1,23 +1,24 @@
import * as cp from "child_process";
import * as os from "os";
import * as path from "path";
import { Stream } from "stream";
import * as util from "util";
import * as zlib from 'zlib';
import { toVSBufferReadableStream } from "vs/base/common/buffer";
import { CancellationToken } from "vs/base/common/cancellation";
import { URI } from "vs/base/common/uri";
import * as pfs from "vs/base/node/pfs";
import { asJson, download } from "vs/base/node/request";
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { IFileService } from "vs/platform/files/common/files";
import { ILogService } from "vs/platform/log/common/log";
import pkg from "vs/platform/product/node/package";
import { IRequestService } from "vs/platform/request/node/request";
import { State, UpdateType, StateType, AvailableForDownload } from "vs/platform/update/common/update";
import { asJson, IRequestService } from "vs/platform/request/common/request";
import { AvailableForDownload, State, StateType, UpdateType } from "vs/platform/update/common/update";
import { AbstractUpdateService } from "vs/platform/update/electron-main/abstractUpdateService";
import { ipcMain } from "vs/server/src/ipc";
import { extract } from "vs/server/src/marketplace";
import { tmpdir } from "vs/server/src/util";
import { extract } from "vs/server/src/tar";
import * as zlib from "zlib";
interface IUpdate {
name: string;
@ -30,7 +31,8 @@ export class UpdateService extends AbstractUpdateService {
@IConfigurationService configurationService: IConfigurationService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService,
) {
super(null, configurationService, environmentService, requestService, logService);
}
@ -92,10 +94,13 @@ export class UpdateService extends AbstractUpdateService {
const context = await this.requestService.request({ url }, CancellationToken.None);
// Decompress the gzip as we download. If the gzip encoding is set then
// the request service already does this.
// HACK: This uses knowledge of the internals of the request service.
if (target !== "darwin" && context.res.headers["content-encoding"] !== "gzip") {
context.stream = context.stream.pipe(zlib.createGunzip());
const stream = (context.res as any as Stream);
stream.removeAllListeners();
context.stream = toVSBufferReadableStream(stream.pipe(zlib.createGunzip()));
}
await download(downloadPath, context);
await this.fileService.writeFile(URI.file(downloadPath), context.stream);
await extract(downloadPath, extractPath, undefined, CancellationToken.None);
const newBinary = path.join(extractPath, releaseName, "code-server");
if (!pfs.exists(newBinary)) {

View File

@ -1,18 +1,17 @@
import { generateUuid } from "vs/base/common/uuid";
import { DesktopDragAndDropData } from "vs/base/browser/ui/list/listView";
import { VSBuffer, VSBufferReadable } from "vs/base/common/buffer";
import { Emitter, Event } from "vs/base/common/event";
import { VSBuffer, VSBufferReadableStream } from "vs/base/common/buffer";
import { Disposable } from "vs/base/common/lifecycle";
import * as path from "vs/base/common/path";
import { URI } from "vs/base/common/uri";
import { generateUuid } from "vs/base/common/uuid";
import { IFileService } from "vs/platform/files/common/files";
import { createDecorator, ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from "vs/platform/notification/common/notification";
import { IProgress, IProgressStep, IProgressService, ProgressLocation } from "vs/platform/progress/common/progress";
import { IProgress, IProgressService, IProgressStep, ProgressLocation } from "vs/platform/progress/common/progress";
import { IWindowsService } from "vs/platform/windows/common/windows";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import { ExplorerItem } from "vs/workbench/contrib/files/common/explorerModel";
import { IEditorGroup } from "vs/workbench/services/editor/common/editorGroupsService";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import { IWindowsService } from "vs/platform/windows/common/windows";
import { IEditorService } from "vs/workbench/services/editor/common/editorService";
export const IUploadService = createDecorator<IUploadService>("uploadService");
@ -208,20 +207,15 @@ class Upload {
),
});
const reader = new Reader(file);
reader.onData((data) => {
if (data && data.length > 0) {
reader.on("data", (data) => {
if (data && data.byteLength > 0) {
this.uploaded += data.byteLength;
}
});
reader.onAbort(() => {
const remaining = file.size - reader.offset;
if (remaining > 0) {
this.uploaded += remaining;
}
});
this.uploadingFiles.set(filePath, reader);
await this.fileService.writeFile(tempUri, reader);
if (reader.aborted) {
this.uploaded += (file.size - reader.offset);
await this.fileService.del(tempUri);
} else {
await this.fileService.move(tempUri, uri, true);
@ -292,17 +286,14 @@ class Upload {
}
}
class Reader implements VSBufferReadable {
class Reader implements VSBufferReadableStream {
private _offset = 0;
private readonly size = 32000; // ~32kb max while reading in the file.
private readonly _onData = new Emitter<Uint8Array | null>();
public readonly onData: Event<Uint8Array | null> = this._onData.event;
private _aborted = false;
private readonly _onAbort = new Emitter<void>();
public readonly onAbort: Event<void> = this._onAbort.event;
private readonly reader = new FileReader();
private paused = true;
private buffer?: VSBuffer;
private callbacks = new Map<string, Array<(...args: any[]) => void>>();
public constructor(private readonly file: File) {
this.reader.addEventListener("load", this.onLoad);
@ -311,35 +302,71 @@ class Reader implements VSBufferReadable {
public get offset(): number { return this._offset; }
public get aborted(): boolean { return this._aborted; }
public on(event: "data" | "error" | "end", callback: (...args:any[]) => void): void {
if (!this.callbacks.has(event)) {
this.callbacks.set(event, []);
}
this.callbacks.get(event)!.push(callback);
if (this.aborted) {
return this.emit("error", new Error("stream has been aborted"));
} else if (this.done) {
return this.emit("error", new Error("stream has ended"));
} else if (event === "end") { // Once this is being listened to we can safely start outputting data.
this.resume();
}
}
public abort = (): void => {
this._aborted = true;
this.reader.abort();
this.reader.removeEventListener("load", this.onLoad);
this._onAbort.fire();
this.emit("end");
}
public read = async (): Promise<VSBuffer | null> => {
return new Promise<VSBuffer | null>((resolve) => {
const disposables = [
this.onAbort(() => {
disposables.forEach((d) => d.dispose());
resolve(null);
}),
this.onData((data) => {
disposables.forEach((d) => d.dispose());
resolve(data && VSBuffer.wrap(data));
}),
];
if (this.aborted || this.offset >= this.file.size) {
return this._onData.fire(null);
}
const slice = this.file.slice(this.offset, this.offset + this.size);
this._offset += this.size;
this.reader.readAsArrayBuffer(slice);
});
public pause(): void {
this.paused = true;
}
private onLoad = () => {
this._onData.fire(new Uint8Array(this.reader.result as ArrayBuffer));
public resume(): void {
if (this.paused) {
this.paused = false;
this.readNextChunk();
}
}
public destroy(): void {
this.abort();
}
private onLoad = (): void => {
this.buffer = VSBuffer.wrap(new Uint8Array(this.reader.result as ArrayBuffer));
if (!this.paused) {
this.readNextChunk();
}
}
private readNextChunk(): void {
if (this.buffer) {
this._offset += this.buffer.byteLength;
this.emit("data", this.buffer);
this.buffer = undefined;
}
if (!this.paused) { // Could be paused during the data event.
if (this.done) {
this.emit("end");
} else {
this.reader.readAsArrayBuffer(this.file.slice(this.offset, this.offset + this.size));
}
}
}
private emit(event: "data" | "error" | "end", ...args: any[]): void {
if (this.callbacks.has(event)) {
this.callbacks.get(event)!.forEach((cb) => cb(...args));
}
}
private get done(): boolean {
return this.offset >= this.file.size;
}
}

View File

@ -63,12 +63,12 @@ export const generatePassword = async (length: number = 24): Promise<string> =>
};
export const getMediaMime = (filePath?: string): string => {
return filePath && (vsGetMediaMime(filePath) || {
return filePath && (vsGetMediaMime(filePath) || (<{[index: string]: string}>{
".css": "text/css",
".html": "text/html",
".js": "text/javascript",
".json": "application/json",
}[extname(filePath)]) || "text/plain";
})[extname(filePath)]) || "text/plain";
};
export const isWsl = async (): Promise<boolean> => {
@ -113,8 +113,16 @@ export const unpackExecutables = async (): Promise<void> => {
}
};
export const buildAllowedMessage = (t: typeof AuthType): string => {
const values = <string[]>Object.keys(t).map((k) => t[k]);
export const enumToArray = (t: any): string[] => {
const values = <string[]>[];
for (const k in t) {
values.push(t[k]);
}
return values;
};
export const buildAllowedMessage = (t: any): string => {
const values = enumToArray(t);
return `Allowed value${values.length === 1 ? " is" : "s are"} ${values.map((t) => `'${t}'`).join(",")}`;
};