From 27b3c097f1edfef05707bfe002b1e2624606784f Mon Sep 17 00:00:00 2001 From: sireeshajonnalagadda Date: Mon, 8 Jun 2026 07:18:48 +0000 Subject: [PATCH 1/5] fix: set default base image in Dockerfiles and improve syntax handling --- .devcontainer/devcontainer-lock.json | 2 +- scripts/updateUID.Dockerfile | 2 +- .../containerFeaturesConfiguration.ts | 8 +- src/spec-node/containerFeatures.ts | 86 +++++++++---------- 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json index 53c5a53c2..55c2bfcef 100644 --- a/.devcontainer/devcontainer-lock.json +++ b/.devcontainer/devcontainer-lock.json @@ -6,4 +6,4 @@ "integrity": "sha256:ce078b7bf7d9ef3bcb9813b32103795d8d72172446890b64772cbe1dec6baafd" } } -} +} \ No newline at end of file diff --git a/scripts/updateUID.Dockerfile b/scripts/updateUID.Dockerfile index 9f6c9a854..3cd1c33fe 100644 --- a/scripts/updateUID.Dockerfile +++ b/scripts/updateUID.Dockerfile @@ -1,6 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -ARG BASE_IMAGE +ARG BASE_IMAGE=placeholder FROM $BASE_IMAGE USER root diff --git a/src/spec-configuration/containerFeaturesConfiguration.ts b/src/spec-configuration/containerFeaturesConfiguration.ts index 5957d0896..3f12b5066 100644 --- a/src/spec-configuration/containerFeaturesConfiguration.ts +++ b/src/spec-configuration/containerFeaturesConfiguration.ts @@ -203,12 +203,14 @@ export function getContainerFeaturesBaseDockerFile(contentSourceRootPath: string #{nonBuildKitFeatureContentFallback} -FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_feature_content_normalize +ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder + +FROM \${_DEV_CONTAINERS_BASE_IMAGE:-scratch} AS dev_containers_feature_content_normalize USER root COPY --from=dev_containers_feature_content_source ${path.posix.join(contentSourceRootPath, 'devcontainer-features.builtin.env')} /tmp/build-features/ RUN chmod -R 0755 /tmp/build-features/ -FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage +FROM \${_DEV_CONTAINERS_BASE_IMAGE:-scratch} AS dev_containers_target_stage USER root @@ -1122,7 +1124,7 @@ export async function fetchContentsAtTarballUri(params: { output: Log; env: Node // No 'metadataFile' to look for. if (!metadataFile) { - await cleanupIterationFetchAndMerge(tempTarballPath, output); + await cleanupIterationFetchAndMerge(tempTarballPath, output); return { computedDigest, metadata: undefined }; } diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index d8912967c..7b7da9149 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -174,8 +174,7 @@ export function generateContainerEnvsV1(featuresConfig: FeaturesConfig) { let result = ''; for (const fSet of featuresConfig.featureSets) { // We only need to generate this ENV references for the initial features specification. - if (fSet.internalVersion !== '2') - { + if (fSet.internalVersion !== '2') { result += '\n'; result += fSet.features .filter(f => (includeAllConfiguredFeatures || f.included) && f.value) @@ -197,23 +196,23 @@ export interface ImageBuildOptions { } async function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): Promise { - const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - return { - dstFolder, - dockerfileContent: ` -FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage + const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; + return { + dstFolder, + dockerfileContent: ` +FROM \${_DEV_CONTAINERS_BASE_IMAGE:-scratch} AS dev_containers_target_stage ${getDevcontainerMetadataLabel(getDevcontainerMetadata(imageBuildInfo.metadata, config, { featureSets: [] }, [], getOmitDevcontainerPropertyOverride(params.common)))} `, - overrideTarget: 'dev_containers_target_stage', - dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} - ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder + overrideTarget: 'dev_containers_target_stage', + dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} + ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `, - buildArgs: { - _DEV_CONTAINERS_BASE_IMAGE: baseName, - } as Record, - buildKitContexts: {} as Record, - securityOpts: [], - }; + buildArgs: { + _DEV_CONTAINERS_BASE_IMAGE: baseName, + } as Record, + buildKitContexts: {} as Record, + securityOpts: [], + }; } function getOmitDevcontainerPropertyOverride(resolverParams: { omitConfigRemotEnvFromMetadata?: boolean }): (keyof DevContainerConfig & keyof ImageMetadataEntry)[] { @@ -244,10 +243,10 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont const useBuildKitBuildContexts = buildKitVersionParsed ? !isEarlierVersion(buildKitVersionParsed, minRequiredVersion) : false; const buildContentImageName = 'dev_container_feature_content_temp'; const disableSELinuxLabels = useBuildKitBuildContexts && await isUsingSELinuxLabels(params); - // Access Docker engine version - const dockerEngineVersionParsed = params.dockerEngineVersion?.versionMatch ? parseVersion(params.dockerEngineVersion.versionMatch) : undefined; - const minDockerEngineVersion = [23, 0, 0]; - const skipDefaultSyntax = dockerEngineVersionParsed ? !isEarlierVersion(dockerEngineVersionParsed, minDockerEngineVersion) : false; + // Access Docker engine version + const dockerEngineVersionParsed = params.dockerEngineVersion?.versionMatch ? parseVersion(params.dockerEngineVersion.versionMatch) : undefined; + const minDockerEngineVersion = [23, 0, 0]; + const skipDefaultSyntax = dockerEngineVersionParsed ? !isEarlierVersion(dockerEngineVersionParsed, minDockerEngineVersion) : false; const omitPropertyOverride = params.common.skipPersistingCustomizationsFromFeatures ? ['customizations'] : []; const imageMetadata = getDevcontainerMetadata(imageBuildInfo.metadata, devContainerConfig, featuresConfig, omitPropertyOverride, getOmitDevcontainerPropertyOverride(params.common)); const { containerUser, remoteUser } = findContainerUsers(imageMetadata, composeServiceUser, imageBuildInfo.user); @@ -268,22 +267,20 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont .replace('#{devcontainerMetadata}', getDevcontainerMetadataLabel(imageMetadata)) .replace('#{containerEnvMetadata}', generateContainerEnvs(devContainerConfig.config.containerEnv, true)) ; - const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed - const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : - skipDefaultSyntax ? (syntax ? `# syntax=${syntax}` : '') : - useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : - syntax ? `# syntax=${syntax}` : ''} -ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder + const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; + const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed + const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : + skipDefaultSyntax ? (syntax ? `# syntax=${syntax}` : '') : + useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : + syntax ? `# syntax=${syntax}` : ''} + ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `; // Build devcontainer-features.env and devcontainer-features-install.sh file(s) for each features source folder for await (const fSet of featuresConfig.featureSets) { - if (fSet.internalVersion === '2') - { + if (fSet.internalVersion === '2') { for await (const fe of fSet.features) { - if (fe.cachePath) - { + if (fe.cachePath) { fe.internalVersion = '2'; const envPath = cliHost.path.join(fe.cachePath, 'devcontainer-features.env'); const variables = getFeatureEnvVariables(fe); @@ -305,17 +302,17 @@ ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder cliHost.writeFile(envPath, Buffer.from(featuresEnv)), ...fSet.features .filter(f => (includeAllConfiguredFeatures || f.included) && f.value) - .map(f => { - const consecutiveId = f.consecutiveId; - if (!consecutiveId) { - throw new Error('consecutiveId is undefined for Feature ' + f.id); - } - const featuresEnv = [ - ...getFeatureEnvVariables(f), - `_BUILD_ARG_${getSafeId(f.id)}_TARGETPATH=${path.posix.join('/usr/local/devcontainer-features', consecutiveId)}` - ] - .join('\n'); - const envPath = cliHost.path.join(dstFolder, consecutiveId, 'devcontainer-features.env'); // next to bin/acquire + .map(f => { + const consecutiveId = f.consecutiveId; + if (!consecutiveId) { + throw new Error('consecutiveId is undefined for Feature ' + f.id); + } + const featuresEnv = [ + ...getFeatureEnvVariables(f), + `_BUILD_ARG_${getSafeId(f.id)}_TARGETPATH=${path.posix.join('/usr/local/devcontainer-features', consecutiveId)}` + ] + .join('\n'); + const envPath = cliHost.path.join(dstFolder, consecutiveId, 'devcontainer-features.env'); // next to bin/acquire return cliHost.writeFile(envPath, Buffer.from(featuresEnv)); }) ]); @@ -378,7 +375,7 @@ async function isUsingSELinuxLabels(params: DockerResolverParameters): Promise `_BUILD_ARG_${idSafe}_${getSafeId(name)}="${values[name]}"`)); From a1852f46a5b5b106420e60176068267465990e41 Mon Sep 17 00:00:00 2001 From: sireeshajonnalagadda Date: Mon, 8 Jun 2026 14:08:06 +0530 Subject: [PATCH 2/5] Fix formatting in containerFeatures.ts --- src/spec-node/containerFeatures.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index 7b7da9149..8ba3e6438 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -203,9 +203,9 @@ async function getImageBuildOptions(params: DockerResolverParameters, config: Su FROM \${_DEV_CONTAINERS_BASE_IMAGE:-scratch} AS dev_containers_target_stage ${getDevcontainerMetadataLabel(getDevcontainerMetadata(imageBuildInfo.metadata, config, { featureSets: [] }, [], getOmitDevcontainerPropertyOverride(params.common)))} `, - overrideTarget: 'dev_containers_target_stage', - dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} - ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder + overrideTarget: 'dev_containers_target_stage', + dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} +ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `, buildArgs: { _DEV_CONTAINERS_BASE_IMAGE: baseName, From 8f1f09ca854076a685321dfd94b0fbe09c2e913a Mon Sep 17 00:00:00 2001 From: sireeshajonnalagadda Date: Mon, 8 Jun 2026 14:13:37 +0530 Subject: [PATCH 3/5] Fix formatting of ARG declaration in containerFeatures.ts --- src/spec-node/containerFeatures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index 8ba3e6438..b78eb4ec8 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -273,7 +273,7 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont skipDefaultSyntax ? (syntax ? `# syntax=${syntax}` : '') : useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : syntax ? `# syntax=${syntax}` : ''} - ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder +ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `; // Build devcontainer-features.env and devcontainer-features-install.sh file(s) for each features source folder From 72221b3420b23ce1df60f23d8d0e0175958b1682 Mon Sep 17 00:00:00 2001 From: sireeshajonnalagadda Date: Mon, 8 Jun 2026 14:34:19 +0530 Subject: [PATCH 4/5] Change base image from placeholder to scratch --- scripts/updateUID.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/updateUID.Dockerfile b/scripts/updateUID.Dockerfile index 3cd1c33fe..303339f76 100644 --- a/scripts/updateUID.Dockerfile +++ b/scripts/updateUID.Dockerfile @@ -1,6 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -ARG BASE_IMAGE=placeholder +ARG BASE_IMAGE=scratch FROM $BASE_IMAGE USER root From 93369269ba13f886aac8a58b6a96a85c7073cca1 Mon Sep 17 00:00:00 2001 From: sireeshajonnalagadda Date: Mon, 8 Jun 2026 14:50:51 +0530 Subject: [PATCH 5/5] Remove default value for BASE_IMAGE argument --- scripts/updateUID.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/updateUID.Dockerfile b/scripts/updateUID.Dockerfile index 303339f76..9f6c9a854 100644 --- a/scripts/updateUID.Dockerfile +++ b/scripts/updateUID.Dockerfile @@ -1,6 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -ARG BASE_IMAGE=scratch +ARG BASE_IMAGE FROM $BASE_IMAGE USER root