(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, imageName: string, additionalImageNames: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>, canAddLabelsToContainer: boolean)
| 29 | .toUpperCase(); |
| 30 | |
| 31 | export async function extendImage(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, imageName: string, additionalImageNames: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>, canAddLabelsToContainer: boolean) { |
| 32 | const { common } = params; |
| 33 | const { cliHost, output } = common; |
| 34 | |
| 35 | const imageBuildInfo = await getImageBuildInfoFromImage(params, imageName, config.substitute); |
| 36 | const extendImageDetails = await getExtendImageBuildInfo(params, config, imageName, imageBuildInfo, undefined, additionalFeatures, canAddLabelsToContainer); |
| 37 | if (!extendImageDetails?.featureBuildInfo) { |
| 38 | // no feature extensions - return |
| 39 | if (additionalImageNames.length) { |
| 40 | if (params.isTTY) { |
| 41 | await Promise.all(additionalImageNames.map(name => dockerPtyCLI(params, 'tag', imageName, name))); |
| 42 | } else { |
| 43 | await Promise.all(additionalImageNames.map(name => dockerCLI(params, 'tag', imageName, name))); |
| 44 | } |
| 45 | } |
| 46 | return { |
| 47 | updatedImageName: [imageName], |
| 48 | imageMetadata: getDevcontainerMetadata(imageBuildInfo.metadata, config, extendImageDetails?.featuresConfig), |
| 49 | imageDetails: async () => imageBuildInfo.imageDetails, |
| 50 | labels: extendImageDetails?.labels, |
| 51 | }; |
| 52 | } |
| 53 | const { featureBuildInfo, featuresConfig } = extendImageDetails; |
| 54 | |
| 55 | // Got feature extensions -> build the image |
| 56 | const dockerfilePath = cliHost.path.join(featureBuildInfo.dstFolder, 'Dockerfile.extended'); |
| 57 | await cliHost.writeFile(dockerfilePath, Buffer.from(featureBuildInfo.dockerfilePrefixContent + featureBuildInfo.dockerfileContent)); |
| 58 | const folderImageName = getFolderImageName(common); |
| 59 | const updatedImageName = `${imageName.startsWith(folderImageName) ? imageName : folderImageName}-features`; |
| 60 | |
| 61 | const args: string[] = []; |
| 62 | if (!params.buildKitVersion && |
| 63 | (params.buildxPlatform || params.buildxPush)) { |
| 64 | throw new ContainerError({ description: '--platform or --push require BuildKit enabled.', data: { fileWithError: dockerfilePath } }); |
| 65 | } |
| 66 | if (params.buildKitVersion) { |
| 67 | args.push('buildx', 'build'); |
| 68 | |
| 69 | // --platform |
| 70 | if (params.buildxPlatform) { |
| 71 | output.write('Setting BuildKit platform(s): ' + params.buildxPlatform, LogLevel.Trace); |
| 72 | args.push('--platform', params.buildxPlatform); |
| 73 | } |
| 74 | |
| 75 | // --push/--output |
| 76 | if (params.buildxPush) { |
| 77 | args.push('--push'); |
| 78 | } else { |
| 79 | if (params.buildxOutput) { |
| 80 | args.push('--output', params.buildxOutput); |
| 81 | } else { |
| 82 | args.push('--load'); // (short for --output=docker, i.e. load into normal 'docker images' collection) |
| 83 | } |
| 84 | } |
| 85 | if (params.buildxCacheTo) { |
| 86 | args.push('--cache-to', params.buildxCacheTo); |
| 87 | } |
| 88 | if (!isBuildxCacheToInline(params.buildxCacheTo)) { |
no test coverage detected