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

Disable cache on feature build when --build-no-cache is passed #790

Merged
3 changes: 3 additions & 0 deletions src/spec-node/containerFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ export async function extendImage(params: DockerResolverParameters, config: Subs
'build',
);
}
if (params.buildNoCache) {
args.push('--no-cache');
}
for (const buildArg in featureBuildInfo.buildArgs) {
args.push('--build-arg', `${buildArg}=${featureBuildInfo.buildArgs[buildArg]}`);
}
Expand Down
57 changes: 57 additions & 0 deletions src/test/cli.build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as os from 'os';
import { buildKitOptions, shellExec } from './testUtils';
import { ImageDetails } from '../spec-shutdown/dockerUtils';
import { envListToObj } from '../spec-node/utils';
import * as crypto from 'crypto';

const pkg = require('../../package.json');

Expand Down Expand Up @@ -64,6 +65,62 @@ describe('Dev Containers CLI', function () {
});
});

describe('caching', () => {
let buildCommandRerunLog = '';
let buildWithoutCacheLog = '';
this.beforeEach(() => {
buildCommandRerunLog = `${os.tmpdir()}/${crypto.randomUUID()}`;
buildWithoutCacheLog = `${os.tmpdir()}/${crypto.randomUUID()}`;
console.log(`rerun log: ${buildCommandRerunLog}`);
console.log(`no cache log: ${buildWithoutCacheLog}`);
});

this.afterEach(() => {
for (const file in [buildCommandRerunLog, buildWithoutCacheLog]) {
if (fs.existsSync(file)) {
// fs.unlinkSync(file);
}
}
});

it('should not use docker cache for features when `--no-cache` flag is passed', async () => {
// Arrange
const testFolder = `${__dirname}/configs/image-with-features`;
const buildCommand = `${cli} build --workspace-folder ${testFolder}`;
const buildWithoutCacheCommand = `${buildCommand} --no-cache`;
const expectedFeatures = [
'ghcr.io/devcontainers/feature-starter/hello',
'ghcr.io/devcontainers/features/docker-in-docker'
];

// Act
await shellExec(`${buildCommand}`); // initial run of command
await shellExec(`${buildCommand} > ${buildCommandRerunLog} 2>&1`); // rerun command using cache
await shellExec(`${buildWithoutCacheCommand} > ${buildWithoutCacheLog} 2>&1`); // rerun command without cache

// Assert
const buildCommandRerunLogContents = fs.readFileSync(buildCommandRerunLog, 'utf8');
const buildWithoutCacheCommandLogContents = fs.readFileSync(buildWithoutCacheLog, 'utf8');

for (const logContent in [buildCommandRerunLogContents, buildWithoutCacheCommandLogContents]) {
assert.notEqual(logContent, null);
assert.notEqual(logContent, undefined);
assert.notEqual(logContent, '');
}

function countInstances(subject: string, search: string) {
return subject.split(search).length - 1;
}

for (const expectedFeature of expectedFeatures) {
const featureNameInstanceCountInRerunOfBuildCommandLogs = countInstances(buildCommandRerunLogContents, expectedFeature);
const featureNameInstanceCountInBuildWithNoCache = countInstances(buildWithoutCacheCommandLogContents, expectedFeature);
assert.equal(featureNameInstanceCountInRerunOfBuildCommandLogs, 1, 'because a cached build prints features being installed in the image once at the start of the build when they are being resolved');
samruddhikhandale marked this conversation as resolved.
Show resolved Hide resolved
assert.equal(featureNameInstanceCountInBuildWithNoCache, 2, 'because a non-cached build prints the features being installed in the image twice. Once at the start of the build when resolving the image, then again when installing the features in the image.');
}
});
});

it('should fail with "not found" error when config is not found', async () => {
let success = false;
try {
Expand Down