Update VS Code to 1.37.0
This commit is contained in:
parent
b257c60636
commit
ba7285192c
@ -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:
|
||||
|
18
README.md
18
README.md
@ -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
58
scripts/build-json.js
Normal 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();
|
@ -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));
|
@ -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
|
||||
}
|
||||
|
1653
scripts/vscode.patch
1653
scripts/vscode.patch
File diff suppressed because it is too large
Load Diff
47
src/api.ts
47
src/api.ts
@ -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.
|
||||
|
@ -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) => {
|
||||
|
41
src/cli.ts
41
src/cli.ts
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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 = {};
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as cp from "child_process";
|
||||
|
||||
import { Emitter } from "vs/base/common/event";
|
||||
|
||||
enum ControlMessage {
|
||||
|
@ -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;
|
@ -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";
|
||||
|
@ -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 {
|
||||
|
@ -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
49
src/telemetry.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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)) {
|
||||
|
115
src/upload.ts
115
src/upload.ts
@ -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;
|
||||
}
|
||||
}
|
||||
|
16
src/util.ts
16
src/util.ts
@ -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(",")}`;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user