@theia/dev-container 1.71.0-next.8 → 1.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/electron-browser/container-connection-contribution.d.ts +8 -0
- package/lib/electron-browser/container-connection-contribution.d.ts.map +1 -1
- package/lib/electron-browser/container-connection-contribution.js +158 -6
- package/lib/electron-browser/container-connection-contribution.js.map +1 -1
- package/lib/electron-browser/dev-container-frontend-module.d.ts.map +1 -1
- package/lib/electron-browser/dev-container-frontend-module.js +3 -0
- package/lib/electron-browser/dev-container-frontend-module.js.map +1 -1
- package/lib/electron-browser/dev-container-suggestion-contribution.d.ts +16 -0
- package/lib/electron-browser/dev-container-suggestion-contribution.d.ts.map +1 -0
- package/lib/electron-browser/dev-container-suggestion-contribution.js +96 -0
- package/lib/electron-browser/dev-container-suggestion-contribution.js.map +1 -0
- package/lib/electron-common/remote-container-connection-provider.d.ts +9 -0
- package/lib/electron-common/remote-container-connection-provider.d.ts.map +1 -1
- package/lib/electron-node/dev-container-file-service.d.ts.map +1 -1
- package/lib/electron-node/dev-container-file-service.js +4 -6
- package/lib/electron-node/dev-container-file-service.js.map +1 -1
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.d.ts.map +1 -1
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.js +7 -1
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.js.map +1 -1
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.d.ts +2 -0
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.d.ts.map +1 -0
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.js +421 -0
- package/lib/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.js.map +1 -0
- package/lib/electron-node/devcontainer-contributions/main-container-creation-contributions.d.ts +28 -1
- package/lib/electron-node/devcontainer-contributions/main-container-creation-contributions.d.ts.map +1 -1
- package/lib/electron-node/devcontainer-contributions/main-container-creation-contributions.js +304 -4
- package/lib/electron-node/devcontainer-contributions/main-container-creation-contributions.js.map +1 -1
- package/lib/electron-node/devcontainer-contributions/variable-resolver-contribution.d.ts.map +1 -1
- package/lib/electron-node/devcontainer-contributions/variable-resolver-contribution.js +0 -1
- package/lib/electron-node/devcontainer-contributions/variable-resolver-contribution.js.map +1 -1
- package/lib/electron-node/devcontainer-file.d.ts +8 -1
- package/lib/electron-node/devcontainer-file.d.ts.map +1 -1
- package/lib/electron-node/devcontainer-file.js +14 -0
- package/lib/electron-node/devcontainer-file.js.map +1 -1
- package/lib/electron-node/remote-container-connection-provider.d.ts +6 -1
- package/lib/electron-node/remote-container-connection-provider.d.ts.map +1 -1
- package/lib/electron-node/remote-container-connection-provider.js +112 -4
- package/lib/electron-node/remote-container-connection-provider.js.map +1 -1
- package/lib/electron-node/remote-container-connection-provider.spec.d.ts +2 -0
- package/lib/electron-node/remote-container-connection-provider.spec.d.ts.map +1 -0
- package/lib/electron-node/remote-container-connection-provider.spec.js +131 -0
- package/lib/electron-node/remote-container-connection-provider.spec.js.map +1 -0
- package/package.json +10 -10
- package/src/electron-browser/container-connection-contribution.ts +173 -7
- package/src/electron-browser/dev-container-frontend-module.ts +4 -0
- package/src/electron-browser/dev-container-suggestion-contribution.ts +93 -0
- package/src/electron-common/remote-container-connection-provider.ts +10 -0
- package/src/electron-node/dev-container-file-service.ts +4 -6
- package/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.ts +519 -0
- package/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts +7 -1
- package/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +323 -5
- package/src/electron-node/devcontainer-contributions/variable-resolver-contribution.ts +0 -1
- package/src/electron-node/devcontainer-file.ts +13 -1
- package/src/electron-node/remote-container-connection-provider.spec.ts +152 -0
- package/src/electron-node/remote-container-connection-provider.ts +121 -5
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import * as net from 'net';
|
|
18
18
|
import {
|
|
19
19
|
ContainerConnectionOptions, ContainerConnectionResult,
|
|
20
|
-
DevContainerFile, RemoteContainerConnectionProvider
|
|
20
|
+
DevContainerFile, RemoteContainerConnectionProvider, RunningContainerInfo
|
|
21
21
|
} from '../electron-common/remote-container-connection-provider';
|
|
22
22
|
import { RemoteConnection, RemoteExecOptions, RemoteExecResult, RemoteExecTester, RemoteStatusReport } from '@theia/remote/lib/electron-node/remote-types';
|
|
23
23
|
import { RemoteSetupResult, RemoteSetupService } from '@theia/remote/lib/electron-node/setup/remote-setup-service';
|
|
@@ -146,7 +146,7 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection
|
|
|
146
146
|
|
|
147
147
|
return {
|
|
148
148
|
containerId: container.id,
|
|
149
|
-
workspacePath: devContainerConfig.workspaceFolder ?? (await container.inspect())
|
|
149
|
+
workspacePath: devContainerConfig.workspaceFolder ?? this.inferWorkspacePath(await container.inspect()),
|
|
150
150
|
port: localPort.toString(),
|
|
151
151
|
};
|
|
152
152
|
} catch (e) {
|
|
@@ -182,6 +182,103 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection
|
|
|
182
182
|
return connection.container.inspect();
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
async listRunningContainers(): Promise<RunningContainerInfo[]> {
|
|
186
|
+
try {
|
|
187
|
+
const docker = new Docker();
|
|
188
|
+
const containers = await docker.listContainers({ all: false });
|
|
189
|
+
return containers.map(container => ({
|
|
190
|
+
id: container.Id,
|
|
191
|
+
name: (container.Names[0] ?? '').replace(/^\//, ''),
|
|
192
|
+
image: container.Image,
|
|
193
|
+
status: container.Status
|
|
194
|
+
}));
|
|
195
|
+
} catch (e) {
|
|
196
|
+
console.error('Failed to list running containers:', e);
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async attachToContainer(containerId: string): Promise<ContainerConnectionResult> {
|
|
202
|
+
const docker = new Docker();
|
|
203
|
+
const container = docker.getContainer(containerId);
|
|
204
|
+
const containerInfo = await container.inspect();
|
|
205
|
+
|
|
206
|
+
const progress = await this.messageService.showProgress({
|
|
207
|
+
text: 'Attaching to container',
|
|
208
|
+
});
|
|
209
|
+
try {
|
|
210
|
+
const report: RemoteStatusReport = message => progress.report({ message });
|
|
211
|
+
report('Connecting to remote system...');
|
|
212
|
+
|
|
213
|
+
const remote = new RemoteDockerContainerConnection({
|
|
214
|
+
id: generateUuid(),
|
|
215
|
+
name: containerInfo.Name.replace(/^\//, ''),
|
|
216
|
+
type: 'Dev Container',
|
|
217
|
+
docker,
|
|
218
|
+
container,
|
|
219
|
+
config: DevContainerConfiguration.empty(),
|
|
220
|
+
logger: this.logger
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const result = await this.remoteSetup.setup({
|
|
224
|
+
connection: remote,
|
|
225
|
+
report,
|
|
226
|
+
});
|
|
227
|
+
remote.remoteSetupResult = result;
|
|
228
|
+
|
|
229
|
+
const registration = this.remoteConnectionService.register(remote);
|
|
230
|
+
const server = await this.serverProvider.getProxyServer(socket => {
|
|
231
|
+
remote.forwardOut(socket);
|
|
232
|
+
});
|
|
233
|
+
remote.onDidDisconnect(() => {
|
|
234
|
+
server.close();
|
|
235
|
+
registration.dispose();
|
|
236
|
+
});
|
|
237
|
+
const localPort = (server.address() as net.AddressInfo).port;
|
|
238
|
+
remote.localPort = localPort;
|
|
239
|
+
|
|
240
|
+
const workspacePath = this.inferWorkspacePath(containerInfo);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
containerId: container.id,
|
|
244
|
+
workspacePath,
|
|
245
|
+
port: localPort.toString(),
|
|
246
|
+
};
|
|
247
|
+
} catch (e) {
|
|
248
|
+
this.messageService.error(e.message);
|
|
249
|
+
console.error(e);
|
|
250
|
+
throw e;
|
|
251
|
+
} finally {
|
|
252
|
+
progress.cancel();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected inferWorkspacePath(containerInfo: Docker.ContainerInspectInfo): string {
|
|
257
|
+
// Skip mounts that are injected by HostConfigSharingContribution
|
|
258
|
+
// (SSH dir, gitconfig) — these are not workspace mounts.
|
|
259
|
+
const workspaceMount = containerInfo.Mounts.find(m =>
|
|
260
|
+
!m.Destination.endsWith('/.ssh') &&
|
|
261
|
+
!m.Destination.endsWith('/.gitconfig') &&
|
|
262
|
+
m.Destination !== '/tmp/host_gitconfig'
|
|
263
|
+
);
|
|
264
|
+
return (workspaceMount?.Destination ?? containerInfo.Config.WorkingDir) || '/';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async removeContainer(containerId: string): Promise<void> {
|
|
268
|
+
try {
|
|
269
|
+
const docker = new Docker();
|
|
270
|
+
const container = docker.getContainer(containerId);
|
|
271
|
+
const info = await container.inspect();
|
|
272
|
+
if (info.State.Running) {
|
|
273
|
+
await container.stop();
|
|
274
|
+
}
|
|
275
|
+
await container.remove();
|
|
276
|
+
} catch (e) {
|
|
277
|
+
console.error('Failed to remove container:', e);
|
|
278
|
+
throw e;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
185
282
|
dispose(): void {
|
|
186
283
|
|
|
187
284
|
}
|
|
@@ -251,12 +348,23 @@ export class RemoteDockerContainerConnection implements RemoteConnection {
|
|
|
251
348
|
this.logger = options.logger;
|
|
252
349
|
}
|
|
253
350
|
|
|
351
|
+
protected getRemoteEnv(): string[] | undefined {
|
|
352
|
+
const remoteEnv = this.config.remoteEnv;
|
|
353
|
+
if (!remoteEnv || Object.keys(remoteEnv).length === 0) {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
return Object.entries(remoteEnv)
|
|
357
|
+
.filter(([, value]) => value !== undefined)
|
|
358
|
+
.map(([key, value]) => `${key}=${value}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
254
361
|
async forwardOut(socket: Socket, port?: number): Promise<void> {
|
|
255
362
|
const node = `${this.remoteSetupResult.nodeDirectory}/bin/node`;
|
|
256
363
|
const devContainerServer = `${this.remoteSetupResult.applicationDirectory}/backend/dev-container-server.js`;
|
|
257
364
|
try {
|
|
258
365
|
const ttySession = await this.container.exec({
|
|
259
366
|
Cmd: ['sh', '-c', `${node} ${devContainerServer} -target-port=${port ?? this.remotePort}`],
|
|
367
|
+
Env: this.getRemoteEnv(),
|
|
260
368
|
AttachStdin: true, AttachStdout: true, AttachStderr: true
|
|
261
369
|
});
|
|
262
370
|
|
|
@@ -274,7 +382,9 @@ export class RemoteDockerContainerConnection implements RemoteConnection {
|
|
|
274
382
|
const deferred = new Deferred<RemoteExecResult>();
|
|
275
383
|
try {
|
|
276
384
|
// TODO add windows container support
|
|
277
|
-
const execution = await this.container.exec({
|
|
385
|
+
const execution = await this.container.exec({
|
|
386
|
+
Cmd: ['sh', '-c', `${cmd} ${args?.join(' ') ?? ''}`], Env: this.getRemoteEnv(), AttachStdout: true, AttachStderr: true
|
|
387
|
+
});
|
|
278
388
|
let stdoutBuffer = '';
|
|
279
389
|
let stderrBuffer = '';
|
|
280
390
|
const stream = await execution?.start({});
|
|
@@ -298,7 +408,9 @@ export class RemoteDockerContainerConnection implements RemoteConnection {
|
|
|
298
408
|
const deferred = new Deferred<RemoteExecResult>();
|
|
299
409
|
try {
|
|
300
410
|
// TODO add windows container support
|
|
301
|
-
const execution = await this.container.exec({
|
|
411
|
+
const execution = await this.container.exec({
|
|
412
|
+
Cmd: ['sh', '-c', `${cmd} ${args?.join(' ') ?? ''}`], Env: this.getRemoteEnv(), AttachStdout: true, AttachStderr: true
|
|
413
|
+
});
|
|
302
414
|
let stdoutBuffer = '';
|
|
303
415
|
let stderrBuffer = '';
|
|
304
416
|
const stream = await execution?.start({});
|
|
@@ -386,11 +498,15 @@ export class RemoteDockerContainerConnection implements RemoteConnection {
|
|
|
386
498
|
protected async shutdownContainer(sync: boolean): Promise<unknown> {
|
|
387
499
|
const remoteHost = this.getDockerHost();
|
|
388
500
|
|
|
389
|
-
const shutdownAction = this.config.shutdownAction ?? this.config.dockerComposeFile ? 'stopCompose' : 'stopContainer';
|
|
501
|
+
const shutdownAction = this.config.shutdownAction ?? (this.config.dockerComposeFile ? 'stopCompose' : 'stopContainer');
|
|
390
502
|
|
|
391
503
|
if (shutdownAction === 'stopContainer') {
|
|
392
504
|
return sync ? execSync(`docker ${remoteHost}stop ${this.container.id}`) : this.container.stop();
|
|
393
505
|
} else if (shutdownAction === 'stopCompose') {
|
|
506
|
+
if (!this.config.dockerComposeFile) {
|
|
507
|
+
console.warn('shutdownAction is stopCompose but dockerComposeFile is not defined, falling back to stopContainer');
|
|
508
|
+
return sync ? execSync(`docker ${remoteHost}stop ${this.container.id}`) : this.container.stop();
|
|
509
|
+
}
|
|
394
510
|
const composeFilePath = resolveComposeFilePath(this.config);
|
|
395
511
|
return sync ? execSync(`docker ${remoteHost}compose -f ${composeFilePath} stop`) :
|
|
396
512
|
new Promise<void>((res, rej) => exec(`docker ${remoteHost}compose -f ${composeFilePath} stop`, err => {
|