|
6 | 6 | import { ContainerError } from '../spec-common/errors'; |
7 | 7 | import { PlatformInfo } from '../spec-common/commonUtils'; |
8 | 8 | import { LifecycleCommand, LifecycleHooksInstallMap } from '../spec-common/injectHeadless'; |
9 | | -import { DevContainerConfig, DevContainerConfigCommand, DevContainerFromDockerComposeConfig, DevContainerFromDockerfileConfig, DevContainerFromImageConfig, getDockerComposeFilePaths, getDockerfilePath, HostGPURequirements, HostRequirements, isDockerFileConfig, PortAttributes, UserEnvProbe } from '../spec-configuration/configuration'; |
| 9 | +import { DevContainerConfig, DevContainerConfigCommand, DevContainerExtendsMergeMode, DevContainerFromDockerComposeConfig, DevContainerFromDockerfileConfig, DevContainerFromImageConfig, getDockerComposeFilePaths, getDockerfilePath, HostGPURequirements, HostRequirements, isDockerFileConfig, PortAttributes, UserEnvProbe } from '../spec-configuration/configuration'; |
10 | 10 | import { Feature, FeaturesConfig, Mount, parseMount, SchemaFeatureLifecycleHooks } from '../spec-configuration/containerFeaturesConfiguration'; |
11 | 11 | import { ContainerDetails, DockerCLIParameters, ImageDetails } from '../spec-shutdown/dockerUtils'; |
12 | 12 | import { Log, LogLevel } from '../spec-utils/log'; |
@@ -204,13 +204,18 @@ export function mergeConfiguration(config: DevContainerConfig, imageMetadata: Im |
204 | 204 | * (https://containers.dev/implementors/spec/#merge-logic) so `extends` behaves the same as |
205 | 205 | * combining a prebuilt image's metadata with a project's config. |
206 | 206 | */ |
207 | | -export function mergeDevContainerConfigs(base: DevContainerConfig, overlay: DevContainerConfig): DevContainerConfig { |
| 207 | +export function mergeDevContainerConfigs(base: DevContainerConfig, overlay: DevContainerConfig, extendsMergeMode: DevContainerExtendsMergeMode = 'combine'): DevContainerConfig { |
| 208 | + if (extendsMergeMode === 'override') { |
| 209 | + return mergeDevContainerConfigsOverride(base, overlay); |
| 210 | + } |
| 211 | + |
208 | 212 | const metadata: ImageMetadataEntry[] = [base, overlay]; |
209 | 213 | const merged = { |
210 | 214 | ...base, |
211 | 215 | ...overlay, |
212 | 216 | } as DevContainerConfig; |
213 | 217 | delete merged.extends; |
| 218 | + delete merged.extendsMergeMode; |
214 | 219 |
|
215 | 220 | if (base.init || overlay.init) { |
216 | 221 | merged.init = true; |
@@ -266,6 +271,34 @@ export function mergeDevContainerConfigs(base: DevContainerConfig, overlay: DevC |
266 | 271 | return merged; |
267 | 272 | } |
268 | 273 |
|
| 274 | +/** |
| 275 | + * Overlay-style merge: scalars and arrays from the overlay replace the base when set; |
| 276 | + * object maps and `hostRequirements` are shallow-merged with overlay keys winning. |
| 277 | + */ |
| 278 | +function mergeDevContainerConfigsOverride(base: DevContainerConfig, overlay: DevContainerConfig): DevContainerConfig { |
| 279 | + const merged = { |
| 280 | + ...base, |
| 281 | + ...overlay, |
| 282 | + } as DevContainerConfig; |
| 283 | + delete merged.extends; |
| 284 | + delete merged.extendsMergeMode; |
| 285 | + |
| 286 | + const remoteEnv = Object.assign({}, base.remoteEnv, overlay.remoteEnv); |
| 287 | + assignOrDelete(merged, 'remoteEnv', Object.keys(remoteEnv).length ? remoteEnv : undefined); |
| 288 | + const containerEnv = Object.assign({}, base.containerEnv, overlay.containerEnv); |
| 289 | + assignOrDelete(merged, 'containerEnv', Object.keys(containerEnv).length ? containerEnv : undefined); |
| 290 | + const portsAttributes = Object.assign({}, base.portsAttributes, overlay.portsAttributes); |
| 291 | + assignOrDelete(merged, 'portsAttributes', Object.keys(portsAttributes).length ? portsAttributes : undefined); |
| 292 | + const features = Object.assign({}, base.features, overlay.features); |
| 293 | + assignOrDelete(merged, 'features', Object.keys(features).length ? features : undefined); |
| 294 | + const customizations = Object.assign({}, base.customizations, overlay.customizations); |
| 295 | + assignOrDelete(merged, 'customizations', Object.keys(customizations).length ? customizations : undefined); |
| 296 | + const hostRequirements = Object.assign({}, base.hostRequirements, overlay.hostRequirements); |
| 297 | + assignOrDelete(merged, 'hostRequirements', Object.keys(hostRequirements).length ? hostRequirements : undefined); |
| 298 | + |
| 299 | + return merged; |
| 300 | +} |
| 301 | + |
269 | 302 | function assignOrDelete<K extends keyof DevContainerConfig>(target: DevContainerConfig, key: K, value: DevContainerConfig[K] | undefined) { |
270 | 303 | if (value !== undefined) { |
271 | 304 | target[key] = value; |
|
0 commit comments