Skip to content

Commit 6687ad4

Browse files
authored
revert changing dest location to /opt/build-features and remerge main
2 parents c000b10 + 4705707 commit 6687ad4

11 files changed

+133
-53
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
Notable changes.
44

5+
## February 2023
6+
7+
### [0.30.0]
8+
9+
- Fix: Merge metadata logic for containerEnv for `devcontainer build`. (https://github.com/devcontainers/cli/pull/392)
10+
- Support querying registries that Accept application/vnd.oci.image.index.v1+json. (https://github.com/devcontainers/cli/pull/393)
11+
- Updates Features cache logic - Incrementally copy features near the layer they're installed. (https://github.com/devcontainers/cli/pull/382)
12+
513
## January 2023
614

715
### [0.29.0]

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@devcontainers/cli",
33
"description": "Dev Containers CLI",
4-
"version": "0.29.0",
4+
"version": "0.30.0",
55
"bin": {
66
"devcontainer": "devcontainer.js"
77
},

src/spec-configuration/containerFeaturesConfiguration.ts

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const DEVCONTAINER_FEATURE_FILE_NAME = 'devcontainer-feature.json';
2525

2626
export type Feature = SchemaFeatureBaseProperties & SchemaFeatureLifecycleHooks & DeprecatedSchemaFeatureProperties & InternalFeatureProperties;
2727

28-
export const CONTAINER_BUILD_FEATURES_DIR = '/opt/build-features';
28+
export const CONTAINER_BUILD_FEATURES_DIR = '/tmp/build-features';
2929

3030
export interface SchemaFeatureLifecycleHooks {
3131
onCreateCommand?: string | string[];
@@ -247,14 +247,14 @@ export function getContainerFeaturesBaseDockerFile() {
247247
248248
FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_feature_content_normalize
249249
USER root
250-
COPY --from=dev_containers_feature_content_source {contentSourceRootPath} /opt/build-features/
251-
RUN chmod -R 0777 /opt/build-features
250+
COPY --from=dev_containers_feature_content_source {contentSourceRootPath}/devcontainer-features.builtin.env /tmp/build-features/
251+
RUN chmod -R 0777 /tmp/build-features
252252
253253
FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage
254254
255255
USER root
256256
257-
COPY --from=dev_containers_feature_content_normalize /opt/build-features /opt/build-features
257+
COPY --from=dev_containers_feature_content_normalize /tmp/build-features /tmp/build-features
258258
259259
#{featureLayer}
260260
@@ -333,32 +333,63 @@ function escapeQuotesForShell(input: string) {
333333
return input.replace(new RegExp(`'`, 'g'), `'\\''`);
334334
}
335335

336-
export function getFeatureLayers(featuresConfig: FeaturesConfig, containerUser: string, remoteUser: string) {
336+
export function getFeatureLayers(featuresConfig: FeaturesConfig, containerUser: string, remoteUser: string, useBuildKitBuildContexts = false, contentSourceRootPath = '/tmp/build-features/') {
337337
let result = `RUN \\
338-
echo "_CONTAINER_USER_HOME=$(getent passwd ${containerUser} | cut -d: -f6)" >> /opt/build-features/devcontainer-features.builtin.env && \\
339-
echo "_REMOTE_USER_HOME=$(getent passwd ${remoteUser} | cut -d: -f6)" >> /opt/build-features/devcontainer-features.builtin.env
338+
echo "_CONTAINER_USER_HOME=$(getent passwd ${containerUser} | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env && \\
339+
echo "_REMOTE_USER_HOME=$(getent passwd ${remoteUser} | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env
340340
341341
`;
342342

343343
// Features version 1
344344
const folders = (featuresConfig.featureSets || []).filter(y => y.internalVersion !== '2').map(x => x.features[0].consecutiveId);
345345
folders.forEach(folder => {
346-
result += `RUN cd /opt/build-features/${folder} \\
346+
const source = path.posix.join(contentSourceRootPath, folder!);
347+
if (!useBuildKitBuildContexts) {
348+
result += `COPY --chown=root:root --from=dev_containers_feature_content_source ${source} /tmp/build-features/${folder}
349+
RUN chmod -R 0777 /tmp/build-features/${folder} \\
350+
&& cd /tmp/build-features/${folder} \\
347351
&& chmod +x ./install.sh \\
348352
&& ./install.sh
349353
350354
`;
355+
} else {
356+
result += `RUN --mount=type=bind,from=dev_containers_feature_content_source,source=${source},target=/tmp/build-features-src/${folder} \\
357+
cp -ar /tmp/build-features-src/${folder} /tmp/build-features/ \\
358+
&& chmod -R 0777 /tmp/build-features/${folder} \\
359+
&& cd /tmp/build-features/${folder} \\
360+
&& chmod +x ./install.sh \\
361+
&& ./install.sh \\
362+
&& rm -rf /tmp/build-features/${folder}
363+
364+
`;
365+
}
351366
});
352367
// Features version 2
353368
featuresConfig.featureSets.filter(y => y.internalVersion === '2').forEach(featureSet => {
354369
featureSet.features.forEach(feature => {
355370
result += generateContainerEnvs(feature);
356-
result += `
357-
RUN cd /opt/build-features/${feature.consecutiveId} \\
371+
const source = path.posix.join(contentSourceRootPath, feature.consecutiveId!);
372+
if (!useBuildKitBuildContexts) {
373+
result += `
374+
COPY --chown=root:root --from=dev_containers_feature_content_source ${source} /tmp/build-features/${feature.consecutiveId}
375+
RUN chmod -R 0777 /tmp/build-features/${feature.consecutiveId} \\
376+
&& cd /tmp/build-features/${feature.consecutiveId} \\
358377
&& chmod +x ./devcontainer-features-install.sh \\
359378
&& ./devcontainer-features-install.sh
360379
361380
`;
381+
} else {
382+
result += `
383+
RUN --mount=type=bind,from=dev_containers_feature_content_source,source=${source},target=/tmp/build-features-src/${feature.consecutiveId} \\
384+
cp -ar /tmp/build-features-src/${feature.consecutiveId} /tmp/build-features/ \\
385+
&& chmod -R 0777 /tmp/build-features/${feature.consecutiveId} \\
386+
&& cd /tmp/build-features/${feature.consecutiveId} \\
387+
&& chmod +x ./devcontainer-features-install.sh \\
388+
&& ./devcontainer-features-install.sh \\
389+
&& rm -rf /tmp/build-features/${feature.consecutiveId}
390+
391+
`;
392+
}
362393
});
363394
});
364395
return result;

src/spec-node/configContainer.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as jsonc from 'jsonc-parser';
99

1010
import { openDockerfileDevContainer } from './singleContainer';
1111
import { openDockerComposeDevContainer } from './dockerCompose';
12-
import { ResolverResult, DockerResolverParameters, isDockerFileConfig, runUserCommand, createDocuments, getWorkspaceConfiguration, BindMountConsistency, uriToFsPath, DevContainerAuthority, isDevContainerAuthority, SubstituteConfig, SubstitutedConfig, addSubstitution, envListToObj } from './utils';
12+
import { ResolverResult, DockerResolverParameters, isDockerFileConfig, runUserCommand, createDocuments, getWorkspaceConfiguration, BindMountConsistency, uriToFsPath, DevContainerAuthority, isDevContainerAuthority, SubstituteConfig, SubstitutedConfig, addSubstitution, envListToObj, findContainerAndIdLabels } from './utils';
1313
import { beforeContainerSubstitute, substitute } from '../spec-common/variableSubstitution';
1414
import { ContainerError } from '../spec-common/errors';
1515
import { Workspace, workspaceFromPath, isWorkspacePath } from '../spec-utils/workspaces';
@@ -21,19 +21,19 @@ import { DevContainerConfig, DevContainerFromDockerComposeConfig, DevContainerFr
2121

2222
export { getWellKnownDevContainerPaths as getPossibleDevContainerPaths } from '../spec-configuration/configurationCommonUtils';
2323

24-
export async function resolve(params: DockerResolverParameters, configFile: URI | undefined, overrideConfigFile: URI | undefined, idLabels: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>): Promise<ResolverResult> {
24+
export async function resolve(params: DockerResolverParameters, configFile: URI | undefined, overrideConfigFile: URI | undefined, providedIdLabels: string[] | undefined, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>): Promise<ResolverResult> {
2525
if (configFile && !/\/\.?devcontainer\.json$/.test(configFile.path)) {
2626
throw new Error(`Filename must be devcontainer.json or .devcontainer.json (${uriToFsPath(configFile, params.common.cliHost.platform)}).`);
2727
}
2828
const parsedAuthority = params.parsedAuthority;
2929
if (!parsedAuthority || isDevContainerAuthority(parsedAuthority)) {
30-
return resolveWithLocalFolder(params, parsedAuthority, configFile, overrideConfigFile, idLabels, additionalFeatures);
30+
return resolveWithLocalFolder(params, parsedAuthority, configFile, overrideConfigFile, providedIdLabels, additionalFeatures);
3131
} else {
3232
throw new Error(`Unexpected authority: ${JSON.stringify(parsedAuthority)}`);
3333
}
3434
}
3535

36-
async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAuthority: DevContainerAuthority | undefined, configFile: URI | undefined, overrideConfigFile: URI | undefined, idLabels: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>): Promise<ResolverResult> {
36+
async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAuthority: DevContainerAuthority | undefined, configFile: URI | undefined, overrideConfigFile: URI | undefined, providedIdLabels: string[] | undefined, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>): Promise<ResolverResult> {
3737
const { common, workspaceMountConsistencyDefault } = params;
3838
const { cliHost, output } = common;
3939

@@ -52,6 +52,7 @@ async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAu
5252
throw new ContainerError({ description: `No dev container config and no workspace found.` });
5353
}
5454
}
55+
const idLabels = providedIdLabels || (await findContainerAndIdLabels(params, undefined, providedIdLabels, workspace?.rootFolderPath, configPath?.fsPath, params.removeOnStartup)).idLabels;
5556
const configWithRaw = addSubstitution(configs.config, config => beforeContainerSubstitute(envListToObj(idLabels), config));
5657
const { config } = configWithRaw;
5758

src/spec-node/containerFeatures.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,13 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont
283283
await cliHost.writeFile(envPath, Buffer.from(builtinVariables.join('\n') + '\n'));
284284

285285
// When copying via buildkit, the content is accessed via '.' (i.e. in the context root)
286-
// When copying via temp image, the content is in '/opt/build-features'
287-
const contentSourceRootPath = useBuildKitBuildContexts ? '.' : '/opt/build-features/';
286+
// When copying via temp image, the content is in '/tmp/build-features'
287+
const contentSourceRootPath = useBuildKitBuildContexts ? '.' : '/tmp/build-features/';
288288
const dockerfile = getContainerFeaturesBaseDockerFile()
289289
.replace('#{nonBuildKitFeatureContentFallback}', useBuildKitBuildContexts ? '' : `FROM ${buildContentImageName} as dev_containers_feature_content_source`)
290290
.replace('{contentSourceRootPath}', contentSourceRootPath)
291291
.replace('#{featureBuildStages}', getFeatureBuildStages(featuresConfig, buildStageScripts, contentSourceRootPath))
292-
.replace('#{featureLayer}', getFeatureLayers(featuresConfig, containerUser, remoteUser))
292+
.replace('#{featureLayer}', getFeatureLayers(featuresConfig, containerUser, remoteUser, useBuildKitBuildContexts, contentSourceRootPath))
293293
.replace('#{containerEnv}', generateContainerEnvs(featuresConfig))
294294
.replace('#{copyFeatureBuildStages}', getCopyFeatureBuildStages(featuresConfig, buildStageScripts))
295295
.replace('#{devcontainerMetadata}', getDevcontainerMetadataLabel(imageMetadata, common.experimentalImageMetadata))
@@ -349,7 +349,7 @@ ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder
349349
if (!useBuildKitBuildContexts) {
350350
const buildContentDockerfile = `
351351
FROM scratch
352-
COPY . /opt/build-features/
352+
COPY . /tmp/build-features/
353353
`;
354354
const buildContentDockerfilePath = cliHost.path.join(dstFolder, 'Dockerfile.buildContent');
355355
await cliHost.writeFile(buildContentDockerfilePath, Buffer.from(buildContentDockerfile));
@@ -394,9 +394,9 @@ function getFeatureBuildStages(featuresConfig: FeaturesConfig, buildStageScripts
394394
.map((featureSet, i) => featureSet.features
395395
.filter(f => (includeAllConfiguredFeatures || f.included) && f.value && buildStageScripts[i][f.id]?.hasAcquire)
396396
.map(f => `FROM mcr.microsoft.com/vscode/devcontainers/base:0-focal as ${getSourceInfoString(featureSet.sourceInformation)}_${f.id}
397-
COPY --from=dev_containers_feature_content_normalize ${path.posix.join(contentSourceRootPath, getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} ${path.posix.join('/opt/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)}
398-
COPY --from=dev_containers_feature_content_normalize ${path.posix.join(contentSourceRootPath, getSourceInfoString(featureSet.sourceInformation), 'common')} ${path.posix.join('/opt/build-features', getSourceInfoString(featureSet.sourceInformation), 'common')}
399-
RUN cd ${path.posix.join('/opt/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} && set -a && . ./devcontainer-features.env && set +a && ./bin/acquire`
397+
COPY --from=dev_containers_feature_content_normalize ${path.posix.join(contentSourceRootPath, getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} ${path.posix.join('/tmp/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)}
398+
COPY --from=dev_containers_feature_content_normalize ${path.posix.join(contentSourceRootPath, getSourceInfoString(featureSet.sourceInformation), 'common')} ${path.posix.join('/tmp/build-features', getSourceInfoString(featureSet.sourceInformation), 'common')}
399+
RUN cd ${path.posix.join('/tmp/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} && set -a && . ./devcontainer-features.env && set +a && ./bin/acquire`
400400
)
401401
)
402402
).join('\n\n');
@@ -409,7 +409,7 @@ function getCopyFeatureBuildStages(featuresConfig: FeaturesConfig, buildStageScr
409409
.map(f => {
410410
const featurePath = path.posix.join('/usr/local/devcontainer-features', getSourceInfoString(featureSet.sourceInformation), f.id);
411411
return `COPY --from=${getSourceInfoString(featureSet.sourceInformation)}_${f.id} ${featurePath} ${featurePath}${buildStageScripts[i][f.id]?.hasConfigure ? `
412-
RUN cd ${path.posix.join('/opt/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} && set -a && . ./devcontainer-features.env && set +a && ./bin/configure` : ''}`;
412+
RUN cd ${path.posix.join('/tmp/build-features', getSourceInfoString(featureSet.sourceInformation), 'features', f.id)} && set -a && . ./devcontainer-features.env && set +a && ./bin/configure` : ''}`;
413413
})
414414
)
415415
).join('\n\n');

src/spec-node/devContainers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ export interface ProvisionOptions {
6565
};
6666
}
6767

68-
export async function launch(options: ProvisionOptions, idLabels: string[], disposables: (() => Promise<unknown> | undefined)[]) {
68+
export async function launch(options: ProvisionOptions, providedIdLabels: string[] | undefined, disposables: (() => Promise<unknown> | undefined)[]) {
6969
const params = await createDockerParams(options, disposables);
7070
const output = params.common.output;
7171
const text = 'Resolving Remote';
7272
const start = output.start(text);
7373

74-
const result = await resolve(params, options.configFile, options.overrideConfigFile, idLabels, options.additionalFeatures ?? {});
74+
const result = await resolve(params, options.configFile, options.overrideConfigFile, providedIdLabels, options.additionalFeatures ?? {});
7575
output.stop(text, start);
7676
const { dockerContainerId, composeProjectName } = result;
7777
return {

0 commit comments

Comments
 (0)