in build/src/push.js [59:184]
async function pushImage(definitionId, repo, release, updateLatest,
registry, registryPath, stubRegistry, stubRegistryPath, prepOnly, pushImages, replaceImage) {
const definitionPath = configUtils.getDefinitionPath(definitionId);
const dotDevContainerPath = path.join(definitionPath, '.devcontainer');
// Use base.Dockerfile for image build if found, otherwise use Dockerfile
const dockerFileExists = await asyncUtils.exists(path.join(dotDevContainerPath, 'Dockerfile'));
const baseDockerFileExists = await asyncUtils.exists(path.join(dotDevContainerPath, 'base.Dockerfile'));
const dockerFilePath = path.join(dotDevContainerPath, `${baseDockerFileExists ? 'base.' : ''}Dockerfile`);
// Make sure there's a Dockerfile present
if (!await asyncUtils.exists(dockerFilePath)) {
throw `Definition ${definitionId} does not exist! Invalid path: ${definitionPath}`;
}
// Look for context in devcontainer.json and use it to build the Dockerfile
console.log('(*) Reading devcontainer.json...');
const devContainerJsonPath = path.join(dotDevContainerPath, 'devcontainer.json');
const devContainerJsonRaw = await asyncUtils.readFile(devContainerJsonPath);
const devContainerJson = jsonc.parse(devContainerJsonRaw);
// Process variants in reverse order to be sure the first one is tagged as "latest" if appropriate
const variants = configUtils.getVariants(definitionId) || [null];
for (let i = variants.length - 1; i > -1; i--) {
const variant = variants[i];
// Update common setup script download URL, SHA, parent tag if applicable
console.log(`(*) Prep Dockerfile for ${definitionId} ${variant ? 'variant "' + variant + '"' : ''}...`);
const prepResult = await prep.prepDockerFile(dockerFilePath,
definitionId, repo, release, registry, registryPath, stubRegistry, stubRegistryPath, true, variant);
if (prepOnly) {
console.log(`(*) Skipping build and push to registry.`);
} else {
if (prepResult.shouldFlattenBaseImage) {
console.log(`(*) Flattening base image...`);
await flattenBaseImage(prepResult.baseImageTag, prepResult.flattenedBaseImageTag, pushImages);
}
// Build image
console.log(`(*) Building image...`);
// Determine tags to use
const versionTags = configUtils.getTagList(definitionId, release, updateLatest, registry, registryPath, variant);
console.log(`(*) Tags:${versionTags.reduce((prev, current) => prev += `\n ${current}`, '')}`);
const buildSettings = configUtils.getBuildSettings(definitionId);
let architectures = buildSettings.architectures;
switch (typeof architectures) {
case 'string': architectures = [architectures]; break;
case 'object': if (!Array.isArray(architectures)) { architectures = architectures[variant]; } break;
case 'undefined': architectures = ['linux/amd64']; break;
}
console.log(`(*) Target image architectures: ${architectures.reduce((prev, current) => prev += `\n ${current}`, '')}`);
let localArchitecture = process.arch;
switch(localArchitecture) {
case 'arm': localArchitecture = 'linux/arm/v7'; break;
case 'aarch32': localArchitecture = 'linux/arm/v7'; break;
case 'aarch64': localArchitecture = 'linux/arm64'; break;
case 'x64': localArchitecture = 'linux/amd64'; break;
case 'x32': localArchitecture = 'linux/386'; break;
default: localArchitecture = `linux/${localArchitecture}`; break;
}
console.log(`(*) Local architecture: ${localArchitecture}`);
if (!pushImages) {
console.log(`(*) Push disabled: Only building local architecture (${localArchitecture}).`);
}
if (replaceImage || !await isDefinitionVersionAlreadyPublished(definitionId, release, registry, registryPath, variant)) {
const context = devContainerJson.build ? devContainerJson.build.context || '.' : devContainerJson.context || '.';
const workingDir = path.resolve(dotDevContainerPath, context);
// Add tags to buildx command params
const buildParams = versionTags.reduce((prev, current) => prev.concat(['-t', current]), []);
// Note: build.args in devcontainer.json is intentionally ignored so you can vary image contents and defaults as needed
// Add VARIANT --build-arg if applicable
if(variant) {
buildParams.push('--build-arg', `VARIANT=${variant}`);
}
// Generate list of --build-arg values if applicable
for (let buildArg in buildSettings.buildArgs || {}) {
buildParams.push('--build-arg', `${buildArg}=${buildSettings.buildArgs[buildArg]}`);
}
// Generate list of variant specific --build-arg values if applicable
if (buildSettings.variantBuildArgs) {
for (let buildArg in buildSettings.variantBuildArgs[variant] || {}) {
buildParams.push('--build-arg', `${buildArg}=${buildSettings.variantBuildArgs[variant][buildArg]}`);
}
}
const spawnOpts = { stdio: 'inherit', cwd: workingDir, shell: true };
await asyncUtils.spawn('docker', [
'buildx',
'build',
workingDir,
'-f', dockerFilePath,
'--label', `version=${prepResult.meta.version}`,
`--label`, `${imageLabelPrefix}.id=${prepResult.meta.definitionId}`,
'--label', `${imageLabelPrefix}.variant=${prepResult.meta.variant}`,
'--label', `${imageLabelPrefix}.release=${prepResult.meta.gitRepositoryRelease}`,
'--label', `${imageLabelPrefix}.source=${prepResult.meta.gitRepository}`,
'--label', `${imageLabelPrefix}.timestamp='${prepResult.meta.buildTimestamp}'`,
'--builder', 'vscode-dev-containers',
'--progress', 'plain',
'--platform', pushImages ? architectures.reduce((prev, current) => prev + ',' + current, '').substring(1) : localArchitecture,
pushImages ? '--push' : '--load',
...buildParams
], spawnOpts);
if (!pushImages) {
console.log(`(*) Skipping push to registry.`);
}
} else {
console.log(`(*) Version already published. Skipping.`);
}
}
}
// If base.Dockerfile found, update stub/devcontainer.json, otherwise create - just use the default (first) variant if one exists
if (baseDockerFileExists && dockerFileExists) {
await prep.updateStub(
dotDevContainerPath, definitionId, repo, release, baseDockerFileExists, stubRegistry, stubRegistryPath);
console.log('(*) Updating devcontainer.json...');
await asyncUtils.writeFile(devContainerJsonPath, devContainerJsonRaw.replace('"base.Dockerfile"', '"Dockerfile"'));
console.log('(*) Removing base.Dockerfile...');
await asyncUtils.rimraf(dockerFilePath);
} else {
await prep.createStub(
dotDevContainerPath, definitionId, repo, release, baseDockerFileExists, stubRegistry, stubRegistryPath);
}
console.log('(*) Done!\n');
}