( updatedImageName: string, originalImageName: string, mergedConfig: MergedDevContainerConfig, config: DevContainerFromDockerComposeConfig, versionPrefix: string, imageDetails: () => Promise<ImageDetails>, service: any, additionalLabels: string[], additionalMounts: Mount[], params: DockerResolverParameters, )
| 502 | } |
| 503 | |
| 504 | async function generateFeaturesComposeOverrideContent( |
| 505 | updatedImageName: string, |
| 506 | originalImageName: string, |
| 507 | mergedConfig: MergedDevContainerConfig, |
| 508 | config: DevContainerFromDockerComposeConfig, |
| 509 | versionPrefix: string, |
| 510 | imageDetails: () => Promise<ImageDetails>, |
| 511 | service: any, |
| 512 | additionalLabels: string[], |
| 513 | additionalMounts: Mount[], |
| 514 | params: DockerResolverParameters, |
| 515 | ) { |
| 516 | const overrideImage = updatedImageName !== originalImageName; |
| 517 | |
| 518 | const user = mergedConfig.containerUser; |
| 519 | const env = mergedConfig.containerEnv || {}; |
| 520 | const capAdd = mergedConfig.capAdd || []; |
| 521 | const securityOpts = mergedConfig.securityOpt || []; |
| 522 | const mounts = [ |
| 523 | ...mergedConfig.mounts || [], |
| 524 | ...additionalMounts, |
| 525 | ].map(m => typeof m === 'string' ? parseMount(m) : m); |
| 526 | const namedVolumeMounts = mounts.filter(m => m.type === 'volume' && m.source); |
| 527 | const customEntrypoints = mergedConfig.entrypoints || []; |
| 528 | const composeEntrypoint: string[] | undefined = typeof service.entrypoint === 'string' ? shellQuote.parse(service.entrypoint) : service.entrypoint; |
| 529 | const composeCommand: string[] | undefined = typeof service.command === 'string' ? shellQuote.parse(service.command) : service.command; |
| 530 | const { overrideCommand } = mergedConfig; |
| 531 | const userEntrypoint = overrideCommand ? [] : composeEntrypoint /* $ already escaped. */ |
| 532 | || ((await imageDetails()).Config.Entrypoint || []).map(c => c.replace(/\$/g, '$$$$')); // $ > $$ to escape docker-compose.yml's interpolation. |
| 533 | const userCommand = overrideCommand ? [] : composeCommand /* $ already escaped. */ |
| 534 | || (composeEntrypoint ? [/* Ignore image CMD per docker-compose.yml spec. */] : ((await imageDetails()).Config.Cmd || []).map(c => c.replace(/\$/g, '$$$$'))); // $ > $$ to escape docker-compose.yml's interpolation. |
| 535 | |
| 536 | const hasGpuRequirement = config.hostRequirements?.gpu; |
| 537 | const addGpuCapability = hasGpuRequirement && await checkDockerSupportForGPU(params); |
| 538 | if (hasGpuRequirement && hasGpuRequirement !== 'optional' && !addGpuCapability) { |
| 539 | params.common.output.write('No GPU support found yet a GPU was required - consider marking it as "optional"', LogLevel.Warning); |
| 540 | } |
| 541 | const gpuResources = addGpuCapability ? ` |
| 542 | deploy: |
| 543 | resources: |
| 544 | reservations: |
| 545 | devices: |
| 546 | - capabilities: [gpu]` : ''; |
| 547 | |
| 548 | return `${versionPrefix}services: |
| 549 | '${config.service}':${overrideImage ? ` |
| 550 | image: ${updatedImageName}` : ''} |
| 551 | entrypoint: ["/bin/sh", "-c", "echo Container started\\n |
| 552 | trap \\"exit 0\\" 15\\n |
| 553 | ${customEntrypoints.join('\\n\n')}\\n |
| 554 | exec \\"$$@\\"\\n |
| 555 | while sleep 1 & wait $$!; do :; done", "-"${userEntrypoint.map(a => `, ${JSON.stringify(a)}`).join('')}]${userCommand !== composeCommand ? ` |
| 556 | command: ${JSON.stringify(userCommand)}` : ''}${mergedConfig.init ? ` |
| 557 | init: true` : ''}${user ? ` |
| 558 | user: ${user}` : ''}${Object.keys(env).length ? ` |
| 559 | environment:${Object.keys(env).map(key => ` |
| 560 | - '${key}=${String(env[key]).replace(/\n/g, '\\n').replace(/\$/g, '$$$$').replace(/'/g, '\'\'')}'`).join('')}` : ''}${mergedConfig.privileged ? ` |
| 561 | privileged: true` : ''}${capAdd.length ? ` |
no test coverage detected