From 03d1b8cbc872cdee6bb8000c2d029c6524036c00 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 15 May 2024 13:52:23 +0200 Subject: [PATCH 01/14] basics for preferences, remoteUser and Post Create Command Signed-off-by: Jonah Iden --- .../src/node/backend-application-module.ts | 4 ++ .../preference-cli-contribution.ts | 37 +++++++++++++++++++ .../main-container-creation-contributions.ts | 28 ++++++++++++++ .../electron-node/docker-container-service.ts | 13 ++++++- 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/node/preferences/preference-cli-contribution.ts diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index f5a17db9bd514..daf01f58c937b 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -43,6 +43,7 @@ import { BackendRequestFacade } from './request/backend-request-facade'; import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking'; import { BackendRemoteService } from './remote/backend-remote-service'; import { RemoteCliContribution } from './remote/remote-cli-contribution'; +import { PreferenceCliContribution } from './preferences/preference-cli-contribution'; decorate(injectable(), ApplicationPackage); @@ -136,4 +137,7 @@ export const backendApplicationModule = new ContainerModule(bind => { bindBackendStopwatchServer(bind); bind(FileSystemLocking).to(FileSystemLockingImpl).inSingletonScope(); + + bind(PreferenceCliContribution).toSelf().inSingletonScope(); + bind(CliContribution).toService(PreferenceCliContribution); }); diff --git a/packages/core/src/node/preferences/preference-cli-contribution.ts b/packages/core/src/node/preferences/preference-cli-contribution.ts new file mode 100644 index 0000000000000..654e38f9313b1 --- /dev/null +++ b/packages/core/src/node/preferences/preference-cli-contribution.ts @@ -0,0 +1,37 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from 'inversify'; +import { Argv } from 'yargs'; +import { MaybePromise } from '../../common'; +import { CliContribution } from '../cli'; + +@injectable() +export class PreferenceCliContribution implements CliContribution { + + configure(conf: Argv<{}>): void { + conf.option('set-preference', { + alias: 'preference', + nargs: 1, + desc: 'Installs or updates a plugin. Argument is a path to the *.vsix file or a plugin id of the form "publisher.name[@version]"' + }) + } + + setArguments(args: Record): MaybePromise { + + } + +} diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index c0c91473201f0..88d16837e38bf 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -25,6 +25,7 @@ export function registerContainerCreationContributions(bind: interfaces.Bind): v bind(ContainerCreationContribution).to(DockerFileContribution).inSingletonScope(); bind(ContainerCreationContribution).to(ForwardPortsContribution).inSingletonScope(); bind(ContainerCreationContribution).to(MountsContribution).inSingletonScope(); + bind(ContainerCreationContribution).to(RemoteUserContribution).inSingletonScope(); } @injectable() @@ -126,6 +127,33 @@ export class MountsContribution implements ContainerCreationContribution { } } +@injectable() +export class RemoteUserContribution implements ContainerCreationContribution { + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise { + if (containerConfig.remoteUser) { + createOptions.User = containerConfig.remoteUser; + } + } +} + +@injectable() +export class PostCreateCommandContribution implements ContainerCreationContribution { + async handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container): Promise { + if (containerConfig.postCreateCommand) { + const commands = typeof containerConfig.postCreateCommand === 'object' && !(containerConfig.postCreateCommand instanceof Array) ? + Object.values(containerConfig.postCreateCommand) : [containerConfig.postCreateCommand]; + for (const command of commands) { + if (command instanceof Array) { + await container.exec({ Cmd: command, }); + } else { + await container.exec({ Cmd: ['sh', '-c', command], }); + + } + } + } + } +} + export namespace OutputHelper { export interface Progress { id?: string; diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index 3626e7c00b762..a590bc94f50a8 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -27,10 +27,15 @@ import { ContainerOutputProvider } from '../electron-common/container-output-pro export const ContainerCreationContribution = Symbol('ContainerCreationContributions'); export interface ContainerCreationContribution { - handleContainerCreation(createOptions: Docker.ContainerCreateOptions, + handleContainerCreation?(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker, outputProvider?: ContainerOutputProvider): Promise; + + /** + * executed after creating and starting the container + */ + handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker): Promise; } @injectable() @@ -92,13 +97,17 @@ export class DockerContainerService { }; for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { - await containerCreateContrib.handleContainerCreation(containerCreateOptions, devcontainerConfig, docker, outputProvider); + await containerCreateContrib.handleContainerCreation?.(containerCreateOptions, devcontainerConfig, docker, outputProvider); } // TODO add more config const container = await docker.createContainer(containerCreateOptions); await container.start(); + for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { + await containerCreateContrib.handlePostCreate?.(devcontainerConfig, container, docker); + } + return container; } From fea7b378d4a62720adbc95fd7446cca6007d9d72 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 15 May 2024 16:44:18 +0200 Subject: [PATCH 02/14] setting preferences from command line when starting theia Signed-off-by: Jonah Iden --- .../src/node/backend-application-module.ts | 4 -- packages/preferences/package.json | 3 +- .../preference-frontend-contribution.ts | 38 +++++++++++++++ .../src/browser/preference-frontend-module.ts | 9 +++- .../src/common/cli-preferences.ts} | 22 ++------- .../src/node/preference-backend-module.ts | 36 ++++++++++++++ .../src/node/preference-cli-contribution.ts | 48 +++++++++++++++++++ 7 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 packages/preferences/src/browser/preference-frontend-contribution.ts rename packages/{core/src/node/preferences/preference-cli-contribution.ts => preferences/src/common/cli-preferences.ts} (57%) create mode 100644 packages/preferences/src/node/preference-backend-module.ts create mode 100644 packages/preferences/src/node/preference-cli-contribution.ts diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index daf01f58c937b..f5a17db9bd514 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -43,7 +43,6 @@ import { BackendRequestFacade } from './request/backend-request-facade'; import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking'; import { BackendRemoteService } from './remote/backend-remote-service'; import { RemoteCliContribution } from './remote/remote-cli-contribution'; -import { PreferenceCliContribution } from './preferences/preference-cli-contribution'; decorate(injectable(), ApplicationPackage); @@ -137,7 +136,4 @@ export const backendApplicationModule = new ContainerModule(bind => { bindBackendStopwatchServer(bind); bind(FileSystemLocking).to(FileSystemLockingImpl).inSingletonScope(); - - bind(PreferenceCliContribution).toSelf().inSingletonScope(); - bind(CliContribution).toService(PreferenceCliContribution); }); diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 85fbaa30a8357..67c9f1529209d 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -21,7 +21,8 @@ }, "theiaExtensions": [ { - "frontend": "lib/browser/preference-frontend-module" + "frontend": "lib/browser/preference-frontend-module", + "backend": "lib/node/preference-backend-module" } ], "keywords": [ diff --git a/packages/preferences/src/browser/preference-frontend-contribution.ts b/packages/preferences/src/browser/preference-frontend-contribution.ts new file mode 100644 index 0000000000000..6ba05ad6763ba --- /dev/null +++ b/packages/preferences/src/browser/preference-frontend-contribution.ts @@ -0,0 +1,38 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution } from '@theia/core/src/browser'; +import { CliPreferences } from '../common/cli-preferences'; +import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser/preferences/preference-service'; + +@injectable() +export class PreferenceFrontendContribution implements FrontendApplicationContribution { + @inject(CliPreferences) + protected readonly CliPreferences: CliPreferences; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + onStart(): void { + this.CliPreferences.getPreferences().then(async preferences => { + await this.preferenceService.ready + for (const [key, value] of preferences) { + this.preferenceService.set(key, value, PreferenceScope.User); + } + }); + } +} diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index ec3c01aace7b4..ad9ae857776b8 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -17,7 +17,7 @@ import '../../src/browser/style/index.css'; import './preferences-monaco-contribution'; import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; -import { bindViewContribution, OpenHandler } from '@theia/core/lib/browser'; +import { bindViewContribution, FrontendApplicationContribution, OpenHandler } from '@theia/core/lib/browser'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { PreferenceTreeGenerator } from './util/preference-tree-generator'; import { bindPreferenceProviders } from './preference-bindings'; @@ -29,6 +29,9 @@ import { PreferencesJsonSchemaContribution } from './preferences-json-schema-con import { MonacoJSONCEditor } from './monaco-jsonc-editor'; import { PreferenceTransaction, PreferenceTransactionFactory, preferenceTransactionFactoryCreator } from './preference-transaction-manager'; import { PreferenceOpenHandler } from './preference-open-handler'; +import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; +import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; +import { PreferenceFrontendContribution } from './preference-frontend-contribution'; export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind): void { bindPreferenceProviders(bind, unbind); @@ -50,6 +53,10 @@ export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind bind(MonacoJSONCEditor).toSelf().inSingletonScope(); bind(PreferenceTransaction).toSelf(); bind(PreferenceTransactionFactory).toFactory(preferenceTransactionFactoryCreator); + + bind(CliPreferences).toDynamicValue(ctx => ServiceConnectionProvider.createProxy(ctx.container, CliPreferencesPath)).inSingletonScope(); + bind(PreferenceFrontendContribution).toSelf().inSingletonScope() + bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution) } export default new ContainerModule((bind, unbind, isBound, rebind) => { diff --git a/packages/core/src/node/preferences/preference-cli-contribution.ts b/packages/preferences/src/common/cli-preferences.ts similarity index 57% rename from packages/core/src/node/preferences/preference-cli-contribution.ts rename to packages/preferences/src/common/cli-preferences.ts index 654e38f9313b1..1847567166c37 100644 --- a/packages/core/src/node/preferences/preference-cli-contribution.ts +++ b/packages/preferences/src/common/cli-preferences.ts @@ -14,24 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable } from 'inversify'; -import { Argv } from 'yargs'; -import { MaybePromise } from '../../common'; -import { CliContribution } from '../cli'; -@injectable() -export class PreferenceCliContribution implements CliContribution { - - configure(conf: Argv<{}>): void { - conf.option('set-preference', { - alias: 'preference', - nargs: 1, - desc: 'Installs or updates a plugin. Argument is a path to the *.vsix file or a plugin id of the form "publisher.name[@version]"' - }) - } - - setArguments(args: Record): MaybePromise { - - } +export const CliPreferences = Symbol('CliPreferences'); +export const CliPreferencesPath = '/services/cli-preferences'; +export interface CliPreferences { + getPreferences(): Promise<[string, unknown][]>; } diff --git a/packages/preferences/src/node/preference-backend-module.ts b/packages/preferences/src/node/preference-backend-module.ts new file mode 100644 index 0000000000000..b261349aabf58 --- /dev/null +++ b/packages/preferences/src/node/preference-backend-module.ts @@ -0,0 +1,36 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { CliContribution } from '@theia/core/lib/node/cli'; +import { PreferenceCliContribution } from './preference-cli-contribution'; +import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module'; +import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; + +const preferencesConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { + bindBackendService(CliPreferencesPath, CliPreferences) +}); + + +export default new ContainerModule(bind => { + bind(PreferenceCliContribution).toSelf().inSingletonScope(); + bind(CliPreferences).toService(PreferenceCliContribution); + bind(CliContribution).toService(PreferenceCliContribution); + + bind(ConnectionContainerModule).toConstantValue(preferencesConnectionModule); +}); + + diff --git a/packages/preferences/src/node/preference-cli-contribution.ts b/packages/preferences/src/node/preference-cli-contribution.ts new file mode 100644 index 0000000000000..7a3bf8ab9e60e --- /dev/null +++ b/packages/preferences/src/node/preference-cli-contribution.ts @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from 'inversify'; +import { Argv } from 'yargs'; +import { CliContribution } from '@theia/core/lib/node/cli'; +import { CliPreferences } from '../common/cli-preferences'; + +@injectable() +export class PreferenceCliContribution implements CliContribution, CliPreferences { + + protected preferences: [string, unknown][] = []; + + configure(conf: Argv<{}>): void { + conf.option('set-preference', { + nargs: 1, + desc: 'sets the specified preference' + }) + } + + setArguments(args: Record): void { + if (args.setPreference) { + const preferences: string[] = args.setPreference instanceof Array ? args.setPreference : [args.setPreference]; + for (const preference of preferences) { + const firstEqualIndex = preference.indexOf('='); + this.preferences.push([preference.substring(0, firstEqualIndex), JSON.parse(preference.substring(firstEqualIndex + 1))]) + } + } + } + + async getPreferences(): Promise<[string, unknown][]> { + return this.preferences; + } + +} From 072f11e85a6c2bd41aa1753d1adc5f8341ca5e09 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 16 May 2024 13:16:37 +0200 Subject: [PATCH 03/14] implemented more devcontainer properties: PostCreateCommand, RemoteUser, sttings and extensions Signed-off-by: Jonah Iden --- .../dev-container-backend-module.ts | 8 +++ .../cli-enhancing-creation-contributions.ts | 69 +++++++++++++++++++ .../main-container-creation-contributions.ts | 64 ++++++++++------- .../src/electron-node/devcontainer-file.ts | 33 ++++++++- .../electron-node/docker-container-service.ts | 11 +-- 5 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts diff --git a/packages/dev-container/src/electron-node/dev-container-backend-module.ts b/packages/dev-container/src/electron-node/dev-container-backend-module.ts index cac6e5e74ebf4..ff56ba14ab842 100644 --- a/packages/dev-container/src/electron-node/dev-container-backend-module.ts +++ b/packages/dev-container/src/electron-node/dev-container-backend-module.ts @@ -23,10 +23,13 @@ import { bindContributionProvider, ConnectionHandler, RpcConnectionHandler } fro import { registerContainerCreationContributions } from './devcontainer-contributions/main-container-creation-contributions'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; +import { ExtensionsContribution, registerTheiaStartOptionsContributions, SettingsContribution } from './devcontainer-contributions/cli-enhancing-creation-contributions'; +import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bindContributionProvider(bind, ContainerCreationContribution); registerContainerCreationContributions(bind); + registerTheiaStartOptionsContributions(bind); bind(DevContainerConnectionProvider).toSelf().inSingletonScope(); bind(RemoteContainerConnectionProvider).toService(DevContainerConnectionProvider); @@ -44,4 +47,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ConnectionContainerModule).toConstantValue(remoteConnectionModule); bind(DevContainerFileService).toSelf().inSingletonScope(); + + bind(ExtensionsContribution).toSelf().inSingletonScope(); + bind(SettingsContribution).toSelf().inSingletonScope(); + bind(RemoteCliContribution).toService(ExtensionsContribution); + bind(RemoteCliContribution).toService(SettingsContribution); }); diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts new file mode 100644 index 0000000000000..46e412d368ecc --- /dev/null +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts @@ -0,0 +1,69 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { RemoteCliContext, RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { ContainerCreationContribution } from '../docker-container-service'; +import * as Docker from 'dockerode'; +import { DevContainerConfiguration, } from '../devcontainer-file'; +import { injectable, interfaces } from '@theia/core/shared/inversify'; + +export function registerTheiaStartOptionsContributions(bind: interfaces.Bind): void { + + bind(ContainerCreationContribution).toService(ExtensionsContribution); + bind(ContainerCreationContribution).toService(SettingsContribution); +} + +@injectable() +export class ExtensionsContribution implements RemoteCliContribution, ContainerCreationContribution { + protected currentConfig: DevContainerConfiguration | undefined; + + enhanceArgs(context: RemoteCliContext): string[] { + if (!this.currentConfig) { + return []; + } + const extensions = [ + ...(this.currentConfig.extensions ?? []), + ...(this.currentConfig.customizations?.vscode?.extensions ?? []) + ]; + this.currentConfig = undefined; + return extensions?.map(extension => `--install-plugin=${extension}`) + } + + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { + this.currentConfig = containerConfig; + } +} + +@injectable() +export class SettingsContribution implements RemoteCliContribution, ContainerCreationContribution { + protected currentConfig: DevContainerConfiguration | undefined; + + enhanceArgs(context: RemoteCliContext): string[] { + if (!this.currentConfig) { + return []; + } + const settings = { + ...(this.currentConfig.settings ?? {}), + ...(this.currentConfig.customizations?.vscode?.settings ?? []) + } + this.currentConfig = undefined; + return Object.entries(settings).map(([key, value]) => `--set-preference=${key}=${JSON.stringify(value)}`) ?? [] + } + + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { + this.currentConfig = containerConfig; + } +} diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index 88d16837e38bf..3e0a903c67510 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -19,6 +19,7 @@ import { ContainerCreationContribution } from '../docker-container-service'; import { DevContainerConfiguration, DockerfileContainer, ImageContainer, NonComposeContainerBase } from '../devcontainer-file'; import { Path } from '@theia/core'; import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; +import * as fs from '@theia/core/shared/fs-extra'; export function registerContainerCreationContributions(bind: interfaces.Bind): void { bind(ContainerCreationContribution).to(ImageFileContribution).inSingletonScope(); @@ -26,6 +27,7 @@ export function registerContainerCreationContributions(bind: interfaces.Bind): v bind(ContainerCreationContribution).to(ForwardPortsContribution).inSingletonScope(); bind(ContainerCreationContribution).to(MountsContribution).inSingletonScope(); bind(ContainerCreationContribution).to(RemoteUserContribution).inSingletonScope(); + bind(ContainerCreationContribution).to(PostCreateCommandContribution).inSingletonScope(); } @injectable() @@ -54,26 +56,35 @@ export class DockerFileContribution implements ContainerCreationContribution { // check if dockerfile container if (containerConfig.dockerFile || containerConfig.build?.dockerfile) { const dockerfile = (containerConfig.dockerFile ?? containerConfig.build?.dockerfile) as string; - const buildStream = await api.buildImage({ - context: containerConfig.context ?? new Path(containerConfig.location as string).dir.fsPath(), - src: [dockerfile], - } as Docker.ImageBuildContext, { - buildargs: containerConfig.build?.args - }); - // TODO probably have some console windows showing the output of the build - const imageId = await new Promise((res, rej) => api.modem.followProgress(buildStream, (err, outputs) => { - if (err) { - rej(err); - } else { - for (let i = outputs.length - 1; i >= 0; i--) { - if (outputs[i].aux?.ID) { - res(outputs[i].aux.ID); - return; + const context = containerConfig.context ?? new Path(containerConfig.location as string).dir.fsPath(); + try { + // ensure dockerfile exists + await fs.lstat(new Path(context as string).join(dockerfile).fsPath()); + + const buildStream = await api.buildImage({ + context, + src: [dockerfile], + } as Docker.ImageBuildContext, { + buildargs: containerConfig.build?.args + }); + // TODO probably have some console windows showing the output of the build + const imageId = await new Promise((res, rej) => api.modem.followProgress(buildStream!, (err, outputs) => { + if (err) { + rej(err); + } else { + for (let i = outputs.length - 1; i >= 0; i--) { + if (outputs[i].aux?.ID) { + res(outputs[i].aux.ID); + return; + } } } - } - }, progress => outputprovider.onRemoteOutput(OutputHelper.parseProgress(progress)))); - createOptions.Image = imageId; + }, progress => outputprovider.onRemoteOutput(OutputHelper.parseProgress(progress)))); + createOptions.Image = imageId; + } catch (error) { + outputprovider.onRemoteOutput(`could not build dockerfile "${dockerfile}" reason: ${error.message}`); + throw error; + } } } } @@ -138,16 +149,23 @@ export class RemoteUserContribution implements ContainerCreationContribution { @injectable() export class PostCreateCommandContribution implements ContainerCreationContribution { - async handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container): Promise { + async handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider): Promise { if (containerConfig.postCreateCommand) { const commands = typeof containerConfig.postCreateCommand === 'object' && !(containerConfig.postCreateCommand instanceof Array) ? Object.values(containerConfig.postCreateCommand) : [containerConfig.postCreateCommand]; for (const command of commands) { - if (command instanceof Array) { - await container.exec({ Cmd: command, }); - } else { - await container.exec({ Cmd: ['sh', '-c', command], }); + try { + let exec; + if (command instanceof Array) { + exec = await container.exec({ Cmd: command, AttachStderr: true, AttachStdout: true }); + } else { + exec = await container.exec({ Cmd: ['sh', '-c', command], AttachStderr: true, AttachStdout: true }); + } + const stream = await exec.start({ Tty: true }) + stream.on('data', (chunk) => outputprovider.onRemoteOutput(chunk.toString())); + } catch (error) { + outputprovider.onRemoteOutput('could not execute postCreateCommand ' + JSON.stringify(command) + ' reason:' + error.message); } } } diff --git a/packages/dev-container/src/electron-node/devcontainer-file.ts b/packages/dev-container/src/electron-node/devcontainer-file.ts index 5bd948dd17a90..1c5a369ec7d7c 100644 --- a/packages/dev-container/src/electron-node/devcontainer-file.ts +++ b/packages/dev-container/src/electron-node/devcontainer-file.ts @@ -258,6 +258,21 @@ export interface DevContainerCommon { * The default is the same user as the container. */ remoteUser?: string + + /** + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * The default is no extensions being installed. + */ + extensions?: string[] + + /** + * settings to set in the container at launch in the settings.json. The expected format is key=value. + * The default is no preferences being set. + */ + settings?: { + [k: string]: unknown + } + /** * A command to run locally before anything else. This command is run before 'onCreateCommand'. * If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. @@ -365,7 +380,23 @@ export interface DevContainerCommon { * Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations. */ customizations?: { - [k: string]: unknown + [k: string]: unknown, + vscode?: { + /** + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * The default is no extensions being installed. + */ + extensions?: string[], + + /** + * settings to set in the container at launch in the settings.json. The expected format is key=value. + * The default is no preferences being set. + */ + settings?: { + [k: string]: unknown + } + [k: string]: unknown + } } additionalProperties?: { [k: string]: unknown diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index a590bc94f50a8..675c88695c53d 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { ContributionProvider, URI } from '@theia/core'; +import { ContributionProvider, MaybePromise, URI } from '@theia/core'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import { WorkspaceServer } from '@theia/workspace/lib/common'; import * as fs from '@theia/core/shared/fs-extra'; @@ -30,12 +30,15 @@ export interface ContainerCreationContribution { handleContainerCreation?(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker, - outputProvider?: ContainerOutputProvider): Promise; + outputProvider?: ContainerOutputProvider): MaybePromise; /** * executed after creating and starting the container */ - handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker): Promise; + handlePostCreate?(containerConfig: DevContainerConfiguration, + container: Docker.Container, + api: Docker, + outputProvider?: ContainerOutputProvider): MaybePromise; } @injectable() @@ -105,7 +108,7 @@ export class DockerContainerService { await container.start(); for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { - await containerCreateContrib.handlePostCreate?.(devcontainerConfig, container, docker); + await containerCreateContrib.handlePostCreate?.(devcontainerConfig, container, docker, outputProvider); } return container; From 9712ab746fd0dedae2549d69c79997ca2c541f57 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 16 May 2024 14:44:26 +0200 Subject: [PATCH 04/14] lint Signed-off-by: Jonah Iden --- .../cli-enhancing-creation-contributions.ts | 3 +-- .../src/browser/preference-frontend-contribution.ts | 4 ++-- .../preferences/src/browser/preference-frontend-module.ts | 4 ++-- packages/preferences/src/common/cli-preferences.ts | 1 - .../preferences/src/node/preference-backend-module.ts | 5 +---- .../preferences/src/node/preference-cli-contribution.ts | 8 ++++---- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts index 46e412d368ecc..de984872308d5 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts @@ -21,7 +21,6 @@ import { DevContainerConfiguration, } from '../devcontainer-file'; import { injectable, interfaces } from '@theia/core/shared/inversify'; export function registerTheiaStartOptionsContributions(bind: interfaces.Bind): void { - bind(ContainerCreationContribution).toService(ExtensionsContribution); bind(ContainerCreationContribution).toService(SettingsContribution); } @@ -39,7 +38,7 @@ export class ExtensionsContribution implements RemoteCliContribution, ContainerC ...(this.currentConfig.customizations?.vscode?.extensions ?? []) ]; this.currentConfig = undefined; - return extensions?.map(extension => `--install-plugin=${extension}`) + return extensions?.map(extension => `--install-plugin=${extension}`); } async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { diff --git a/packages/preferences/src/browser/preference-frontend-contribution.ts b/packages/preferences/src/browser/preference-frontend-contribution.ts index 6ba05ad6763ba..6f8a025236ff5 100644 --- a/packages/preferences/src/browser/preference-frontend-contribution.ts +++ b/packages/preferences/src/browser/preference-frontend-contribution.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { inject, injectable } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution } from '@theia/core/src/browser'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; import { CliPreferences } from '../common/cli-preferences'; import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser/preferences/preference-service'; @@ -29,7 +29,7 @@ export class PreferenceFrontendContribution implements FrontendApplicationContri onStart(): void { this.CliPreferences.getPreferences().then(async preferences => { - await this.preferenceService.ready + await this.preferenceService.ready; for (const [key, value] of preferences) { this.preferenceService.set(key, value, PreferenceScope.User); } diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index ad9ae857776b8..a66fbace9102e 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -55,8 +55,8 @@ export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind bind(PreferenceTransactionFactory).toFactory(preferenceTransactionFactoryCreator); bind(CliPreferences).toDynamicValue(ctx => ServiceConnectionProvider.createProxy(ctx.container, CliPreferencesPath)).inSingletonScope(); - bind(PreferenceFrontendContribution).toSelf().inSingletonScope() - bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution) + bind(PreferenceFrontendContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution); } export default new ContainerModule((bind, unbind, isBound, rebind) => { diff --git a/packages/preferences/src/common/cli-preferences.ts b/packages/preferences/src/common/cli-preferences.ts index 1847567166c37..f17d5d762dad0 100644 --- a/packages/preferences/src/common/cli-preferences.ts +++ b/packages/preferences/src/common/cli-preferences.ts @@ -14,7 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** - export const CliPreferences = Symbol('CliPreferences'); export const CliPreferencesPath = '/services/cli-preferences'; diff --git a/packages/preferences/src/node/preference-backend-module.ts b/packages/preferences/src/node/preference-backend-module.ts index b261349aabf58..7b0db89f887d0 100644 --- a/packages/preferences/src/node/preference-backend-module.ts +++ b/packages/preferences/src/node/preference-backend-module.ts @@ -21,10 +21,9 @@ import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connec import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; const preferencesConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { - bindBackendService(CliPreferencesPath, CliPreferences) + bindBackendService(CliPreferencesPath, CliPreferences); }); - export default new ContainerModule(bind => { bind(PreferenceCliContribution).toSelf().inSingletonScope(); bind(CliPreferences).toService(PreferenceCliContribution); @@ -32,5 +31,3 @@ export default new ContainerModule(bind => { bind(ConnectionContainerModule).toConstantValue(preferencesConnectionModule); }); - - diff --git a/packages/preferences/src/node/preference-cli-contribution.ts b/packages/preferences/src/node/preference-cli-contribution.ts index 7a3bf8ab9e60e..0fd46963b486e 100644 --- a/packages/preferences/src/node/preference-cli-contribution.ts +++ b/packages/preferences/src/node/preference-cli-contribution.ts @@ -14,8 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable } from 'inversify'; -import { Argv } from 'yargs'; +import { injectable } from '@theia/core/shared/inversify'; +import { Argv } from '@theia/core/shared/yargs'; import { CliContribution } from '@theia/core/lib/node/cli'; import { CliPreferences } from '../common/cli-preferences'; @@ -28,7 +28,7 @@ export class PreferenceCliContribution implements CliContribution, CliPreference conf.option('set-preference', { nargs: 1, desc: 'sets the specified preference' - }) + }); } setArguments(args: Record): void { @@ -36,7 +36,7 @@ export class PreferenceCliContribution implements CliContribution, CliPreference const preferences: string[] = args.setPreference instanceof Array ? args.setPreference : [args.setPreference]; for (const preference of preferences) { const firstEqualIndex = preference.indexOf('='); - this.preferences.push([preference.substring(0, firstEqualIndex), JSON.parse(preference.substring(firstEqualIndex + 1))]) + this.preferences.push([preference.substring(0, firstEqualIndex), JSON.parse(preference.substring(firstEqualIndex + 1))]); } } } From 15ff25642d6045bbf04a2be0603d6b87b0e57e34 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 16 May 2024 14:55:17 +0200 Subject: [PATCH 05/14] more lint Signed-off-by: Jonah Iden --- .../cli-enhancing-creation-contributions.ts | 4 ++-- .../main-container-creation-contributions.ts | 4 ++-- .../dev-container/src/electron-node/devcontainer-file.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts index de984872308d5..7410d83ea1d06 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts @@ -57,9 +57,9 @@ export class SettingsContribution implements RemoteCliContribution, ContainerCre const settings = { ...(this.currentConfig.settings ?? {}), ...(this.currentConfig.customizations?.vscode?.settings ?? []) - } + }; this.currentConfig = undefined; - return Object.entries(settings).map(([key, value]) => `--set-preference=${key}=${JSON.stringify(value)}`) ?? [] + return Object.entries(settings).map(([key, value]) => `--set-preference=${key}=${JSON.stringify(value)}`) ?? []; } async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index 3e0a903c67510..fd8521d9dd0cb 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -162,8 +162,8 @@ export class PostCreateCommandContribution implements ContainerCreationContribut } else { exec = await container.exec({ Cmd: ['sh', '-c', command], AttachStderr: true, AttachStdout: true }); } - const stream = await exec.start({ Tty: true }) - stream.on('data', (chunk) => outputprovider.onRemoteOutput(chunk.toString())); + const stream = await exec.start({ Tty: true }); + stream.on('data', chunk => outputprovider.onRemoteOutput(chunk.toString())); } catch (error) { outputprovider.onRemoteOutput('could not execute postCreateCommand ' + JSON.stringify(command) + ' reason:' + error.message); } diff --git a/packages/dev-container/src/electron-node/devcontainer-file.ts b/packages/dev-container/src/electron-node/devcontainer-file.ts index 1c5a369ec7d7c..ec77ac4b5e7f3 100644 --- a/packages/dev-container/src/electron-node/devcontainer-file.ts +++ b/packages/dev-container/src/electron-node/devcontainer-file.ts @@ -260,13 +260,13 @@ export interface DevContainerCommon { remoteUser?: string /** - * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. * The default is no extensions being installed. */ extensions?: string[] /** - * settings to set in the container at launch in the settings.json. The expected format is key=value. + * settings to set in the container at launch in the settings.json. The expected format is key=value. * The default is no preferences being set. */ settings?: { @@ -383,13 +383,13 @@ export interface DevContainerCommon { [k: string]: unknown, vscode?: { /** - * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. * The default is no extensions being installed. */ extensions?: string[], /** - * settings to set in the container at launch in the settings.json. The expected format is key=value. + * settings to set in the container at launch in the settings.json. The expected format is key=value. * The default is no preferences being set. */ settings?: { From 46d47ae8076bea7bd5e7ff0f2b387aab06a36e03 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 22 May 2024 14:54:29 +0200 Subject: [PATCH 06/14] modify /etc/profile if possible to not have it overwrite the path when launching a terminal Signed-off-by: Jonah Iden --- .../dev-container-backend-module.ts | 3 ++ .../profile-file-modification-contribution.ts | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts diff --git a/packages/dev-container/src/electron-node/dev-container-backend-module.ts b/packages/dev-container/src/electron-node/dev-container-backend-module.ts index ff56ba14ab842..e07d5d7a8ec0d 100644 --- a/packages/dev-container/src/electron-node/dev-container-backend-module.ts +++ b/packages/dev-container/src/electron-node/dev-container-backend-module.ts @@ -25,11 +25,14 @@ import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; import { ExtensionsContribution, registerTheiaStartOptionsContributions, SettingsContribution } from './devcontainer-contributions/cli-enhancing-creation-contributions'; import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { ProfileFileModificationContribution } from './devcontainer-contributions/profile-file-modification-contribution'; export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bindContributionProvider(bind, ContainerCreationContribution); registerContainerCreationContributions(bind); registerTheiaStartOptionsContributions(bind); + bind(ProfileFileModificationContribution).toSelf().inSingletonScope(); + bind(ContainerCreationContribution).toService(ProfileFileModificationContribution) bind(DevContainerConnectionProvider).toSelf().inSingletonScope(); bind(RemoteContainerConnectionProvider).toService(DevContainerConnectionProvider); diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts new file mode 100644 index 0000000000000..c0e1f19df91b6 --- /dev/null +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { DevContainerConfiguration } from '../devcontainer-file'; +import { ContainerCreationContribution } from '../docker-container-service'; +import * as Docker from 'dockerode'; +import { ContainerOutputProvider } from '../../electron-browser/container-output-provider'; +import { injectable } from '@theia/core/shared/inversify'; + +/** + * this contribution changes the /etc/profile file so that it won't overwrite the PATH variable set by docker + */ +@injectable() +export class ProfileFileModificationContribution implements ContainerCreationContribution { + async handlePostCreate(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider) { + const stream = await (await container.exec({ + Cmd: ['sh', '-c', 'sed -i \'s|PATH="\\([^"]*\\)"|PATH=${PATH:-"\\1"}|g\' /etc/profile'], User: 'root', + AttachStderr: true, AttachStdout: true + })).start({}); + stream.on('data', data => outputprovider.onRemoteOutput(data.toString())); + } +} From 4d107af6f0b2c1b631be0d31ea97af5eff2366ec Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 22 May 2024 15:16:08 +0200 Subject: [PATCH 07/14] fixed stopping containers on conenction dispose Signed-off-by: Jonah Iden --- .../electron-node/remote-container-connection-provider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 214b98b3a19ad..5a89b295f1068 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -287,8 +287,9 @@ export class RemoteDockerContainerConnection implements RemoteConnection { return deferred.promise; } - dispose(): void { - this.container.stop(); + async dispose(): Promise { + // cant use dockerrode here since this needs to happen on one tick + exec(`docker stop ${this.container.id}`); } } From 0e48b7a01ce5702e6af2d39e904ecc9ff6130a35 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 22 May 2024 15:40:16 +0200 Subject: [PATCH 08/14] lint Signed-off-by: Jonah Iden --- .../src/electron-node/dev-container-backend-module.ts | 2 +- .../profile-file-modification-contribution.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dev-container/src/electron-node/dev-container-backend-module.ts b/packages/dev-container/src/electron-node/dev-container-backend-module.ts index e07d5d7a8ec0d..e03066275fba8 100644 --- a/packages/dev-container/src/electron-node/dev-container-backend-module.ts +++ b/packages/dev-container/src/electron-node/dev-container-backend-module.ts @@ -32,7 +32,7 @@ export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, registerContainerCreationContributions(bind); registerTheiaStartOptionsContributions(bind); bind(ProfileFileModificationContribution).toSelf().inSingletonScope(); - bind(ContainerCreationContribution).toService(ProfileFileModificationContribution) + bind(ContainerCreationContribution).toService(ProfileFileModificationContribution); bind(DevContainerConnectionProvider).toSelf().inSingletonScope(); bind(RemoteContainerConnectionProvider).toService(DevContainerConnectionProvider); diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts index c0e1f19df91b6..ada4ecf25da6f 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts @@ -17,15 +17,15 @@ import { DevContainerConfiguration } from '../devcontainer-file'; import { ContainerCreationContribution } from '../docker-container-service'; import * as Docker from 'dockerode'; -import { ContainerOutputProvider } from '../../electron-browser/container-output-provider'; import { injectable } from '@theia/core/shared/inversify'; +import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; /** * this contribution changes the /etc/profile file so that it won't overwrite the PATH variable set by docker */ @injectable() export class ProfileFileModificationContribution implements ContainerCreationContribution { - async handlePostCreate(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider) { + async handlePostCreate(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider): Promise { const stream = await (await container.exec({ Cmd: ['sh', '-c', 'sed -i \'s|PATH="\\([^"]*\\)"|PATH=${PATH:-"\\1"}|g\' /etc/profile'], User: 'root', AttachStderr: true, AttachStdout: true From 8d68a700c0d2943cf8fec153768d4496d70ce015 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 22 May 2024 16:38:51 +0200 Subject: [PATCH 09/14] added displaying of default forwarded ports by the container Signed-off-by: Jonah Iden --- .../container-info-contribution.ts | 46 +++++++++++++++++++ .../dev-container-frontend-module.ts | 5 ++ .../remote-container-connection-provider.ts | 2 + .../remote-container-connection-provider.ts | 13 +++++- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 packages/dev-container/src/electron-browser/container-info-contribution.ts diff --git a/packages/dev-container/src/electron-browser/container-info-contribution.ts b/packages/dev-container/src/electron-browser/container-info-contribution.ts new file mode 100644 index 0000000000000..499fa1722f00b --- /dev/null +++ b/packages/dev-container/src/electron-browser/container-info-contribution.ts @@ -0,0 +1,46 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution } from '@theia/core/src/browser'; +import type { ContainerInspectInfo } from 'dockerode'; +import { RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; +import { PortForwardingService } from '@theia/remote/lib/electron-browser/port-forwarding/port-forwarding-service'; + +@injectable() +export class ContainerInfoContribution implements FrontendApplicationContribution { + + @inject(RemoteContainerConnectionProvider) + protected readonly connectionProvider: RemoteContainerConnectionProvider; + + @inject(PortForwardingService) + protected readonly portForwardingService: PortForwardingService; + + containerInfo: ContainerInspectInfo | undefined + + async onStart(): Promise { + this.containerInfo = await this.connectionProvider.getCurrentContainerInfo(parseInt(new URLSearchParams(location.search).get('port') ?? '0')); + + this.portForwardingService.forwardedPorts = Object.entries(this.containerInfo?.NetworkSettings.Ports ?? {}).flatMap(([_, ports]) => ( + ports.map(port => ({ + editing: false, + address: port.HostIp ?? '', + localPort: parseInt(port.HostPort ?? '0'), + origin: 'container' + })))); + } + +} diff --git a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts index 77cdd79844b72..807e5152cb2d9 100644 --- a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts +++ b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts @@ -19,6 +19,8 @@ import { RemoteContainerConnectionProvider, RemoteContainerConnectionProviderPat import { ContainerConnectionContribution } from './container-connection-contribution'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import { ContainerOutputProvider } from './container-output-provider'; +import { ContainerInfoContribution } from './container-info-contribution'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; export default new ContainerModule(bind => { bind(ContainerConnectionContribution).toSelf().inSingletonScope(); @@ -30,4 +32,7 @@ export default new ContainerModule(bind => { const outputProvider = ctx.container.get(ContainerOutputProvider); return ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteContainerConnectionProviderPath, outputProvider); }).inSingletonScope(); + + bind(ContainerInfoContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(ContainerInfoContribution); }); diff --git a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts index 236cb3113cd90..c6674f91371b9 100644 --- a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts @@ -15,6 +15,7 @@ import { RpcServer } from '@theia/core'; import { ContainerOutputProvider } from './container-output-provider'; +import type { ContainerInspectInfo } from 'dockerode'; // ***************************************************************************** export const RemoteContainerConnectionProviderPath = '/remote/container'; @@ -46,4 +47,5 @@ export interface DevContainerFile { export interface RemoteContainerConnectionProvider extends RpcServer { connectToContainer(options: ContainerConnectionOptions): Promise; getDevContainerFiles(): Promise; + getCurrentContainerInfo(port: number): Promise; } diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 5a89b295f1068..93cca3fd54208 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -56,6 +56,9 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection @inject(DevContainerFileService) protected readonly devContainerFileService: DevContainerFileService; + @inject(RemoteConnectionService) + protected readonly remoteService: RemoteConnectionService; + protected outputProvider: ContainerOutputProvider | undefined; setClient(client: ContainerOutputProvider): void { @@ -128,6 +131,14 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection })); } + async getCurrentContainerInfo(port: number): Promise { + const connection = this.remoteConnectionService.getConnectionFromPort(port); + if (!connection || !(connection instanceof RemoteDockerContainerConnection)) { + return undefined; + } + return await connection.container.inspect(); + } + dispose(): void { } @@ -167,8 +178,6 @@ export class RemoteDockerContainerConnection implements RemoteConnection { docker: Docker; container: Docker.Container; - containerInfo: Docker.ContainerInspectInfo | undefined; - remoteSetupResult: RemoteSetupResult; protected activeTerminalSession: ContainerTerminalSession | undefined; From 01d361d9aa318db121432b6a5c41bcbd86fa0674 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 22 May 2024 16:39:25 +0200 Subject: [PATCH 10/14] lint Signed-off-by: Jonah Iden --- .../profile-file-modification-contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts index ada4ecf25da6f..6cfa4033c0de5 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts @@ -21,7 +21,7 @@ import { injectable } from '@theia/core/shared/inversify'; import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; /** - * this contribution changes the /etc/profile file so that it won't overwrite the PATH variable set by docker + * this contribution changes the /etc/profile file so that it won't overwrite the PATH variable set by docker */ @injectable() export class ProfileFileModificationContribution implements ContainerCreationContribution { From 8e95d3e9ab963b6fe90df468c196569f54b78c6a Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 23 May 2024 08:41:13 +0200 Subject: [PATCH 11/14] more lint because my local eslint is not working Signed-off-by: Jonah Iden --- .../src/electron-browser/container-info-contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev-container/src/electron-browser/container-info-contribution.ts b/packages/dev-container/src/electron-browser/container-info-contribution.ts index 499fa1722f00b..06a39417f5a6b 100644 --- a/packages/dev-container/src/electron-browser/container-info-contribution.ts +++ b/packages/dev-container/src/electron-browser/container-info-contribution.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { inject, injectable } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution } from '@theia/core/src/browser'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; import type { ContainerInspectInfo } from 'dockerode'; import { RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; import { PortForwardingService } from '@theia/remote/lib/electron-browser/port-forwarding/port-forwarding-service'; @@ -29,7 +29,7 @@ export class ContainerInfoContribution implements FrontendApplicationContributio @inject(PortForwardingService) protected readonly portForwardingService: PortForwardingService; - containerInfo: ContainerInspectInfo | undefined + containerInfo: ContainerInspectInfo | undefined; async onStart(): Promise { this.containerInfo = await this.connectionProvider.getCurrentContainerInfo(parseInt(new URLSearchParams(location.search).get('port') ?? '0')); From 2b9adc1ed3ed555ff348cc638ff2cb0f488697e4 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 29 May 2024 10:16:20 +0200 Subject: [PATCH 12/14] dynamic forwarded ports from devcontainer.json Signed-off-by: Jonah Iden --- .../main-container-creation-contributions.ts | 31 +++++++++------- .../electron-node/docker-container-service.ts | 36 +++++++++---------- .../remote-container-connection-provider.ts | 8 ++++- .../port-forwarding-service.ts | 10 +++++- .../remote-port-forwarding-provider.ts | 1 + .../remote-port-forwarding-provider.ts | 28 +++++++++++---- 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index fd8521d9dd0cb..ad0d0d4d265de 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -14,12 +14,14 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import * as Docker from 'dockerode'; -import { injectable, interfaces } from '@theia/core/shared/inversify'; +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; import { ContainerCreationContribution } from '../docker-container-service'; import { DevContainerConfiguration, DockerfileContainer, ImageContainer, NonComposeContainerBase } from '../devcontainer-file'; import { Path } from '@theia/core'; import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; import * as fs from '@theia/core/shared/fs-extra'; +import { RemotePortForwardingProvider } from '@theia/remote/lib/electron-common/remote-port-forwarding-provider'; +import { RemoteDockerContainerConnection } from '../remote-container-connection-provider'; export function registerContainerCreationContributions(bind: interfaces.Bind): void { bind(ContainerCreationContribution).to(ImageFileContribution).inSingletonScope(); @@ -91,24 +93,27 @@ export class DockerFileContribution implements ContainerCreationContribution { @injectable() export class ForwardPortsContribution implements ContainerCreationContribution { - async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise { + + @inject(RemotePortForwardingProvider) + protected readonly portForwardingProvider: RemotePortForwardingProvider; + + async handlePostConnect(containerConfig: DevContainerConfiguration, connection: RemoteDockerContainerConnection): Promise { if (!containerConfig.forwardPorts) { return; } - for (const port of containerConfig.forwardPorts) { - let portKey: string; - let hostPort: string; - if (typeof port === 'string') { - const parts = port.split(':'); - portKey = isNaN(+parts[0]) ? parts[0] : `${parts[0]}/tcp`; - hostPort = parts[1] ?? parts[0]; + for (const forward of containerConfig.forwardPorts) { + let port: number; + let address: string | undefined; + if (typeof forward === 'string') { + const parts = forward.split(':'); + address = parts[0]; + port = parseInt(parts[1]); } else { - portKey = `${port}/tcp`; - hostPort = port.toString(); + port = forward; } - createOptions.ExposedPorts![portKey] = {}; - createOptions.HostConfig!.PortBindings[portKey] = [{ HostPort: hostPort }]; + + this.portForwardingProvider.forwardPort(connection.localPort, { port, address }); } } diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index 675c88695c53d..575984ec94c13 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -23,6 +23,7 @@ import { LastContainerInfo } from '../electron-common/remote-container-connectio import { DevContainerConfiguration } from './devcontainer-file'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; +import { RemoteDockerContainerConnection } from './remote-container-connection-provider'; export const ContainerCreationContribution = Symbol('ContainerCreationContributions'); @@ -39,6 +40,12 @@ export interface ContainerCreationContribution { container: Docker.Container, api: Docker, outputProvider?: ContainerOutputProvider): MaybePromise; + + /** + * executed after a connection has been established with the container and theia has been setup + */ + handlePostConnect?(containerConfig: DevContainerConfiguration, connection: RemoteDockerContainerConnection, + outputProvider?: ContainerOutputProvider): MaybePromise; } @injectable() @@ -78,6 +85,15 @@ export class DockerContainerService { return container; } + async postConnect(devcontainerFile: string, connection: RemoteDockerContainerConnection, outputProvider?: ContainerOutputProvider) { + const devcontainerConfig = await this.devContainerFileService.getConfiguration(devcontainerFile); + + for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { + await containerCreateContrib.handlePostConnect?.(devcontainerConfig, connection, outputProvider); + } + + } + protected async buildContainer(docker: Docker, devcontainerFile: string, workspace: URI, outputProvider?: ContainerOutputProvider): Promise { const devcontainerConfig = await this.devContainerFileService.getConfiguration(devcontainerFile); @@ -113,24 +129,4 @@ export class DockerContainerService { return container; } - - protected getPortBindings(forwardPorts: (string | number)[]): { exposedPorts: {}, portBindings: {} } { - const res: { exposedPorts: { [key: string]: {} }, portBindings: { [key: string]: {} } } = { exposedPorts: {}, portBindings: {} }; - for (const port of forwardPorts) { - let portKey: string; - let hostPort: string; - if (typeof port === 'string') { - const parts = port.split(':'); - portKey = isNaN(+parts[0]) ? parts[0] : `${parts[0]}/tcp`; - hostPort = parts[1] ?? parts[0]; - } else { - portKey = `${port}/tcp`; - hostPort = port.toString(); - } - res.exposedPorts[portKey] = {}; - res.portBindings[portKey] = [{ HostPort: hostPort }]; - } - - return res; - } } diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 93cca3fd54208..08b2b9ac1f2a9 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -103,6 +103,9 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection }); const localPort = (server.address() as net.AddressInfo).port; remote.localPort = localPort; + + await this.containerService.postConnect(options.devcontainerFile, remote, this.outputProvider); + return { containerId: container.id, workspacePath: (await container.inspect()).Mounts[0].Destination, @@ -189,10 +192,13 @@ export class RemoteDockerContainerConnection implements RemoteConnection { this.id = options.id; this.type = options.type; this.name = options.name; - this.onDidDisconnect(() => this.dispose()); this.docker = options.docker; this.container = options.container; + + this.docker.getEvents({ filters: { container: [this.container.id], event: ['stop'] } }).then(stream => { + stream.on('data', () => this.onDidDisconnectEmitter.fire()); + }); } async forwardOut(socket: Socket, port?: number): Promise { diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts index 109cfb098a30d..7a5a0c58bfe33 100644 --- a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { Emitter } from '@theia/core'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { RemotePortForwardingProvider } from '../../electron-common/remote-port-forwarding-provider'; export interface ForwardedPort { @@ -36,6 +36,14 @@ export class PortForwardingService { forwardedPorts: ForwardedPort[] = []; + @postConstruct() + init(): void { + this.provider.getForwardedPorts().then(ports => { + this.forwardedPorts = ports.map(p => ({ address: p.address, localPort: p.port, editing: false })); + this.onDidChangePortsEmitter.fire(); + }); + } + forwardNewPort(origin?: string): ForwardedPort { const index = this.forwardedPorts.push({ editing: true, origin }); return this.forwardedPorts[index - 1]; diff --git a/packages/remote/src/electron-common/remote-port-forwarding-provider.ts b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts index 6f01e01da2ccd..a79bd5b9df285 100644 --- a/packages/remote/src/electron-common/remote-port-forwarding-provider.ts +++ b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts @@ -26,4 +26,5 @@ export interface ForwardedPort { export interface RemotePortForwardingProvider { forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise; portRemoved(port: ForwardedPort): Promise; + getForwardedPorts(): Promise } diff --git a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts index 23c8e4ec9f579..322580c44551d 100644 --- a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts +++ b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts @@ -18,6 +18,13 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { ForwardedPort, RemotePortForwardingProvider } from '../electron-common/remote-port-forwarding-provider'; import { createServer, Server } from 'net'; import { RemoteConnectionService } from './remote-connection-service'; +import { RemoteConnection } from './remote-types'; + +interface ForwardInfo { + connection: RemoteConnection + port: ForwardedPort + server: Server +} @injectable() export class RemotePortForwardingProviderImpl implements RemotePortForwardingProvider { @@ -25,7 +32,7 @@ export class RemotePortForwardingProviderImpl implements RemotePortForwardingPro @inject(RemoteConnectionService) protected readonly connectionService: RemoteConnectionService; - protected forwardedPorts: Map = new Map(); + protected static forwardedPorts: ForwardInfo[] = []; async forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise { const currentConnection = this.connectionService.getConnectionFromPort(connectionPort); @@ -36,15 +43,24 @@ export class RemotePortForwardingProviderImpl implements RemotePortForwardingPro const server = createServer(socket => { currentConnection?.forwardOut(socket, portToForward.port); }).listen(portToForward.port, portToForward.address); - this.forwardedPorts.set(portToForward.port, server); + + currentConnection.onDidDisconnect(() => { + this.portRemoved(portToForward); + }); + + RemotePortForwardingProviderImpl.forwardedPorts.push({ connection: currentConnection, port: portToForward, server }); } async portRemoved(forwardedPort: ForwardedPort): Promise { - const proxy = this.forwardedPorts.get(forwardedPort.port); - if (proxy) { - proxy.close(); - this.forwardedPorts.delete(forwardedPort.port); + const forwardInfo = RemotePortForwardingProviderImpl.forwardedPorts.find(forwardInfo => forwardInfo.port.port === forwardedPort.port); + if (forwardInfo) { + forwardInfo.server.close(); + RemotePortForwardingProviderImpl.forwardedPorts.splice(RemotePortForwardingProviderImpl.forwardedPorts.indexOf(forwardInfo), 1); } } + async getForwardedPorts(): Promise { + return Array.from(RemotePortForwardingProviderImpl.forwardedPorts) + .map(forwardInfo => ({ ...forwardInfo.port, editing: false })); + } } From 738d3c081dfc591a27484eade9b8707cbe2c8e68 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 29 May 2024 11:58:42 +0200 Subject: [PATCH 13/14] lint Signed-off-by: Jonah Iden --- .../remote/src/electron-node/remote-port-forwarding-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts index 322580c44551d..6c5fbb9b68b48 100644 --- a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts +++ b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts @@ -52,7 +52,7 @@ export class RemotePortForwardingProviderImpl implements RemotePortForwardingPro } async portRemoved(forwardedPort: ForwardedPort): Promise { - const forwardInfo = RemotePortForwardingProviderImpl.forwardedPorts.find(forwardInfo => forwardInfo.port.port === forwardedPort.port); + const forwardInfo = RemotePortForwardingProviderImpl.forwardedPorts.find(info => info.port.port === forwardedPort.port); if (forwardInfo) { forwardInfo.server.close(); RemotePortForwardingProviderImpl.forwardedPorts.splice(RemotePortForwardingProviderImpl.forwardedPorts.indexOf(forwardInfo), 1); From a3088541d5a7b964b78dbac26dc3883953b38a23 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 29 May 2024 12:22:51 +0200 Subject: [PATCH 14/14] more lint Signed-off-by: Jonah Iden --- .../dev-container/src/electron-node/docker-container-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index 575984ec94c13..70dfbc2fa72a0 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -85,7 +85,7 @@ export class DockerContainerService { return container; } - async postConnect(devcontainerFile: string, connection: RemoteDockerContainerConnection, outputProvider?: ContainerOutputProvider) { + async postConnect(devcontainerFile: string, connection: RemoteDockerContainerConnection, outputProvider?: ContainerOutputProvider): Promise { const devcontainerConfig = await this.devContainerFileService.getConfiguration(devcontainerFile); for (const containerCreateContrib of this.containerCreationContributions.getContributions()) {