Skip to content

Commit

Permalink
Merge pull request #570 from yokonao/add-buildx-cache-to
Browse files Browse the repository at this point in the history
Add `--cache-to` option to `devcontainer build` command
  • Loading branch information
samruddhikhandale authored Jul 31, 2023
2 parents ac349ef + 4cb96b7 commit 413c5f7
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/spec-common/injectHeadless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface ResolverParameters {
buildxPlatform: string | undefined;
buildxPush: boolean;
buildxOutput: string | undefined;
buildxCacheTo: string | undefined;
skipFeatureAutoMapping: boolean;
skipPostAttach: boolean;
containerSessionDataFolder?: string;
Expand Down
5 changes: 4 additions & 1 deletion src/spec-node/containerFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ export async function extendImage(params: DockerResolverParameters, config: Subs
args.push('--load'); // (short for --output=docker, i.e. load into normal 'docker images' collection)
}
}
if (params.buildxCacheTo) {
args.push('--cache-to', params.buildxCacheTo);
}

for (const buildContext in featureBuildInfo.buildKitContexts) {
args.push('--build-context', `${buildContext}=${featureBuildInfo.buildKitContexts[buildContext]}`);
Expand Down Expand Up @@ -472,4 +475,4 @@ export async function updateRemoteUserUID(params: DockerResolverParameters, merg
await dockerCLI(params, ...args);
}
return fixedImageName;
}
}
5 changes: 4 additions & 1 deletion src/spec-node/devContainers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface ProvisionOptions {
buildxPlatform: string | undefined;
buildxPush: boolean;
buildxOutput: string | undefined;
buildxCacheTo: string | undefined;
additionalFeatures?: Record<string, string | boolean | Record<string, string | boolean>>;
skipFeatureAutoMapping: boolean;
skipPostAttach: boolean;
Expand Down Expand Up @@ -140,6 +141,7 @@ export async function createDockerParams(options: ProvisionOptions, disposables:
buildxPlatform: options.buildxPlatform,
buildxPush: options.buildxPush,
buildxOutput: options.buildxOutput,
buildxCacheTo: options.buildxCacheTo,
skipFeatureAutoMapping: options.skipFeatureAutoMapping,
skipPostAttach: options.skipPostAttach,
containerSessionDataFolder: options.containerSessionDataFolder,
Expand Down Expand Up @@ -190,6 +192,7 @@ export async function createDockerParams(options: ProvisionOptions, disposables:
buildxPlatform: common.buildxPlatform,
buildxPush: common.buildxPush,
buildxOutput: common.buildxOutput,
buildxCacheTo: common.buildxCacheTo,
};
}

Expand Down Expand Up @@ -231,4 +234,4 @@ function maskSecrets(handler: LogHandler, secrets?: Record<string, string>): Log
}

