Skip to content

Commit

Permalink
Add tags with build (devcontainers/ci#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Mar 1, 2024
1 parent 8885d5a commit 6784267
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 11 deletions.
10 changes: 9 additions & 1 deletion src/spec-node/containerFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,21 @@ export const getSafeId = (str: string) => str
.replace(/^[\d_]+/g, '_')
.toUpperCase();

export async function extendImage(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, imageName: string, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>, canAddLabelsToContainer: boolean) {
export async function extendImage(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, imageName: string, additionalImageNames: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>, canAddLabelsToContainer: boolean) {
const { common } = params;
const { cliHost, output } = common;

const imageBuildInfo = await getImageBuildInfoFromImage(params, imageName, config.substitute);
const extendImageDetails = await getExtendImageBuildInfo(params, config, imageName, imageBuildInfo, undefined, additionalFeatures, canAddLabelsToContainer);
if (!extendImageDetails?.featureBuildInfo) {
// no feature extensions - return
if (additionalImageNames.length) {
if (params.isTTY) {
await Promise.all(additionalImageNames.map(name => dockerPtyCLI(params, 'tag', imageName, name)));
} else {
await Promise.all(additionalImageNames.map(name => dockerCLI(params, 'tag', imageName, name)));
}
}
return {
updatedImageName: [imageName],
imageMetadata: getDevcontainerMetadata(imageBuildInfo.metadata, config, extendImageDetails?.featuresConfig),
Expand Down Expand Up @@ -99,6 +106,7 @@ export async function extendImage(params: DockerResolverParameters, config: Subs
args.push(
'--target', featureBuildInfo.overrideTarget,
'-t', updatedImageName,
...additionalImageNames.map(name => ['-t', name]).flat(),
'-f', dockerfilePath,
emptyTempDir
);
Expand Down
19 changes: 10 additions & 9 deletions src/spec-node/devContainersSpecCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ContainerError } from '../spec-common/errors';
import { Log, LogDimensions, LogLevel, makeLog, mapLogLevel } from '../spec-utils/log';
import { probeRemoteEnv, runLifecycleHooks, runRemoteCommand, UserEnvProbe, setupInContainer } from '../spec-common/injectHeadless';
import { extendImage } from './containerFeatures';
import { DockerCLIParameters, dockerPtyCLI, inspectContainer } from '../spec-shutdown/dockerUtils';
import { dockerCLI, DockerCLIParameters, dockerPtyCLI, inspectContainer } from '../spec-shutdown/dockerUtils';
import { buildAndExtendDockerCompose, dockerComposeCLIConfig, getDefaultImageName, getProjectName, readDockerComposeConfig, readVersionPrefix } from './dockerCompose';
import { DevContainerFromDockerComposeConfig, DevContainerFromDockerfileConfig, getDockerComposeFilePaths } from '../spec-configuration/configuration';
import { workspaceFromPath } from '../spec-utils/workspaces';
Expand Down Expand Up @@ -583,7 +583,7 @@ async function doBuild({
omitSyntaxDirective,
}, disposables);

const { common, dockerCLI, dockerComposeCLI } = params;
const { common, dockerComposeCLI } = params;
const { cliHost, env, output } = common;
const workspace = workspaceFromPath(cliHost.path, workspaceFolder);
const configPath = configFile ? configFile : workspace
Expand All @@ -602,7 +602,7 @@ async function doBuild({
throw new ContainerError({ description: '--push true cannot be used with --output.' });
}

const buildParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env, output, platformInfo: params.platformInfo };
const buildParams: DockerCLIParameters = { cliHost, dockerCLI: params.dockerCLI, dockerComposeCLI, env, output, platformInfo: params.platformInfo };
await ensureNoDisallowedFeatures(buildParams, config, additionalFeatures, undefined);

// Support multiple use of `--image-name`
Expand All @@ -614,9 +614,6 @@ async function doBuild({
let { updatedImageName } = await buildNamedImageAndExtend(params, configWithRaw as SubstitutedConfig<DevContainerFromDockerfileConfig>, additionalFeatures, false, imageNames);

if (imageNames) {
if (!buildxPush && !buildxOutput) {
await Promise.all(imageNames.map(imageName => dockerPtyCLI(params, 'tag', updatedImageName[0], imageName)));
}
imageNameResult = imageNames;
} else {
imageNameResult = updatedImageName;
Expand Down Expand Up @@ -660,7 +657,12 @@ async function doBuild({
const originalImageName = overrideImageName || service.image || getDefaultImageName(await buildParams.dockerComposeCLI(), projectName, config.service);

if (imageNames) {
await Promise.all(imageNames.map(imageName => dockerPtyCLI(params, 'tag', originalImageName, imageName)));
// Future improvement: Compose 2.6.0 (released 2022-05-30) added `tags` to the compose file.
if (params.isTTY) {
await Promise.all(imageNames.map(imageName => dockerPtyCLI(params, 'tag', originalImageName, imageName)));
} else {
await Promise.all(imageNames.map(imageName => dockerCLI(params, 'tag', originalImageName, imageName)));
}
imageNameResult = imageNames;
} else {
imageNameResult = originalImageName;
Expand All @@ -672,10 +674,9 @@ async function doBuild({
}

await inspectDockerImage(params, config.image, true);
const { updatedImageName } = await extendImage(params, configWithRaw, config.image, additionalFeatures, false);
const { updatedImageName } = await extendImage(params, configWithRaw, config.image, imageNames || [], additionalFeatures, false);

if (imageNames) {
await Promise.all(imageNames.map(imageName => dockerPtyCLI(params, 'tag', updatedImageName[0], imageName)));
imageNameResult = imageNames;
} else {
imageNameResult = updatedImageName;
Expand Down
2 changes: 1 addition & 1 deletion src/spec-node/singleContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export async function buildNamedImageAndExtend(params: DockerResolverParameters,
return await buildAndExtendImage(params, configWithRaw as SubstitutedConfig<DevContainerFromDockerfileConfig>, imageNames, params.buildNoCache ?? false, additionalFeatures);
}
// image-based dev container - extend
return await extendImage(params, configWithRaw, imageNames[0], additionalFeatures, canAddLabelsToContainer);
return await extendImage(params, configWithRaw, imageNames[0], argImageNames || [], additionalFeatures, canAddLabelsToContainer);
}

async function buildAndExtendImage(buildParams: DockerResolverParameters, configWithRaw: SubstitutedConfig<DevContainerFromDockerfileConfig>, baseImageNames: string[], noCache: boolean, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>) {
Expand Down
6 changes: 6 additions & 0 deletions src/test/cli.build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,33 +153,39 @@ describe('Dev Containers CLI', function () {
const testFolder = `${__dirname}/configs/dockerfile-with-features`;
const image1 = 'image-1';
const image2 = 'image-2';
await shellExec(`docker rmi -f ${image1} ${image2}`);
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --image-name ${image1} --image-name ${image2}`);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
assert.equal(response.imageName[0], image1);
assert.equal(response.imageName[1], image2);
await shellExec(`docker inspect --type image ${image1} ${image2}`);
});

it('should succeed with multiple --image-name parameters when dockerComposeFile is present', async () => {
const testFolder = `${__dirname}/configs/compose-Dockerfile-alpine`;
const image1 = 'image-1';
const image2 = 'image-2';
await shellExec(`docker rmi -f ${image1} ${image2}`);
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --image-name ${image1} --image-name ${image2}`);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
assert.equal(response.imageName[0], image1);
assert.equal(response.imageName[1], image2);
await shellExec(`docker inspect --type image ${image1} ${image2}`);
});

it('should succeed with multiple --image-name parameters when image is present', async () => {
const testFolder = `${__dirname}/configs/image`;
const image1 = 'image-1';
const image2 = 'image-2';
await shellExec(`docker rmi -f ${image1} ${image2}`);
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --image-name ${image1} --image-name ${image2}`);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
assert.equal(response.imageName[0], image1);
assert.equal(response.imageName[1], image2);
await shellExec(`docker inspect --type image ${image1} ${image2}`);
});

it('should fail with --push true and --output', async () => {
Expand Down

0 comments on commit 6784267

Please sign in to comment.