@theia/dev-container 1.71.0-next.8 → 1.72.0-next.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
package/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.spec.ts
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { SettingsContribution } from './cli-enhancing-creation-contributions';
|
|
19
|
+
import { RemoteCliContext } from '@theia/core/lib/node/remote/remote-cli-contribution';
|
|
20
|
+
import { OS } from '@theia/core/lib/common/os';
|
|
21
|
+
import * as Docker from 'dockerode';
|
|
22
|
+
|
|
23
|
+
describe('SettingsContribution', () => {
|
|
24
|
+
let settingsContribution: SettingsContribution;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
settingsContribution = new SettingsContribution();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('base64 encoding for complex values', () => {
|
|
31
|
+
|
|
32
|
+
it('should encode nested objects as base64', async () => {
|
|
33
|
+
const nestedSettings = {
|
|
34
|
+
'editor.codeActionsOnSave': {
|
|
35
|
+
'source.fixAll': true,
|
|
36
|
+
'source.organizeImports': true
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const containerConfig = {
|
|
41
|
+
image: 'test',
|
|
42
|
+
settings: nestedSettings
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
46
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
47
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
48
|
+
|
|
49
|
+
expect(args).to.have.lengthOf(1);
|
|
50
|
+
expect(args[0]).to.match(/^--set-preference=editor\.codeActionsOnSave=base64:/);
|
|
51
|
+
|
|
52
|
+
const base64Part = args[0].split('base64:')[1];
|
|
53
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
54
|
+
expect(decoded).to.deep.equal(nestedSettings['editor.codeActionsOnSave']);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should encode deeply nested objects as base64', async () => {
|
|
58
|
+
const deeplyNestedSettings = {
|
|
59
|
+
'complex.setting': {
|
|
60
|
+
level1: {
|
|
61
|
+
level2: {
|
|
62
|
+
level3: {
|
|
63
|
+
value: 'deep'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const containerConfig = {
|
|
71
|
+
image: 'test',
|
|
72
|
+
settings: deeplyNestedSettings
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
76
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
77
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
78
|
+
|
|
79
|
+
expect(args).to.have.lengthOf(1);
|
|
80
|
+
expect(args[0]).to.match(/^--set-preference=complex\.setting=base64:/);
|
|
81
|
+
|
|
82
|
+
const base64Part = args[0].split('base64:')[1];
|
|
83
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
84
|
+
expect(decoded).to.deep.equal(deeplyNestedSettings['complex.setting']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should encode arrays as base64', async () => {
|
|
88
|
+
const arraySettings = {
|
|
89
|
+
'files.exclude': ['**/.git', '**/.svn', '**/node_modules']
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const containerConfig = {
|
|
93
|
+
image: 'test',
|
|
94
|
+
settings: arraySettings
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
98
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
99
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
100
|
+
|
|
101
|
+
expect(args).to.have.lengthOf(1);
|
|
102
|
+
expect(args[0]).to.match(/^--set-preference=files\.exclude=base64:/);
|
|
103
|
+
|
|
104
|
+
const base64Part = args[0].split('base64:')[1];
|
|
105
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
106
|
+
expect(decoded).to.deep.equal(arraySettings['files.exclude']);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should encode arrays of objects as base64', async () => {
|
|
110
|
+
const complexArraySettings = {
|
|
111
|
+
'launch.configurations': [
|
|
112
|
+
{ type: 'node', request: 'launch', name: 'Launch Program' },
|
|
113
|
+
{ type: 'chrome', request: 'attach', name: 'Attach to Chrome' }
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const containerConfig = {
|
|
118
|
+
image: 'test',
|
|
119
|
+
settings: complexArraySettings
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
123
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
124
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
125
|
+
|
|
126
|
+
expect(args).to.have.lengthOf(1);
|
|
127
|
+
expect(args[0]).to.match(/^--set-preference=launch\.configurations=base64:/);
|
|
128
|
+
|
|
129
|
+
const base64Part = args[0].split('base64:')[1];
|
|
130
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
131
|
+
expect(decoded).to.deep.equal(complexArraySettings['launch.configurations']);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should encode empty objects as base64', async () => {
|
|
135
|
+
const emptyObjectSettings = {
|
|
136
|
+
'empty.object': {}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const containerConfig = {
|
|
140
|
+
image: 'test',
|
|
141
|
+
settings: emptyObjectSettings
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
145
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
146
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
147
|
+
|
|
148
|
+
expect(args).to.have.lengthOf(1);
|
|
149
|
+
expect(args[0]).to.match(/^--set-preference=empty\.object=base64:/);
|
|
150
|
+
|
|
151
|
+
const base64Part = args[0].split('base64:')[1];
|
|
152
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
153
|
+
expect(decoded).to.deep.equal({});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should encode empty arrays as base64', async () => {
|
|
157
|
+
const emptyArraySettings = {
|
|
158
|
+
'empty.array': []
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const containerConfig = {
|
|
162
|
+
image: 'test',
|
|
163
|
+
settings: emptyArraySettings
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
167
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
168
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
169
|
+
|
|
170
|
+
expect(args).to.have.lengthOf(1);
|
|
171
|
+
expect(args[0]).to.match(/^--set-preference=empty\.array=base64:/);
|
|
172
|
+
|
|
173
|
+
const base64Part = args[0].split('base64:')[1];
|
|
174
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
175
|
+
expect(decoded).to.deep.equal([]);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should handle values containing equals signs in objects', async () => {
|
|
179
|
+
const settingsWithEquals = {
|
|
180
|
+
'test.config': {
|
|
181
|
+
formula: 'a=b+c',
|
|
182
|
+
equation: 'x=y=z'
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const containerConfig = {
|
|
187
|
+
image: 'test',
|
|
188
|
+
settings: settingsWithEquals
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
192
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
193
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
194
|
+
|
|
195
|
+
expect(args).to.have.lengthOf(1);
|
|
196
|
+
expect(args[0]).to.match(/^--set-preference=test\.config=base64:/);
|
|
197
|
+
|
|
198
|
+
const base64Part = args[0].split('base64:')[1];
|
|
199
|
+
const decoded = JSON.parse(Buffer.from(base64Part, 'base64').toString('utf-8'));
|
|
200
|
+
expect(decoded).to.deep.equal(settingsWithEquals['test.config']);
|
|
201
|
+
expect(decoded.formula).to.equal('a=b+c');
|
|
202
|
+
expect(decoded.equation).to.equal('x=y=z');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('round-trip encode/decode validation', () => {
|
|
207
|
+
|
|
208
|
+
const simulateDecode = (encodedArg: string): unknown => {
|
|
209
|
+
const prefixRemoved = encodedArg.substring('--set-preference='.length);
|
|
210
|
+
const firstEqualIndex = prefixRemoved.indexOf('=');
|
|
211
|
+
let rawValue = prefixRemoved.substring(firstEqualIndex + 1);
|
|
212
|
+
if (rawValue.startsWith('base64:')) {
|
|
213
|
+
rawValue = Buffer.from(rawValue.substring('base64:'.length), 'base64').toString('utf-8');
|
|
214
|
+
}
|
|
215
|
+
return JSON.parse(rawValue);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
it('should successfully round-trip nested objects', async () => {
|
|
219
|
+
const originalValue = {
|
|
220
|
+
nested: {
|
|
221
|
+
deep: {
|
|
222
|
+
value: 'test',
|
|
223
|
+
number: 42
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const containerConfig = {
|
|
229
|
+
image: 'test',
|
|
230
|
+
settings: { 'test.setting': originalValue }
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
234
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
235
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
236
|
+
|
|
237
|
+
const decodedValue = simulateDecode(args[0]);
|
|
238
|
+
expect(decodedValue).to.deep.equal(originalValue);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should successfully round-trip arrays', async () => {
|
|
242
|
+
const originalValue = ['item1', 'item2', 'item3'];
|
|
243
|
+
|
|
244
|
+
const containerConfig = {
|
|
245
|
+
image: 'test',
|
|
246
|
+
settings: { 'test.array': originalValue }
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
250
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
251
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
252
|
+
|
|
253
|
+
const decodedValue = simulateDecode(args[0]);
|
|
254
|
+
expect(decodedValue).to.deep.equal(originalValue);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should successfully round-trip values with equals signs', async () => {
|
|
258
|
+
const originalValue = {
|
|
259
|
+
equation: 'a=b=c',
|
|
260
|
+
formula: 'x=y'
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const containerConfig = {
|
|
264
|
+
image: 'test',
|
|
265
|
+
settings: { 'test.equals': originalValue }
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
269
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
270
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
271
|
+
|
|
272
|
+
const decodedValue = simulateDecode(args[0]);
|
|
273
|
+
expect(decodedValue).to.deep.equal(originalValue);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('should successfully round-trip empty objects', async () => {
|
|
277
|
+
const originalValue = {};
|
|
278
|
+
|
|
279
|
+
const containerConfig = {
|
|
280
|
+
image: 'test',
|
|
281
|
+
settings: { 'test.empty': originalValue }
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
285
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
286
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
287
|
+
|
|
288
|
+
const decodedValue = simulateDecode(args[0]);
|
|
289
|
+
expect(decodedValue).to.deep.equal(originalValue);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should successfully round-trip mixed complex structures', async () => {
|
|
293
|
+
const originalValue = {
|
|
294
|
+
string: 'test',
|
|
295
|
+
number: 123,
|
|
296
|
+
boolean: true,
|
|
297
|
+
array: [1, 'two', { three: 3 }],
|
|
298
|
+
nested: {
|
|
299
|
+
deep: {
|
|
300
|
+
value: 'with=equals'
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const containerConfig = {
|
|
306
|
+
image: 'test',
|
|
307
|
+
settings: { 'test.complex': originalValue }
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
311
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
312
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
313
|
+
|
|
314
|
+
const decodedValue = simulateDecode(args[0]);
|
|
315
|
+
expect(decodedValue).to.deep.equal(originalValue);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('primitive values (not base64 encoded)', () => {
|
|
320
|
+
|
|
321
|
+
it('should not encode string values as base64', async () => {
|
|
322
|
+
const stringSettings = {
|
|
323
|
+
'editor.fontSize': '14'
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const containerConfig = {
|
|
327
|
+
image: 'test',
|
|
328
|
+
settings: stringSettings
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
332
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
333
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
334
|
+
|
|
335
|
+
expect(args).to.have.lengthOf(1);
|
|
336
|
+
expect(args[0]).to.equal('--set-preference=editor.fontSize="14"');
|
|
337
|
+
expect(args[0]).to.not.include('base64:');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should not encode number values as base64', async () => {
|
|
341
|
+
const numberSettings = {
|
|
342
|
+
'editor.tabSize': 4
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const containerConfig = {
|
|
346
|
+
image: 'test',
|
|
347
|
+
settings: numberSettings
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
351
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
352
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
353
|
+
|
|
354
|
+
expect(args).to.have.lengthOf(1);
|
|
355
|
+
expect(args[0]).to.equal('--set-preference=editor.tabSize=4');
|
|
356
|
+
expect(args[0]).to.not.include('base64:');
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should not encode boolean values as base64', async () => {
|
|
360
|
+
const booleanSettings = {
|
|
361
|
+
'editor.wordWrap': true
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const containerConfig = {
|
|
365
|
+
image: 'test',
|
|
366
|
+
settings: booleanSettings
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
370
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
371
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
372
|
+
|
|
373
|
+
expect(args).to.have.lengthOf(1);
|
|
374
|
+
expect(args[0]).to.equal('--set-preference=editor.wordWrap=true');
|
|
375
|
+
expect(args[0]).to.not.include('base64:');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should handle strings containing equals signs without base64', async () => {
|
|
379
|
+
const stringWithEquals = {
|
|
380
|
+
'test.value': 'a=b'
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const containerConfig = {
|
|
384
|
+
image: 'test',
|
|
385
|
+
settings: stringWithEquals
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
389
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
390
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
391
|
+
|
|
392
|
+
expect(args).to.have.lengthOf(1);
|
|
393
|
+
expect(args[0]).to.equal('--set-preference=test.value="a=b"');
|
|
394
|
+
expect(args[0]).to.not.include('base64:');
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe('vscode customizations', () => {
|
|
399
|
+
|
|
400
|
+
it('should merge settings from customizations.vscode.settings', async () => {
|
|
401
|
+
const containerConfig = {
|
|
402
|
+
image: 'test',
|
|
403
|
+
settings: {
|
|
404
|
+
'editor.fontSize': '14'
|
|
405
|
+
},
|
|
406
|
+
customizations: {
|
|
407
|
+
vscode: {
|
|
408
|
+
settings: {
|
|
409
|
+
'editor.tabSize': 4,
|
|
410
|
+
'files.exclude': ['**/.git']
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
417
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
418
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
419
|
+
|
|
420
|
+
expect(args).to.have.lengthOf(3);
|
|
421
|
+
expect(args.some(arg => arg.includes('editor.fontSize'))).to.be.true;
|
|
422
|
+
expect(args.some(arg => arg.includes('editor.tabSize'))).to.be.true;
|
|
423
|
+
expect(args.some(arg => arg.includes('files.exclude'))).to.be.true;
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should override settings with customizations.vscode.settings', async () => {
|
|
427
|
+
const containerConfig = {
|
|
428
|
+
image: 'test',
|
|
429
|
+
settings: {
|
|
430
|
+
'editor.fontSize': '14'
|
|
431
|
+
},
|
|
432
|
+
customizations: {
|
|
433
|
+
vscode: {
|
|
434
|
+
settings: {
|
|
435
|
+
'editor.fontSize': '16'
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
442
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
443
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
444
|
+
|
|
445
|
+
expect(args).to.have.lengthOf(1);
|
|
446
|
+
expect(args[0]).to.equal('--set-preference=editor.fontSize="16"');
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
describe('edge cases', () => {
|
|
451
|
+
|
|
452
|
+
it('should return empty array when no config is set', () => {
|
|
453
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
454
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
455
|
+
expect(args).to.be.empty;
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should handle config with no settings', async () => {
|
|
459
|
+
const containerConfig = {
|
|
460
|
+
image: 'test'
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
464
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
465
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
466
|
+
|
|
467
|
+
expect(args).to.be.empty;
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should clear config after enhanceArgs is called', async () => {
|
|
471
|
+
const containerConfig = {
|
|
472
|
+
image: 'test',
|
|
473
|
+
settings: { 'test.setting': 'value' }
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
477
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
478
|
+
const firstCall = settingsContribution.enhanceArgs(context);
|
|
479
|
+
const secondCall = settingsContribution.enhanceArgs(context);
|
|
480
|
+
|
|
481
|
+
expect(firstCall).to.have.lengthOf(1);
|
|
482
|
+
expect(secondCall).to.be.empty;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('should handle undefined values', async () => {
|
|
486
|
+
const containerConfig = {
|
|
487
|
+
image: 'test',
|
|
488
|
+
settings: {
|
|
489
|
+
'test.undefined': undefined
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
494
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
495
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
496
|
+
|
|
497
|
+
expect(args).to.have.lengthOf(1);
|
|
498
|
+
expect(args[0]).to.equal('--set-preference=test.undefined=undefined');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('should handle special characters in setting keys', async () => {
|
|
502
|
+
const containerConfig = {
|
|
503
|
+
image: 'test',
|
|
504
|
+
settings: {
|
|
505
|
+
'setting.with-dash': 'value',
|
|
506
|
+
'setting.with_underscore': 'value'
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
await settingsContribution.handleContainerCreation({} as Docker.ContainerCreateOptions, containerConfig);
|
|
511
|
+
const context: RemoteCliContext = { platform: { os: OS.Type.Linux, arch: 'x64' }, directory: '/workspace' };
|
|
512
|
+
const args = settingsContribution.enhanceArgs(context);
|
|
513
|
+
|
|
514
|
+
expect(args).to.have.lengthOf(2);
|
|
515
|
+
expect(args.some(arg => arg.includes('setting.with-dash'))).to.be.true;
|
|
516
|
+
expect(args.some(arg => arg.includes('setting.with_underscore'))).to.be.true;
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
});
|
package/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts
CHANGED
|
@@ -59,7 +59,13 @@ export class SettingsContribution implements RemoteCliContribution, ContainerCre
|
|
|
59
59
|
...(this.currentConfig.customizations?.vscode?.settings ?? [])
|
|
60
60
|
};
|
|
61
61
|
this.currentConfig = undefined;
|
|
62
|
-
return Object.entries(settings).map(([key, value]) =>
|
|
62
|
+
return Object.entries(settings).map(([key, value]) => {
|
|
63
|
+
const jsonValue = JSON.stringify(value);
|
|
64
|
+
if (value && typeof value === 'object') {
|
|
65
|
+
return `--set-preference=${key}=base64:${Buffer.from(jsonValue).toString('base64')}`;
|
|
66
|
+
}
|
|
67
|
+
return `--set-preference=${key}=${jsonValue}`;
|
|
68
|
+
});
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise<void> {
|