return handler;
}
}
13 changes: 12 additions & 1 deletion src/spec-node/devContainersSpecCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ async function provision({
buildxPlatform: undefined,
buildxPush: false,
buildxOutput: undefined,
buildxCacheTo: undefined,
additionalFeatures,
skipFeatureAutoMapping,
skipPostAttach,
Expand Down Expand Up @@ -405,6 +406,7 @@ async function doSetUp({
buildxPlatform: undefined,
buildxPush: false,
buildxOutput: undefined,
buildxCacheTo: undefined,
skipFeatureAutoMapping: false,
skipPostAttach: false,
skipPersistingCustomizationsFromFeatures: false,
Expand Down Expand Up @@ -474,6 +476,7 @@ function buildOptions(y: Argv) {
'no-cache': { type: 'boolean', default: false, description: 'Builds the image with `--no-cache`.' },
'image-name': { type: 'string', description: 'Image name.' },
'cache-from': { type: 'string', description: 'Additional image to use as potential layer cache' },
'cache-to': { type: 'string', description: 'A destination of buildx cache' },
'buildkit': { choices: ['auto' as 'auto', 'never' as 'never'], default: 'auto' as 'auto', description: 'Control whether BuildKit should be used' },
'platform': { type: 'string', description: 'Set target platforms.' },
'push': { type: 'boolean', default: false, description: 'Push to a container registry.' },
Expand Down Expand Up @@ -515,6 +518,7 @@ async function doBuild({
'platform': buildxPlatform,
'push': buildxPush,
'output': buildxOutput,
'cache-to': buildxCacheTo,
'additional-features': additionalFeaturesJson,
'skip-feature-auto-mapping': skipFeatureAutoMapping,
'skip-persisting-customizations-from-features': skipPersistingCustomizationsFromFeatures,
Expand Down Expand Up @@ -560,6 +564,7 @@ async function doBuild({
buildxPlatform,
buildxPush,
buildxOutput,
buildxCacheTo,
skipFeatureAutoMapping,
skipPostAttach: true,
skipPersistingCustomizationsFromFeatures: skipPersistingCustomizationsFromFeatures,
Expand Down Expand Up @@ -616,6 +621,10 @@ async function doBuild({
throw new ContainerError({ description: '--output not supported.' });
}

if (buildxCacheTo) {
throw new ContainerError({ description: '--cache-to not supported.' });
}

const cwdEnvFile = cliHost.path.join(cliHost.cwd, '.env');
const envFile = Array.isArray(config.dockerComposeFile) && config.dockerComposeFile.length === 0 && await cliHost.isFile(cwdEnvFile) ? cwdEnvFile : undefined;
const composeFiles = await getDockerComposeFilePaths(cliHost, config, cliHost.env, workspaceFolder);
Expand Down Expand Up @@ -818,6 +827,7 @@ async function doRunUserCommands({
buildxPlatform: undefined,
buildxPush: false,
buildxOutput: undefined,
buildxCacheTo: undefined,
skipFeatureAutoMapping,
skipPostAttach,
skipPersistingCustomizationsFromFeatures: false,
Expand Down Expand Up @@ -1268,6 +1278,7 @@ export async function doExec({
omitLoggerHeader: true,
buildxPlatform: undefined,
buildxPush: false,
buildxCacheTo: undefined,
skipFeatureAutoMapping,
buildxOutput: undefined,
skipPostAttach: false,
Expand Down Expand Up @@ -1367,4 +1378,4 @@ async function readSecretsFromFile(params: { output?: Log; secretsFile?: string;
originalError: e
});
}
}
}
1 change: 1 addition & 0 deletions src/spec-node/featuresCLI/testCommandImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ async function generateDockerParams(workspaceFolder: string, args: FeaturesTestC
buildxPlatform: undefined,
buildxPush: false,
buildxOutput: undefined,
buildxCacheTo: undefined,
skipFeatureAutoMapping: false,
skipPostAttach: false,
skipPersistingCustomizationsFromFeatures: false,
Expand Down
1 change: 1 addition & 0 deletions src/spec-node/featuresCLI/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const staticProvisionParams = {
buildxPlatform: undefined,
buildxPush: false,
buildxOutput: undefined,
buildxCacheTo: undefined,
skipPostAttach: false,
};

Expand Down
3 changes: 3 additions & 0 deletions src/spec-node/singleContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ async function buildAndExtendImage(buildParams: DockerResolverParameters, config
args.push('--load'); // (short for --output=docker, i.e. load into normal 'docker images' collection)
}
}
if (buildParams.buildxCacheTo) {
args.push('--cache-to', buildParams.buildxCacheTo);
}
args.push('--build-arg', 'BUILDKIT_INLINE_CACHE=1');
} else {
args.push('build');
Expand Down
1 change: 1 addition & 0 deletions src/spec-node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export interface DockerResolverParameters {
buildxPlatform: string | undefined;
buildxPush: boolean;
buildxOutput: string | undefined;
buildxCacheTo: string | undefined;
}

export interface ResolverResult {
Expand Down
57 changes: 56 additions & 1 deletion src/test/cli.build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,61 @@ describe('Dev Containers CLI', function () {
assert.equal(fs.existsSync(outputPath), true);
});

it(`should execute successfully and export buildx cache with container builder`, async () => {
const builderName = 'test-container-builder';
try {
await shellExec(`docker buildx create --name ${builderName} --driver docker-container --use`);

const testFolder = `${__dirname}/configs/dockerfile-without-features`;
const outputPath = `${os.tmpdir()}/test-build-cache`;
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --log-level trace --cache-to=type=local,dest=${outputPath}`);
console.log(res.stdout);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
assert.equal(fs.existsSync(`${outputPath}/index.json`), true);
} finally {
await shellExec(`docker buildx rm ${builderName}`);
}
});

it(`should execute successfully and export buildx cache with container builder and image`, async () => {
const builderName = 'test-container-builder-image';
try {
await shellExec(`docker buildx create --name ${builderName} --driver docker-container --use`);

const testFolder = `${__dirname}/configs/image`;
const outputPath = `${os.tmpdir()}/test-build-cache-image`;
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --log-level trace --cache-to=type=local,dest=${outputPath}`);
console.log(res.stdout);
const response = JSON.parse(res.stdout);
assert.equal(response.outcome, 'success');
assert.equal(fs.existsSync(`${outputPath}/index.json`), true);
} finally {
await shellExec(`docker buildx rm ${builderName}`);
}
});

it('should fail with docker-compose and --cache-to not supported', async () => {
let success = false;
const testFolder = `${__dirname}/configs/compose-image-with-features`;
const builderName = 'test-container-builder';
const outputPath = `${os.tmpdir()}/test-build-cache`;

try {
await shellExec(`docker buildx create --name ${builderName} --driver docker-container --use`);

await shellExec(`${cli} build --workspace-folder ${testFolder} --log-level trace --cache-to=type=local,dest=${outputPath}`);
} catch (error) {
assert.equal(error.error.code, 1, 'Should fail with exit code 1');
const res = JSON.parse(error.stdout);
assert.equal(res.outcome, 'error');
assert.match(res.message, /not supported/);
} finally {
await shellExec(`docker buildx rm ${builderName}`);
}
assert.equal(success, false, 'expect non-successful call');
});

it(`should execute successfully docker-compose without features with container builder`, async () => {
const builderName = 'test-container-builder';
try {
Expand Down Expand Up @@ -261,4 +316,4 @@ describe('Dev Containers CLI', function () {
assert.strictEqual(envListToObj(details.Config.Env).SUBFOLDER_CONFIG_IMAGE_ENV, 'true');
});
});
});
});

0 comments on commit 413c5f7

Please sign in to comment.