mirror of https://git.tuxpa.in/a/code-server.git
fixup: move openHelpAbout
This commit is contained in:
parent
d3df963d39
commit
36714da613
59
docs/FAQ.md
59
docs/FAQ.md
|
@ -2,36 +2,35 @@
|
||||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
- [FAQ](#faq)
|
- [Questions?](#questions)
|
||||||
- [Questions?](#questions)
|
- [iPad Status?](#ipad-status)
|
||||||
- [iPad Status?](#ipad-status)
|
- [Community Projects (awesome-code-server)](#community-projects-awesome-code-server)
|
||||||
- [Community Projects (awesome-code-server)](#community-projects-awesome-code-server)
|
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
||||||
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
||||||
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
||||||
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
- [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url)
|
||||||
- [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url)
|
- [Where are extensions stored?](#where-are-extensions-stored)
|
||||||
- [Where are extensions stored?](#where-are-extensions-stored)
|
- [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces)
|
||||||
- [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces)
|
- [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet)
|
||||||
- [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet)
|
- [Can I store my password hashed?](#can-i-store-my-password-hashed)
|
||||||
- [Can I store my password hashed?](#can-i-store-my-password-hashed)
|
- [How do I securely access web services?](#how-do-i-securely-access-web-services)
|
||||||
- [How do I securely access web services?](#how-do-i-securely-access-web-services)
|
- [Sub-paths](#sub-paths)
|
||||||
- [Sub-paths](#sub-paths)
|
- [Sub-domains](#sub-domains)
|
||||||
- [Sub-domains](#sub-domains)
|
- [Why does the code-server proxy strip `/proxy/<port>` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path)
|
||||||
- [Why does the code-server proxy strip `/proxy/<port>` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path)
|
- [Proxying to Create React App](#proxying-to-create-react-app)
|
||||||
- [Proxying to Create React App](#proxying-to-create-react-app)
|
- [Multi-tenancy](#multi-tenancy)
|
||||||
- [Multi-tenancy](#multi-tenancy)
|
- [Docker in code-server container?](#docker-in-code-server-container)
|
||||||
- [Docker in code-server container?](#docker-in-code-server-container)
|
- [How can I disable telemetry?](#how-can-i-disable-telemetry)
|
||||||
- [How can I disable telemetry?](#how-can-i-disable-telemetry)
|
- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open)
|
||||||
- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open)
|
- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server)
|
||||||
- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server)
|
- [Heartbeat File](#heartbeat-file)
|
||||||
- [Heartbeat File](#heartbeat-file)
|
- [Healthz endpoint](#healthz-endpoint)
|
||||||
- [Healthz endpoint](#healthz-endpoint)
|
- [How does the config file work?](#how-does-the-config-file-work)
|
||||||
- [How does the config file work?](#how-does-the-config-file-work)
|
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
|
||||||
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
|
- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work)
|
||||||
- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work)
|
- [Differences compared to Theia?](#differences-compared-to-theia)
|
||||||
- [Differences compared to Theia?](#differences-compared-to-theia)
|
- [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy)
|
||||||
- [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy)
|
- [Enterprise](#enterprise)
|
||||||
- [Enterprise](#enterprise)
|
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
|
|
@ -330,11 +330,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Reference: - ../../workbench/api/common/extHostDebugService.ts
|
||||||
NOTE@coder:
|
|
||||||
Reference: - ../../workbench/api/common/extHostDebugService.ts
|
|
||||||
3/16/21 jsjoeio
|
|
||||||
*/
|
|
||||||
class VariableResolverService extends AbstractVariableResolverService {
|
class VariableResolverService extends AbstractVariableResolverService {
|
||||||
constructor(
|
constructor(
|
||||||
remoteAuthority: string,
|
remoteAuthority: string,
|
||||||
|
@ -356,10 +352,6 @@ class VariableResolverService extends AbstractVariableResolverService {
|
||||||
return args.resolvedVariables[`config:${section}`];
|
return args.resolvedVariables[`config:${section}`];
|
||||||
},
|
},
|
||||||
getAppRoot: (): string | undefined => {
|
getAppRoot: (): string | undefined => {
|
||||||
/*
|
|
||||||
NOTE@coder: not sure where we could get this from. This is new.
|
|
||||||
@jsjoeio 3/11/21
|
|
||||||
*/
|
|
||||||
return (args.resolverEnv && args.resolverEnv['VSCODE_CWD']) || env['VSCODE_CWD'] || process.cwd();
|
return (args.resolverEnv && args.resolverEnv['VSCODE_CWD']) || env['VSCODE_CWD'] || process.cwd();
|
||||||
},
|
},
|
||||||
getExecPath: (): string | undefined => {
|
getExecPath: (): string | undefined => {
|
||||||
|
|
|
@ -1,108 +1,99 @@
|
||||||
import { field } from "@coder/logger"
|
import { field } from '@coder/logger';
|
||||||
import { release } from "os"
|
import { release } from 'os';
|
||||||
import * as fs from "fs"
|
import * as fs from 'fs';
|
||||||
import * as net from "net"
|
import * as net from 'net';
|
||||||
import * as path from "path"
|
import * as path from 'path';
|
||||||
import { Emitter } from "vs/base/common/event"
|
import { Emitter } from 'vs/base/common/event';
|
||||||
import { Schemas } from "vs/base/common/network"
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import { URI } from "vs/base/common/uri"
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { getMachineId } from "vs/base/node/id"
|
import { getMachineId } from 'vs/base/node/id';
|
||||||
import { ClientConnectionEvent, IPCServer, IServerChannel, ProxyChannel } from "vs/base/parts/ipc/common/ipc"
|
import { ClientConnectionEvent, IPCServer, IServerChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||||
import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"
|
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
|
||||||
import { main } from "vs/code/node/cliProcessMain"
|
import { main } from 'vs/code/node/cliProcessMain';
|
||||||
import { IConfigurationService } from "vs/platform/configuration/common/configuration"
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { ConfigurationService } from "vs/platform/configuration/common/configurationService"
|
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||||
import { ExtensionHostDebugBroadcastChannel } from "vs/platform/debug/common/extensionHostDebugIpc"
|
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||||
import { NativeParsedArgs } from "vs/platform/environment/common/argv"
|
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||||
import { IEnvironmentService, INativeEnvironmentService } from "vs/platform/environment/common/environment"
|
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { NativeEnvironmentService } from "vs/platform/environment/node/environmentService"
|
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||||
import { ExtensionGalleryService } from "vs/platform/extensionManagement/common/extensionGalleryService"
|
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||||
import {
|
import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
IExtensionGalleryService,
|
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
|
||||||
IExtensionManagementService,
|
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||||
} from "vs/platform/extensionManagement/common/extensionManagement"
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
import { ExtensionManagementChannel } from "vs/platform/extensionManagement/common/extensionManagementIpc"
|
import { FileService } from 'vs/platform/files/common/fileService';
|
||||||
import { ExtensionManagementService } from "vs/platform/extensionManagement/node/extensionManagementService"
|
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||||
import { IFileService } from "vs/platform/files/common/files"
|
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||||
import { FileService } from "vs/platform/files/common/fileService"
|
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||||
import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider"
|
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||||
import { SyncDescriptor } from "vs/platform/instantiation/common/descriptors"
|
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||||
import { InstantiationService } from "vs/platform/instantiation/common/instantiationService"
|
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
|
||||||
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"
|
import { ConsoleLogger, getLogLevel, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||||
import { ILocalizationsService } from "vs/platform/localizations/common/localizations"
|
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';
|
||||||
import { LocalizationsService } from "vs/platform/localizations/node/localizations"
|
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||||
import {
|
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
|
||||||
ConsoleLogger,
|
import product from 'vs/platform/product/common/product';
|
||||||
getLogLevel,
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
ILoggerService,
|
import { ConnectionType, ConnectionTypeRequest } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||||
ILogService,
|
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||||
MultiplexLogService,
|
import { IRequestService } from 'vs/platform/request/common/request';
|
||||||
} from "vs/platform/log/common/log"
|
import { RequestChannel } from 'vs/platform/request/common/requestIpc';
|
||||||
import { LogLevelChannel } from "vs/platform/log/common/logIpc"
|
import { RequestService } from 'vs/platform/request/node/requestService';
|
||||||
import { LoggerService } from "vs/platform/log/node/loggerService"
|
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
|
||||||
import { SpdLogLogger } from "vs/platform/log/node/spdlogLog"
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import product from "vs/platform/product/common/product"
|
import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender';
|
||||||
import { IProductService } from "vs/platform/product/common/productService"
|
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||||
import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection"
|
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||||
import { RemoteAgentConnectionContext } from "vs/platform/remote/common/remoteAgentEnvironment"
|
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||||
import { IRequestService } from "vs/platform/request/common/request"
|
import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties';
|
||||||
import { RequestChannel } from "vs/platform/request/common/requestIpc"
|
import { TelemetryChannel } from 'vs/server/common/telemetry';
|
||||||
import { RequestService } from "vs/platform/request/node/requestService"
|
import { Query, VscodeOptions, WorkbenchOptions } from 'vs/server/ipc';
|
||||||
import ErrorTelemetry from "vs/platform/telemetry/browser/errorTelemetry"
|
import { ExtensionEnvironmentChannel, FileProviderChannel, TerminalProviderChannel } from 'vs/server/node/channel';
|
||||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"
|
import { Connection, ExtensionHostConnection, ManagementConnection } from 'vs/server/node/connection';
|
||||||
import { TelemetryLogAppender } from "vs/platform/telemetry/common/telemetryLogAppender"
|
import { TelemetryClient } from 'vs/server/node/insights';
|
||||||
import { TelemetryService } from "vs/platform/telemetry/common/telemetryService"
|
import { logger } from 'vs/server/node/logger';
|
||||||
import { combinedAppender, NullTelemetryService } from "vs/platform/telemetry/common/telemetryUtils"
|
import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/node/nls';
|
||||||
import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender"
|
import { Protocol } from 'vs/server/node/protocol';
|
||||||
import { resolveCommonProperties } from "vs/platform/telemetry/common/commonProperties"
|
import { getUriTransformer } from 'vs/server/node/util';
|
||||||
import { TelemetryChannel } from "vs/server/common/telemetry"
|
import { REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
|
||||||
import { Query, VscodeOptions, WorkbenchOptions } from "vs/server/ipc"
|
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
|
||||||
import { ExtensionEnvironmentChannel, FileProviderChannel, TerminalProviderChannel } from "vs/server/node/channel"
|
import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||||
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/node/connection"
|
|
||||||
import { TelemetryClient } from "vs/server/node/insights"
|
|
||||||
import { logger } from "vs/server/node/logger"
|
|
||||||
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/node/nls"
|
|
||||||
import { Protocol } from "vs/server/node/protocol"
|
|
||||||
import { getUriTransformer } from "vs/server/node/util"
|
|
||||||
import { REMOTE_TERMINAL_CHANNEL_NAME } from "vs/workbench/contrib/terminal/common/remoteTerminalChannel"
|
|
||||||
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/workbench/services/remote/common/remoteAgentFileSystemChannel"
|
|
||||||
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService"
|
|
||||||
|
|
||||||
export class Vscode {
|
export class Vscode {
|
||||||
public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>()
|
public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>();
|
||||||
public readonly onDidClientConnect = this._onDidClientConnect.event
|
public readonly onDidClientConnect = this._onDidClientConnect.event;
|
||||||
private readonly ipc = new IPCServer<RemoteAgentConnectionContext>(this.onDidClientConnect)
|
private readonly ipc = new IPCServer<RemoteAgentConnectionContext>(this.onDidClientConnect);
|
||||||
|
|
||||||
private readonly maxExtraOfflineConnections = 0
|
private readonly maxExtraOfflineConnections = 0;
|
||||||
private readonly connections = new Map<ConnectionType, Map<string, Connection>>()
|
private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
|
||||||
|
|
||||||
private readonly services = new ServiceCollection()
|
private readonly services = new ServiceCollection();
|
||||||
private servicesPromise?: Promise<void>
|
private servicesPromise?: Promise<void>;
|
||||||
|
|
||||||
public async cli(args: NativeParsedArgs): Promise<void> {
|
public async cli(args: NativeParsedArgs): Promise<void> {
|
||||||
return main(args)
|
return main(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initialize(options: VscodeOptions): Promise<WorkbenchOptions> {
|
public async initialize(options: VscodeOptions): Promise<WorkbenchOptions> {
|
||||||
const transformer = getUriTransformer(options.remoteAuthority)
|
const transformer = getUriTransformer(options.remoteAuthority);
|
||||||
if (!this.servicesPromise) {
|
if (!this.servicesPromise) {
|
||||||
this.servicesPromise = this.initializeServices(options.args)
|
this.servicesPromise = this.initializeServices(options.args);
|
||||||
}
|
}
|
||||||
await this.servicesPromise
|
await this.servicesPromise;
|
||||||
const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService
|
const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService;
|
||||||
const startPath = options.startPath
|
const startPath = options.startPath;
|
||||||
const parseUrl = (url: string): URI => {
|
const parseUrl = (url: string): URI => {
|
||||||
// This might be a fully-specified URL or just a path.
|
// This might be a fully-specified URL or just a path.
|
||||||
try {
|
try {
|
||||||
return URI.parse(url, true)
|
return URI.parse(url, true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return URI.from({
|
return URI.from({
|
||||||
scheme: Schemas.vscodeRemote,
|
scheme: Schemas.vscodeRemote,
|
||||||
authority: options.remoteAuthority,
|
authority: options.remoteAuthority,
|
||||||
path: url,
|
path: url,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
return {
|
return {
|
||||||
workbenchWebConfiguration: {
|
workbenchWebConfiguration: {
|
||||||
workspaceUri: startPath && startPath.workspace ? parseUrl(startPath.url) : undefined,
|
workspaceUri: startPath && startPath.workspace ? parseUrl(startPath.url) : undefined,
|
||||||
|
@ -111,112 +102,106 @@ export class Vscode {
|
||||||
logLevel: getLogLevel(environment),
|
logLevel: getLogLevel(environment),
|
||||||
workspaceProvider: {
|
workspaceProvider: {
|
||||||
payload: [
|
payload: [
|
||||||
["userDataPath", environment.userDataPath],
|
['userDataPath', environment.userDataPath],
|
||||||
["enableProposedApi", JSON.stringify(options.args["enable-proposed-api"] || [])],
|
['enableProposedApi', JSON.stringify(options.args['enable-proposed-api'] || [])]
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)),
|
remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)),
|
||||||
productConfiguration: product,
|
productConfiguration: product,
|
||||||
nlsConfiguration: await getNlsConfiguration(
|
nlsConfiguration: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath),
|
||||||
environment.args.locale || (await getLocaleFromConfig(environment.userDataPath)),
|
commit: product.commit || 'development',
|
||||||
environment.userDataPath,
|
};
|
||||||
),
|
|
||||||
commit: product.commit || "development",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleWebSocket(socket: net.Socket, query: Query, permessageDeflate: boolean): Promise<true> {
|
public async handleWebSocket(socket: net.Socket, query: Query, permessageDeflate: boolean): Promise<true> {
|
||||||
if (!query.reconnectionToken) {
|
if (!query.reconnectionToken) {
|
||||||
throw new Error("Reconnection token is missing from query parameters")
|
throw new Error('Reconnection token is missing from query parameters');
|
||||||
}
|
}
|
||||||
const protocol = new Protocol(socket, {
|
const protocol = new Protocol(socket, {
|
||||||
reconnectionToken: <string>query.reconnectionToken,
|
reconnectionToken: <string>query.reconnectionToken,
|
||||||
reconnection: query.reconnection === "true",
|
reconnection: query.reconnection === 'true',
|
||||||
skipWebSocketFrames: query.skipWebSocketFrames === "true",
|
skipWebSocketFrames: query.skipWebSocketFrames === 'true',
|
||||||
permessageDeflate,
|
permessageDeflate,
|
||||||
recordInflateBytes: permessageDeflate,
|
recordInflateBytes: permessageDeflate,
|
||||||
})
|
});
|
||||||
try {
|
try {
|
||||||
await this.connect(await protocol.handshake(), protocol)
|
await this.connect(await protocol.handshake(), protocol);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
protocol.sendMessage({ type: "error", reason: error.message })
|
protocol.sendMessage({ type: 'error', reason: error.message });
|
||||||
protocol.dispose()
|
protocol.dispose();
|
||||||
protocol.getSocket().dispose()
|
protocol.getSocket().dispose();
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
|
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
|
||||||
if (product.commit && message.commit !== product.commit) {
|
if (product.commit && message.commit !== product.commit) {
|
||||||
logger.warn(`Version mismatch (${message.commit} instead of ${product.commit})`)
|
logger.warn(`Version mismatch (${message.commit} instead of ${product.commit})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (message.desiredConnectionType) {
|
switch (message.desiredConnectionType) {
|
||||||
case ConnectionType.ExtensionHost:
|
case ConnectionType.ExtensionHost:
|
||||||
case ConnectionType.Management:
|
case ConnectionType.Management:
|
||||||
if (!this.connections.has(message.desiredConnectionType)) {
|
if (!this.connections.has(message.desiredConnectionType)) {
|
||||||
this.connections.set(message.desiredConnectionType, new Map())
|
this.connections.set(message.desiredConnectionType, new Map());
|
||||||
}
|
}
|
||||||
const connections = this.connections.get(message.desiredConnectionType)!
|
const connections = this.connections.get(message.desiredConnectionType)!;
|
||||||
|
|
||||||
const ok = async () => {
|
const ok = async () => {
|
||||||
return message.desiredConnectionType === ConnectionType.ExtensionHost
|
return message.desiredConnectionType === ConnectionType.ExtensionHost
|
||||||
? { debugPort: await this.getDebugPort() }
|
? { debugPort: await this.getDebugPort() }
|
||||||
: { type: "ok" }
|
: { type: 'ok' };
|
||||||
}
|
};
|
||||||
|
|
||||||
const token = protocol.options.reconnectionToken
|
const token = protocol.options.reconnectionToken;
|
||||||
if (protocol.options.reconnection && connections.has(token)) {
|
if (protocol.options.reconnection && connections.has(token)) {
|
||||||
protocol.sendMessage(await ok())
|
protocol.sendMessage(await ok());
|
||||||
const buffer = protocol.readEntireBuffer()
|
const buffer = protocol.readEntireBuffer();
|
||||||
protocol.dispose()
|
protocol.dispose();
|
||||||
return connections.get(token)!.reconnect(protocol.getSocket(), buffer)
|
return connections.get(token)!.reconnect(protocol.getSocket(), buffer);
|
||||||
} else if (protocol.options.reconnection || connections.has(token)) {
|
} else if (protocol.options.reconnection || connections.has(token)) {
|
||||||
throw new Error(
|
throw new Error(protocol.options.reconnection
|
||||||
protocol.options.reconnection ? "Unrecognized reconnection token" : "Duplicate reconnection token",
|
? 'Unrecognized reconnection token'
|
||||||
)
|
: 'Duplicate reconnection token'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("New connection", field("token", token))
|
logger.debug('New connection', field('token', token));
|
||||||
protocol.sendMessage(await ok())
|
protocol.sendMessage(await ok());
|
||||||
|
|
||||||
let connection: Connection
|
let connection: Connection;
|
||||||
if (message.desiredConnectionType === ConnectionType.Management) {
|
if (message.desiredConnectionType === ConnectionType.Management) {
|
||||||
connection = new ManagementConnection(protocol, token)
|
connection = new ManagementConnection(protocol, token);
|
||||||
this._onDidClientConnect.fire({
|
this._onDidClientConnect.fire({
|
||||||
protocol,
|
protocol, onDidClientDisconnect: connection.onClose,
|
||||||
onDidClientDisconnect: connection.onClose,
|
});
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
const buffer = protocol.readEntireBuffer()
|
const buffer = protocol.readEntireBuffer();
|
||||||
connection = new ExtensionHostConnection(
|
connection = new ExtensionHostConnection(
|
||||||
message.args ? message.args.language : "en",
|
message.args ? message.args.language : 'en',
|
||||||
protocol,
|
protocol, buffer, token,
|
||||||
buffer,
|
|
||||||
token,
|
|
||||||
this.services.get(IEnvironmentService) as INativeEnvironmentService,
|
this.services.get(IEnvironmentService) as INativeEnvironmentService,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
connections.set(token, connection)
|
connections.set(token, connection);
|
||||||
connection.onClose(() => {
|
connection.onClose(() => {
|
||||||
logger.debug("Connection closed", field("token", token))
|
logger.debug('Connection closed', field('token', token));
|
||||||
connections.delete(token)
|
connections.delete(token);
|
||||||
})
|
});
|
||||||
this.disposeOldOfflineConnections(connections)
|
this.disposeOldOfflineConnections(connections);
|
||||||
break
|
break;
|
||||||
case ConnectionType.Tunnel:
|
case ConnectionType.Tunnel: return protocol.tunnel();
|
||||||
return protocol.tunnel()
|
default: throw new Error('Unrecognized connection type');
|
||||||
default:
|
|
||||||
throw new Error("Unrecognized connection type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private disposeOldOfflineConnections(connections: Map<string, Connection>): void {
|
private disposeOldOfflineConnections(connections: Map<string, Connection>): void {
|
||||||
const offline = Array.from(connections.values()).filter((connection) => typeof connection.offline !== "undefined")
|
const offline = Array.from(connections.values())
|
||||||
|
.filter((connection) => typeof connection.offline !== 'undefined');
|
||||||
for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) {
|
for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) {
|
||||||
logger.debug("Disposing offline connection", field("token", offline[i].token))
|
logger.debug('Disposing offline connection', field('token', offline[i].token));
|
||||||
offline[i].dispose()
|
offline[i].dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,9 +213,9 @@ export class Vscode {
|
||||||
If upstream changes cause conflicts, look there ^.
|
If upstream changes cause conflicts, look there ^.
|
||||||
3/11/21 @jsjoeio
|
3/11/21 @jsjoeio
|
||||||
*/
|
*/
|
||||||
const environmentService = new NativeEnvironmentService(args)
|
const environmentService = new NativeEnvironmentService(args);
|
||||||
// https://github.com/cdr/code-server/issues/1693
|
// https://github.com/cdr/code-server/issues/1693
|
||||||
fs.mkdirSync(environmentService.globalStorageHome.fsPath, { recursive: true })
|
fs.mkdirSync(environmentService.globalStorageHome.fsPath, { recursive: true });
|
||||||
/*
|
/*
|
||||||
NOTE@coder: Made these updates on based on this file (and lines):
|
NOTE@coder: Made these updates on based on this file (and lines):
|
||||||
Reference: - ../../electron-browser/sharedProcess/sharedProcessMain.ts#L144-L149
|
Reference: - ../../electron-browser/sharedProcess/sharedProcessMain.ts#L144-L149
|
||||||
|
@ -243,133 +228,91 @@ export class Vscode {
|
||||||
*/
|
*/
|
||||||
const logService = new MultiplexLogService([
|
const logService = new MultiplexLogService([
|
||||||
new ConsoleLogger(getLogLevel(environmentService)),
|
new ConsoleLogger(getLogLevel(environmentService)),
|
||||||
new SpdLogLogger(
|
new SpdLogLogger(RemoteExtensionLogFileName, path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), false, getLogLevel(environmentService))
|
||||||
RemoteExtensionLogFileName,
|
]);
|
||||||
path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`),
|
const fileService = new FileService(logService);
|
||||||
false,
|
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
|
||||||
getLogLevel(environmentService),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
const fileService = new FileService(logService)
|
|
||||||
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService))
|
|
||||||
|
|
||||||
const loggerService = new LoggerService(logService, fileService)
|
const loggerService = new LoggerService(logService, fileService);
|
||||||
|
|
||||||
const piiPaths = [
|
const piiPaths = [
|
||||||
path.join(environmentService.userDataPath, "clp"), // Language packs.
|
path.join(environmentService.userDataPath, 'clp'), // Language packs.
|
||||||
environmentService.appRoot,
|
environmentService.appRoot,
|
||||||
environmentService.extensionsPath,
|
environmentService.extensionsPath,
|
||||||
environmentService.builtinExtensionsPath,
|
environmentService.builtinExtensionsPath,
|
||||||
...environmentService.extraExtensionPaths,
|
...environmentService.extraExtensionPaths,
|
||||||
...environmentService.extraBuiltinExtensionPaths,
|
...environmentService.extraBuiltinExtensionPaths,
|
||||||
]
|
];
|
||||||
|
|
||||||
/*
|
this.ipc.registerChannel('logger', new LogLevelChannel(logService));
|
||||||
NOTE@coder: we changed this channel registration from LogLevel to LogLevelChannel
|
this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
|
||||||
because it changed upstream.
|
|
||||||
|
|
||||||
3/15/21 jsjoeio
|
this.services.set(ILogService, logService);
|
||||||
*/
|
this.services.set(IEnvironmentService, environmentService);
|
||||||
this.ipc.registerChannel("logger", new LogLevelChannel(logService))
|
this.services.set(INativeEnvironmentService, environmentService);
|
||||||
this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel())
|
this.services.set(ILoggerService, loggerService);
|
||||||
|
|
||||||
this.services.set(ILogService, logService)
|
const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
|
||||||
this.services.set(IEnvironmentService, environmentService)
|
await configurationService.initialize();
|
||||||
this.services.set(INativeEnvironmentService, environmentService)
|
this.services.set(IConfigurationService, configurationService);
|
||||||
/*
|
|
||||||
NOTE@coder: we changed this from LoggerService to the loggerService defined above.
|
|
||||||
3/11/21 @jsjoeio
|
|
||||||
*/
|
|
||||||
this.services.set(ILoggerService, loggerService)
|
|
||||||
|
|
||||||
const configurationService = new ConfigurationService(environmentService.settingsResource, fileService)
|
this.services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||||
await configurationService.initialize()
|
this.services.set(IFileService, fileService);
|
||||||
this.services.set(IConfigurationService, configurationService)
|
this.services.set(IProductService, { _serviceBrand: undefined, ...product });
|
||||||
|
|
||||||
this.services.set(IRequestService, new SyncDescriptor(RequestService))
|
const machineId = await getMachineId();
|
||||||
this.services.set(IFileService, fileService)
|
|
||||||
this.services.set(IProductService, { _serviceBrand: undefined, ...product })
|
|
||||||
|
|
||||||
const machineId = await getMachineId()
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
const instantiationService = new InstantiationService(this.services)
|
const instantiationService = new InstantiationService(this.services);
|
||||||
|
|
||||||
instantiationService.invokeFunction((accessor) => {
|
instantiationService.invokeFunction((accessor) => {
|
||||||
instantiationService.createInstance(LogsDataCleaner)
|
instantiationService.createInstance(LogsDataCleaner);
|
||||||
|
|
||||||
let telemetryService: ITelemetryService
|
let telemetryService: ITelemetryService;
|
||||||
if (!environmentService.disableTelemetry) {
|
if (!environmentService.disableTelemetry) {
|
||||||
telemetryService = new TelemetryService(
|
telemetryService = new TelemetryService({
|
||||||
{
|
appender: combinedAppender(
|
||||||
appender: combinedAppender(
|
new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any),
|
||||||
new AppInsightsAppender("code-server", null, () => new TelemetryClient() as any),
|
new TelemetryLogAppender(accessor.get(ILoggerService), environmentService)
|
||||||
new TelemetryLogAppender(accessor.get(ILoggerService), environmentService),
|
),
|
||||||
),
|
sendErrorTelemetry: true,
|
||||||
sendErrorTelemetry: true,
|
commonProperties: resolveCommonProperties(
|
||||||
commonProperties: resolveCommonProperties(
|
fileService, release(), process.arch, product.commit, product.version, machineId,
|
||||||
fileService,
|
[], environmentService.installSourcePath, 'code-server',
|
||||||
release(),
|
),
|
||||||
process.arch,
|
piiPaths,
|
||||||
product.commit,
|
}, configurationService);
|
||||||
product.version,
|
|
||||||
machineId,
|
|
||||||
[],
|
|
||||||
environmentService.installSourcePath,
|
|
||||||
"code-server",
|
|
||||||
),
|
|
||||||
piiPaths,
|
|
||||||
},
|
|
||||||
configurationService,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
telemetryService = NullTelemetryService
|
telemetryService = NullTelemetryService;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.services.set(ITelemetryService, telemetryService)
|
this.services.set(ITelemetryService, telemetryService);
|
||||||
|
|
||||||
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService))
|
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||||
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService))
|
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||||
this.services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService))
|
this.services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
|
||||||
|
|
||||||
this.ipc.registerChannel(
|
this.ipc.registerChannel('extensions', new ExtensionManagementChannel(
|
||||||
"extensions",
|
accessor.get(IExtensionManagementService),
|
||||||
new ExtensionManagementChannel(accessor.get(IExtensionManagementService), (context) =>
|
(context) => getUriTransformer(context.remoteAuthority),
|
||||||
getUriTransformer(context.remoteAuthority),
|
));
|
||||||
),
|
this.ipc.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel(
|
||||||
)
|
environmentService, logService, telemetryService, '',
|
||||||
this.ipc.registerChannel(
|
));
|
||||||
"remoteextensionsenvironment",
|
this.ipc.registerChannel('request', new RequestChannel(accessor.get(IRequestService)));
|
||||||
new ExtensionEnvironmentChannel(environmentService, logService, telemetryService, ""),
|
this.ipc.registerChannel('telemetry', new TelemetryChannel(telemetryService));
|
||||||
)
|
this.ipc.registerChannel('localizations', <IServerChannel<any>>ProxyChannel.fromService(accessor.get(ILocalizationsService)));
|
||||||
this.ipc.registerChannel("request", new RequestChannel(accessor.get(IRequestService)))
|
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
|
||||||
this.ipc.registerChannel("telemetry", new TelemetryChannel(telemetryService))
|
this.ipc.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new TerminalProviderChannel(logService));
|
||||||
/*
|
resolve(new ErrorTelemetry(telemetryService));
|
||||||
NOTE@coder: they renamed createChannelReceiver and made it part of the ProxyChannel namespace
|
});
|
||||||
See: https://github.com/microsoft/vscode/commit/e371faebfb679ca0dcdb61f4f2f33b3d69922a77
|
});
|
||||||
|
|
||||||
And see this as an example similar to our code below:
|
|
||||||
https://github.com/microsoft/vscode/blob/e371faebfb679ca0dcdb61f4f2f33b3d69922a77/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts#L273
|
|
||||||
3/11/2021 by @jsjoeio
|
|
||||||
*/
|
|
||||||
this.ipc.registerChannel(
|
|
||||||
"localizations",
|
|
||||||
<IServerChannel<any>>ProxyChannel.fromService(accessor.get(ILocalizationsService)),
|
|
||||||
)
|
|
||||||
this.ipc.registerChannel(
|
|
||||||
REMOTE_FILE_SYSTEM_CHANNEL_NAME,
|
|
||||||
new FileProviderChannel(environmentService, logService),
|
|
||||||
)
|
|
||||||
this.ipc.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new TerminalProviderChannel(logService))
|
|
||||||
resolve(new ErrorTelemetry(telemetryService))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: implement.
|
* TODO: implement.
|
||||||
*/
|
*/
|
||||||
private async getDebugPort(): Promise<number | undefined> {
|
private async getDebugPort(): Promise<number | undefined> {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { chromium, Page, Browser, BrowserContext, Cookie } from "playwright"
|
||||||
|
import { hash } from "../../src/node/util"
|
||||||
|
import { CODE_SERVER_ADDRESS, PASSWORD, STORAGE, E2E_VIDEO_DIR } from "../utils/constants"
|
||||||
|
import { createCookieIfDoesntExist } from "../utils/helpers"
|
||||||
|
|
||||||
|
describe("Open Help > About", () => {
|
||||||
|
let browser: Browser
|
||||||
|
let page: Page
|
||||||
|
let context: BrowserContext
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
browser = await chromium.launch()
|
||||||
|
// Create a new context with the saved storage state
|
||||||
|
const storageState = JSON.parse(STORAGE) || {}
|
||||||
|
|
||||||
|
const cookieToStore = {
|
||||||
|
sameSite: "Lax" as const,
|
||||||
|
name: "key",
|
||||||
|
value: hash(PASSWORD),
|
||||||
|
domain: "localhost",
|
||||||
|
path: "/",
|
||||||
|
expires: -1,
|
||||||
|
httpOnly: false,
|
||||||
|
secure: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some odd reason, the login method used in globalSetup.ts doesn't always work
|
||||||
|
// I don't know if it's on playwright clearing our cookies by accident
|
||||||
|
// or if it's our cookies disappearing.
|
||||||
|
// This means we need an additional check to make sure we're logged in.
|
||||||
|
// We do this by manually adding the cookie to the browser environment
|
||||||
|
// if it's not there at the time the test starts
|
||||||
|
const cookies: Cookie[] = storageState.cookies || []
|
||||||
|
// If the cookie exists in cookies then
|
||||||
|
// this will return the cookies with no changes
|
||||||
|
// otherwise if it doesn't exist, it will create it
|
||||||
|
// hence the name maybeUpdatedCookies
|
||||||
|
//
|
||||||
|
// TODO(@jsjoeio)
|
||||||
|
// The playwright storage thing sometimes works and sometimes doesn't. We should investigate this further
|
||||||
|
// at some point.
|
||||||
|
// See discussion: https://github.com/cdr/code-server/pull/2648#discussion_r575434946
|
||||||
|
|
||||||
|
const maybeUpdatedCookies = createCookieIfDoesntExist(cookies, cookieToStore)
|
||||||
|
|
||||||
|
context = await browser.newContext({
|
||||||
|
storageState: { cookies: maybeUpdatedCookies },
|
||||||
|
recordVideo: { dir: E2E_VIDEO_DIR },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Remove password from local storage
|
||||||
|
await context.clearCookies()
|
||||||
|
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
page = await context.newPage()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should see a 'Help' then 'About' button in the Application Menu that opens a dialog", async () => {
|
||||||
|
// waitUntil: "domcontentloaded"
|
||||||
|
// In case the page takes a long time to load
|
||||||
|
await page.goto(CODE_SERVER_ADDRESS, { waitUntil: "domcontentloaded" })
|
||||||
|
|
||||||
|
// Make sure the editor actually loaded
|
||||||
|
expect(await page.isVisible("div.monaco-workbench"))
|
||||||
|
|
||||||
|
// Click the Application menu
|
||||||
|
await page.click("[aria-label='Application Menu']")
|
||||||
|
// See the Help button
|
||||||
|
const helpButton = "a.action-menu-item span[aria-label='Help']"
|
||||||
|
expect(await page.isVisible(helpButton))
|
||||||
|
|
||||||
|
// Hover the helpButton
|
||||||
|
await page.hover(helpButton)
|
||||||
|
|
||||||
|
// see the About button and click it
|
||||||
|
const aboutButton = "a.action-menu-item span[aria-label='About']"
|
||||||
|
expect(await page.isVisible(aboutButton))
|
||||||
|
// NOTE: it won't work unless you hover it first
|
||||||
|
await page.hover(aboutButton)
|
||||||
|
await page.click(aboutButton)
|
||||||
|
|
||||||
|
const codeServerText = "text=code-server"
|
||||||
|
expect(await page.isVisible(codeServerText))
|
||||||
|
})
|
||||||
|
})
|
|
@ -7,7 +7,7 @@ const config: Config.InitialOptions = {
|
||||||
},
|
},
|
||||||
globalSetup: "<rootDir>/utils/globalSetup.ts",
|
globalSetup: "<rootDir>/utils/globalSetup.ts",
|
||||||
testEnvironment: "node",
|
testEnvironment: "node",
|
||||||
testPathIgnorePatterns: ["node_modules", "lib", "out", "test/unit"],
|
testPathIgnorePatterns: ["/node_modules/", "/lib/", "/out/", "test/unit"],
|
||||||
testTimeout: 30000,
|
testTimeout: 30000,
|
||||||
modulePathIgnorePatterns: [
|
modulePathIgnorePatterns: [
|
||||||
"<rootDir>/../lib/vscode",
|
"<rootDir>/../lib/vscode",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"license": "MIT",
|
||||||
"#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.",
|
"#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
|
|
Loading…
Reference in New Issue