Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow prebuilding image without a Dockerfile #352

Merged
merged 6 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/spec-node/containerFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, get
import { isEarlierVersion, parseVersion } from '../spec-common/commonUtils';
import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata';
import { supportsBuildContexts } from './dockerfileUtils';
import { ContainerError } from '../spec-common/errors';

// Escapes environment variable keys.
//
Expand Down Expand Up @@ -53,15 +54,35 @@ export async function extendImage(params: DockerResolverParameters, config: Subs
const updatedImageName = `${imageName.startsWith(folderImageName) ? imageName : folderImageName}-features`;

const args: string[] = [];
if (!params.buildKitVersion &&
(params.buildxPlatform || params.buildxPush)) {
throw new ContainerError({ description: '--platform or --push require BuildKit enabled.', data: { fileWithError: dockerfilePath } });
}
if (params.buildKitVersion) {
args.push(
'buildx', 'build',
'--load', // (short for --output=docker, i.e. load into normal 'docker images' collection)
);
args.push('buildx', 'build');

// --platform
if (params.buildxPlatform) {
output.write('Setting BuildKit platform(s): ' + params.buildxPlatform, LogLevel.Trace);
args.push('--platform', params.buildxPlatform);
}

// --push/--output
if (params.buildxPush) {
args.push('--push');
} else {
if (params.buildxOutput) {
args.push('--output', params.buildxOutput);
} else {
args.push('--load'); // (short for --output=docker, i.e. load into normal 'docker images' collection)
}
}

for (const buildContext in featureBuildInfo.buildKitContexts) {
args.push('--build-context', `${buildContext}=${featureBuildInfo.buildKitContexts[buildContext]}`);
}
} else {
// Not using buildx
args.push(
'build',
);
Expand Down
6 changes: 0 additions & 6 deletions src/spec-node/devContainersSpecCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,6 @@ async function doBuild({
await inspectDockerImage(params, config.image, true);
const { updatedImageName } = await extendImage(params, configWithRaw, config.image, additionalFeatures, false);

if (buildxPlatform || buildxPush) {
throw new ContainerError({ description: '--platform or --push require dockerfilePath.' });
}
if (buildxOutput) {
throw new ContainerError({ description: '--output requires dockerfilePath.' });
}
if (imageNames) {
await Promise.all(imageNames.map(imageName => dockerPtyCLI(params, 'tag', updatedImageName[0], imageName)));
imageNameResult = imageNames;
Expand Down
2 changes: 1 addition & 1 deletion src/spec-node/dockerfileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface Stage {
}

export interface From {
platfrom?: string;
platform?: string;
image: string;
label?: string;
}
Expand Down
1 change: 1 addition & 0 deletions src/spec-node/singleContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ async function buildAndExtendImage(buildParams: DockerResolverParameters, config
if (buildParams.buildKitVersion) {
args.push('buildx', 'build');
if (buildParams.buildxPlatform) {
output.write('Setting BuildKit platform(s): ' + buildParams.buildxPlatform, LogLevel.Trace);
args.push('--platform', buildParams.buildxPlatform);
}
if (buildParams.buildxPush) {
Expand Down
9 changes: 8 additions & 1 deletion src/test/cli.build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,20 @@ describe('Dev Containers CLI', function () {
assert.equal(success, false, 'expect non-successful call');
});

it('should succeed with supported --platform', async () => {
it('should succeed (dockerfile) with supported --platform', async () => {
const testFolder = `${__dirname}/configs/dockerfile-with-target`;
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --platform linux/amd64`);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
});

it('should succeed (image) with supported --platform', async () => {
const testFolder = `${__dirname}/configs/image-with-features`;
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --platform linux/amd64`);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
});

it('should fail --platform without dockerfile', async () => {
let success = false;
const testFolder = `${__dirname}/configs/image`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
}
},
"features": {
"docker-in-docker": "latest",
"codspace/myfeatures/helloworld": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/feature-starter/hello:1": {
"greeting": "howdy"
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/test/configs/image-with-features/.devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:0-16-bullseye",
"image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:0-16",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
"codspace/myfeatures/helloworld": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/feature-starter/hello:1": {
"greeting": "howdy"
}
}
Expand Down