New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@devcontainers/cli

Package Overview
Dependencies
Maintainers
3
Versions
101
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@devcontainers/cli - npm Package Compare versions

Comparing version 0.15.0 to 0.16.0

dist/spec-node/dockerfileUtils.d.ts

4

CHANGELOG.md

@@ -7,2 +7,6 @@ # Change Log

### [0.16.0]
- Image metadata. (https://github.com/devcontainers/cli/issues/188)
### [0.15.0]

@@ -9,0 +13,0 @@

@@ -48,2 +48,3 @@ /// <reference types="node" />

skipPostAttach: boolean;
experimentalImageMetadata: boolean;
}

@@ -80,2 +81,22 @@ export interface PostCreate {

}
export interface CommonContainerMetadata {
onCreateCommand?: string | string[];
updateContentCommand?: string | string[];
postCreateCommand?: string | string[];
postStartCommand?: string | string[];
postAttachCommand?: string | string[];
waitFor?: DevContainerConfigCommand;
remoteEnv?: Record<string, string | null>;
userEnvProbe?: UserEnvProbe;
}
export declare type CommonMergedDevContainerConfig = MergedConfig<CommonDevContainerConfig>;
declare type MergedConfig<T extends CommonDevContainerConfig> = Omit<T, typeof replaceProperties[number]> & UpdatedConfigProperties;
declare const replaceProperties: readonly ["onCreateCommand", "updateContentCommand", "postCreateCommand", "postStartCommand", "postAttachCommand"];
interface UpdatedConfigProperties {
onCreateCommands?: (string | string[])[];
updateContentCommands?: (string | string[])[];
postCreateCommands?: (string | string[])[];
postStartCommands?: (string | string[])[];
postAttachCommands?: (string | string[])[];
}
export interface OSRelease {

@@ -129,7 +150,7 @@ hardware: string;

export declare function getSystemVarFolder(params: ResolverParameters): string;
export declare function setupInContainer(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonDevContainerConfig): Promise<{
export declare function setupInContainer(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonMergedDevContainerConfig): Promise<{
remoteEnv: Record<string, string>;
}>;
export declare function probeRemoteEnv(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonDevContainerConfig): Promise<Record<string, string>>;
export declare function runPostCreateCommands(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonDevContainerConfig, remoteEnv: Promise<Record<string, string>>, stopForPersonalization: boolean): Promise<'skipNonBlocking' | 'prebuild' | 'stopForPersonalization' | 'done'>;
export declare function probeRemoteEnv(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonMergedDevContainerConfig): Promise<Record<string, string>>;
export declare function runPostCreateCommands(params: ResolverParameters, containerProperties: ContainerProperties, config: CommonMergedDevContainerConfig, remoteEnv: Promise<Record<string, string>>, stopForPersonalization: boolean): Promise<'skipNonBlocking' | 'prebuild' | 'stopForPersonalization' | 'done'>;
export declare function getOSRelease(shellServer: ShellServer): Promise<{

@@ -155,1 +176,2 @@ hardware: string;

export declare function finishBackgroundTasks(tasks: (Promise<void> | (() => Promise<void>))[]): Promise<void>;
export {};

21

dist/spec-common/injectHeadless.js

@@ -78,2 +78,9 @@ "use strict";

const defaultWaitFor = 'updateContentCommand';
const replaceProperties = [
'onCreateCommand',
'updateContentCommand',
'postCreateCommand',
'postStartCommand',
'postAttachCommand',
];
async function getContainerProperties(options) {

@@ -250,3 +257,3 @@ let { params, createdAt, startedAt, remoteWorkspaceFolder, containerUser, containerGroup, containerEnv, remoteExec, remotePtyExec, remoteExecAsRoot, rootShellServer } = options;

const doRun = !!containerProperties.createdAt && await updateMarkerFile(containerProperties.shellServer, markerFile, containerProperties.createdAt) || rerun;
await runPostCommand(params, containerProperties, config, postCommandName, remoteEnv, doRun);
await runPostCommands(params, containerProperties, config, postCommandName, remoteEnv, doRun);
}

@@ -256,3 +263,3 @@ async function runPostStartCommand(params, containerProperties, config, remoteEnv) {

const doRun = !!containerProperties.startedAt && await updateMarkerFile(containerProperties.shellServer, markerFile, containerProperties.startedAt);
await runPostCommand(params, containerProperties, config, 'postStartCommand', remoteEnv, doRun);
await runPostCommands(params, containerProperties, config, 'postStartCommand', remoteEnv, doRun);
}

@@ -269,6 +276,8 @@ async function updateMarkerFile(shellServer, location, content) {

async function runPostAttachCommand(params, containerProperties, config, remoteEnv) {
await runPostCommand(params, containerProperties, config, 'postAttachCommand', remoteEnv, true);
await runPostCommands(params, containerProperties, config, 'postAttachCommand', remoteEnv, true);
}
async function runPostCommand({ postCreate }, containerProperties, config, postCommandName, remoteEnv, doRun) {
const postCommand = config[postCommandName];
async function runPostCommands(params, containerProperties, config, postCommandName, remoteEnv, doRun) {
return Promise.all((config[`${postCommandName}s`] || []).map(config => runPostCommand(params, containerProperties, config, postCommandName, remoteEnv, doRun)));
}
async function runPostCommand({ postCreate }, containerProperties, postCommand, postCommandName, remoteEnv, doRun) {
if (doRun && postCommand && (typeof postCommand === 'string' ? postCommand.trim() : postCommand.length)) {

@@ -499,3 +508,3 @@ const progressName = `Running ${postCommandName}...`;

var _a;
let { userEnvProbe } = config || {};
let userEnvProbe = config === null || config === void 0 ? void 0 : config.userEnvProbe;
params.output.write(`userEnvProbe: ${userEnvProbe || params.defaultUserEnvProbe}${userEnvProbe ? '' : ' (default)'}`);

@@ -502,0 +511,0 @@ if (!userEnvProbe) {

/// <reference types="node" />
import { URI } from 'vscode-uri';
import { FileHost } from './configurationCommonUtils';
import { Mount } from './containerFeaturesConfiguration';
export declare type DevContainerConfig = DevContainerFromImageConfig | DevContainerFromDockerfileConfig | DevContainerFromDockerComposeConfig;

@@ -42,6 +43,10 @@ export interface PortAttributes {

workspaceMount?: string;
mounts?: string[];
mounts?: (Mount | string)[];
containerEnv?: Record<string, string>;
containerUser?: string;
init?: boolean;
privileged?: boolean;
capAdd?: string[];
securityOpt?: string[];
remoteEnv?: Record<string, string | null>;
containerUser?: string;
remoteUser?: string;

@@ -53,2 +58,3 @@ updateRemoteUserUID?: boolean;

hostRequirements?: HostRequirements;
customizations?: Record<string, any>;
}

@@ -75,6 +81,10 @@ export declare type DevContainerFromDockerfileConfig = {

workspaceMount?: string;
mounts?: string[];
mounts?: (Mount | string)[];
containerEnv?: Record<string, string>;
containerUser?: string;
init?: boolean;
privileged?: boolean;
capAdd?: string[];
securityOpt?: string[];
remoteEnv?: Record<string, string | null>;
containerUser?: string;
remoteUser?: string;

@@ -86,2 +96,3 @@ updateRemoteUserUID?: boolean;

hostRequirements?: HostRequirements;
customizations?: Record<string, any>;
} & ({

@@ -123,2 +134,9 @@ dockerFile: string;

runServices?: string[];
mounts?: (Mount | string)[];
containerEnv?: Record<string, string>;
containerUser?: string;
init?: boolean;
privileged?: boolean;
capAdd?: string[];
securityOpt?: string[];
remoteEnv?: Record<string, string | null>;

@@ -131,2 +149,3 @@ remoteUser?: string;

hostRequirements?: HostRequirements;
customizations?: Record<string, any>;
}

@@ -133,0 +152,0 @@ interface DevContainerVSCodeConfig {

@@ -54,2 +54,3 @@ /// <reference types="node" />

}
export declare function parseMount(str: string): Mount;
export declare type SourceInformation = LocalCacheSourceInformation | GithubSourceInformation | DirectTarballSourceInformation | FilePathSourceInformation | OCISourceInformation;

@@ -56,0 +57,0 @@ interface BaseSourceInformation {

@@ -26,3 +26,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getFeatureValueObject = exports.getFeatureMainValue = exports.getFeatureMainProperty = exports.processFeatureIdentifier = exports.getBackwardCompatibleFeatureId = exports.getFeatureIdType = exports.generateFeaturesConfig = exports.loadV1FeaturesJsonFromDisk = exports.loadFeaturesJson = exports.generateContainerEnvs = exports.getFeatureLayers = exports.getFeatureInstallWrapperScript = exports.getContainerFeaturesBaseDockerFile = exports.getSourceInfoString = exports.getContainerFeaturesFolder = exports.multiStageBuildExploration = exports.collapseFeaturesConfig = exports.DEVCONTAINER_FEATURE_FILE_NAME = exports.V1_DEVCONTAINER_FEATURES_FILE_NAME = void 0;
exports.getFeatureValueObject = exports.getFeatureMainValue = exports.getFeatureMainProperty = exports.processFeatureIdentifier = exports.getBackwardCompatibleFeatureId = exports.getFeatureIdType = exports.generateFeaturesConfig = exports.loadV1FeaturesJsonFromDisk = exports.loadFeaturesJson = exports.generateContainerEnvs = exports.getFeatureLayers = exports.getFeatureInstallWrapperScript = exports.getContainerFeaturesBaseDockerFile = exports.getSourceInfoString = exports.getContainerFeaturesFolder = exports.multiStageBuildExploration = exports.collapseFeaturesConfig = exports.parseMount = exports.DEVCONTAINER_FEATURE_FILE_NAME = exports.V1_DEVCONTAINER_FEATURES_FILE_NAME = void 0;
const jsonc = __importStar(require("jsonc-parser"));

@@ -42,2 +42,8 @@ const path = __importStar(require("path"));

exports.DEVCONTAINER_FEATURE_FILE_NAME = 'devcontainer-feature.json';
function parseMount(str) {
return str.split(',')
.map(s => s.split('='))
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
}
exports.parseMount = parseMount;
function collapseFeaturesConfig(original) {

@@ -106,2 +112,4 @@ const collapsed = {

USER $_DEV_CONTAINERS_IMAGE_USER
#{devcontainerMetadata}
`;

@@ -108,0 +116,0 @@ }

@@ -1,2 +0,2 @@

import { ResolverResult, DockerResolverParameters, BindMountConsistency } from './utils';
import { ResolverResult, DockerResolverParameters, BindMountConsistency, SubstituteConfig } from './utils';
import { Workspace } from '../spec-utils/workspaces';

@@ -10,4 +10,8 @@ import { URI } from 'vscode-uri';

export declare function readDevContainerConfigFile(cliHost: CLIHost, workspace: Workspace | undefined, configFile: URI, mountWorkspaceGitRoot: boolean, output: Log, consistency?: BindMountConsistency, overrideConfigFile?: URI): Promise<{
config: DevContainerConfig;
config: {
config: DevContainerConfig;
raw: DevContainerConfig;
substitute: SubstituteConfig;
};
workspaceConfig: import("./utils").WorkspaceConfiguration;
} | undefined>;

@@ -70,7 +70,8 @@ "use strict";

}
const config = configs.config;
const configWithRaw = configs.config;
const { config } = configWithRaw;
await (0, utils_1.runUserCommand)({ ...params, common: { ...common, output: common.postCreate.output } }, config.initializeCommand, common.postCreate.onDidInput);
let result;
if ((0, utils_1.isDockerFileConfig)(config) || 'image' in config) {
result = await (0, singleContainer_1.openDockerfileDevContainer)(params, config, configs.workspaceConfig, idLabels);
result = await (0, singleContainer_1.openDockerfileDevContainer)(params, configWithRaw, configs.workspaceConfig, idLabels);
}

@@ -81,3 +82,3 @@ else if ('dockerComposeFile' in config) {

}
result = await (0, dockerCompose_1.openDockerComposeDevContainer)(params, workspace, config, idLabels);
result = await (0, dockerCompose_1.openDockerComposeDevContainer)(params, workspace, configWithRaw, idLabels);
}

@@ -101,3 +102,3 @@ else {

const workspaceConfig = await (0, utils_1.getWorkspaceConfiguration)(cliHost, workspace, updated, mountWorkspaceGitRoot, output, consistency);
const config = (0, variableSubstitution_1.substitute)({
const substitute0 = value => (0, variableSubstitution_1.substitute)({
platform: cliHost.platform,

@@ -108,3 +109,4 @@ localWorkspaceFolder: workspace === null || workspace === void 0 ? void 0 : workspace.rootFolderPath,

env: cliHost.env,
}, updated);
}, value);
const config = substitute0(updated);
if (typeof config.workspaceFolder === 'string') {

@@ -118,3 +120,7 @@ workspaceConfig.workspaceFolder = config.workspaceFolder;

return {
config,
config: {
config,
raw: updated,
substitute: substitute0,
},
workspaceConfig,

@@ -121,0 +127,0 @@ };

import { DevContainerConfig } from '../spec-configuration/configuration';
import { ImageDetails } from '../spec-shutdown/dockerUtils';
import { FeaturesConfig } from '../spec-configuration/containerFeaturesConfiguration';
import { DockerResolverParameters } from './utils';
import { DockerResolverParameters, SubstitutedConfig } from './utils';
import { ImageBuildInfo, MergedDevContainerConfig } from './imageMetadata';
export declare const getSafeId: (str: string) => string;
export declare function extendImage(params: DockerResolverParameters, config: DevContainerConfig, imageName: string, pullImageOnError: boolean): Promise<{
export declare function extendImage(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, imageName: string): Promise<{
updatedImageName: string[];
collapsedFeaturesConfig: undefined;
imageMetadata: SubstitutedConfig<import("./imageMetadata").ImageMetadataEntry[]>;
imageDetails: () => Promise<ImageDetails>;
} | {
updatedImageName: string[];
collapsedFeaturesConfig: import("../spec-configuration/containerFeaturesConfiguration").CollapsedFeaturesConfig;
imageDetails: () => Promise<ImageDetails>;
}>;
export declare function getExtendImageBuildInfo(params: DockerResolverParameters, config: DevContainerConfig, baseName: string, imageUser: () => Promise<string>): Promise<{
export declare function getExtendImageBuildInfo(params: DockerResolverParameters, config: SubstitutedConfig<DevContainerConfig>, baseName: string, imageBuildInfo: ImageBuildInfo): Promise<{
featureBuildInfo: {

@@ -24,6 +21,17 @@ dstFolder: string;

};
collapsedFeaturesConfig?: undefined;
} | {
featureBuildInfo: ImageBuildOptions;
collapsedFeaturesConfig: import("../spec-configuration/containerFeaturesConfiguration").CollapsedFeaturesConfig;
} | null>;
} | undefined>;
export declare function generateContainerEnvs(featuresConfig: FeaturesConfig): string;
export declare function getRemoteUserUIDUpdateDetails(params: DockerResolverParameters, config: DevContainerConfig, imageName: string, imageDetails: () => Promise<ImageDetails>, runArgsUser: string | undefined): Promise<{
export interface ImageBuildOptions {
dstFolder: string;
dockerfileContent: string;
overrideTarget: string;
dockerfilePrefixContent: string;
buildArgs: Record<string, string>;
buildKitContexts: Record<string, string>;
}
export declare function getRemoteUserUIDUpdateDetails(params: DockerResolverParameters, mergedConfig: MergedDevContainerConfig, imageName: string, imageDetails: () => Promise<ImageDetails>, runArgsUser: string | undefined): Promise<{
imageName: string;

@@ -33,2 +41,2 @@ remoteUser: string;

} | null>;
export declare function updateRemoteUserUID(params: DockerResolverParameters, config: DevContainerConfig, imageName: string, imageDetails: () => Promise<ImageDetails>, runArgsUser: string | undefined): Promise<string>;
export declare function updateRemoteUserUID(params: DockerResolverParameters, mergedConfig: MergedDevContainerConfig, imageName: string, imageDetails: () => Promise<ImageDetails>, runArgsUser: string | undefined): Promise<string>;

@@ -37,2 +37,3 @@ "use strict";

const commonUtils_1 = require("../spec-common/commonUtils");
const imageMetadata_1 = require("./imageMetadata");
// Escapes environment variable keys.

@@ -49,9 +50,7 @@ //

exports.getSafeId = getSafeId;
async function extendImage(params, config, imageName, pullImageOnError) {
let cache;
async function extendImage(params, config, imageName) {
const { common } = params;
const { cliHost, output } = common;
const imageDetails = () => cache || (cache = (0, utils_1.inspectDockerImage)(params, imageName, pullImageOnError));
const imageUser = async () => (await imageDetails()).Config.User || 'root';
const extendImageDetails = await getExtendImageBuildInfo(params, config, imageName, imageUser);
const imageBuildInfo = await (0, imageMetadata_1.getImageBuildInfoFromImage)(params, imageName, config.substitute, common.experimentalImageMetadata);
const extendImageDetails = await getExtendImageBuildInfo(params, config, imageName, imageBuildInfo);
if (!extendImageDetails || !extendImageDetails.featureBuildInfo) {

@@ -61,4 +60,4 @@ // no feature extensions - return

updatedImageName: [imageName],
collapsedFeaturesConfig: undefined,
imageDetails
imageMetadata: imageBuildInfo.metadata,
imageDetails: async () => imageBuildInfo.imageDetails,
};

@@ -99,20 +98,24 @@ }

}
return { updatedImageName: [updatedImageName], collapsedFeaturesConfig, imageDetails };
return {
updatedImageName: [updatedImageName],
imageMetadata: (0, imageMetadata_1.getDevcontainerMetadata)(imageBuildInfo.metadata, config, (collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || []),
imageDetails: async () => imageBuildInfo.imageDetails,
};
}
exports.extendImage = extendImage;
async function getExtendImageBuildInfo(params, config, baseName, imageUser) {
async function getExtendImageBuildInfo(params, config, baseName, imageBuildInfo) {
// Creates the folder where the working files will be setup.
const tempFolder = await (0, utils_1.createFeaturesTempFolder)(params.common);
const dstFolder = await (0, utils_1.createFeaturesTempFolder)(params.common);
// Extracts the local cache of features.
await createLocalFeatures(params, tempFolder);
await createLocalFeatures(params, dstFolder);
// Processes the user's configuration.
const featuresConfig = await (0, containerFeaturesConfiguration_1.generateFeaturesConfig)(params.common, tempFolder, config, containerFeaturesConfiguration_1.getContainerFeaturesFolder);
const featuresConfig = await (0, containerFeaturesConfiguration_1.generateFeaturesConfig)(params.common, dstFolder, config.config, containerFeaturesConfiguration_1.getContainerFeaturesFolder);
if (!featuresConfig) {
return null;
return { featureBuildInfo: getImageBuildOptions(params, config, dstFolder, baseName, imageBuildInfo) };
}
// Generates the end configuration.
const collapsedFeaturesConfig = (0, containerFeaturesConfiguration_1.collapseFeaturesConfig)(featuresConfig);
const featureBuildInfo = await getContainerFeaturesBuildInfo(params, featuresConfig, baseName, imageUser);
const featureBuildInfo = await getFeaturesBuildOptions(params, config, featuresConfig, baseName, imageBuildInfo);
if (!featureBuildInfo) {
return null;
return undefined;
}

@@ -177,3 +180,20 @@ return { featureBuildInfo, collapsedFeaturesConfig };

}
async function getContainerFeaturesBuildInfo(params, featuresConfig, baseName, imageUser) {
function getImageBuildOptions(params, config, dstFolder, baseName, imageBuildInfo) {
return {
dstFolder,
dockerfileContent: `
FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage
${(0, imageMetadata_1.getDevcontainerMetadataLabel)(imageBuildInfo.metadata, config, [], params.common.experimentalImageMetadata)}
`,
overrideTarget: 'dev_containers_target_stage',
dockerfilePrefixContent: `
ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder
`,
buildArgs: {
_DEV_CONTAINERS_BASE_IMAGE: baseName,
},
buildKitContexts: {},
};
}
async function getFeaturesBuildOptions(params, devContainerConfig, featuresConfig, baseName, imageBuildInfo) {
const { common } = params;

@@ -184,3 +204,3 @@ const { cliHost, output } = common;

output.write('dstFolder is undefined or empty in addContainerFeatures', log_1.LogLevel.Error);
return null;
return undefined;
}

@@ -219,3 +239,4 @@ const buildStageScripts = await Promise.all(featuresConfig.featureSets

.replace('#{containerEnv}', generateContainerEnvs(featuresConfig))
.replace('#{copyFeatureBuildStages}', getCopyFeatureBuildStages(featuresConfig, buildStageScripts));
.replace('#{copyFeatureBuildStages}', getCopyFeatureBuildStages(featuresConfig, buildStageScripts))
.replace('#{devcontainerMetadata}', (0, imageMetadata_1.getDevcontainerMetadataLabel)(imageBuildInfo.metadata, devContainerConfig, featuresConfig, common.experimentalImageMetadata));
const dockerfilePrefixContent = `${useBuildKitBuildContexts ? '# syntax=docker/dockerfile:1.4' : ''}

@@ -292,3 +313,3 @@ ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder

_DEV_CONTAINERS_BASE_IMAGE: baseName,
_DEV_CONTAINERS_IMAGE_USER: await imageUser(),
_DEV_CONTAINERS_IMAGE_USER: imageBuildInfo.user,
_DEV_CONTAINERS_FEATURE_CONTENT_SOURCE: buildContentImageName,

@@ -345,10 +366,12 @@ },

}
async function getRemoteUserUIDUpdateDetails(params, config, imageName, imageDetails, runArgsUser) {
async function getRemoteUserUIDUpdateDetails(params, mergedConfig, imageName, imageDetails, runArgsUser) {
const { common } = params;
const { cliHost } = common;
if (params.updateRemoteUserUIDDefault === 'never' || !(typeof config.updateRemoteUserUID === 'boolean' ? config.updateRemoteUserUID : params.updateRemoteUserUIDDefault === 'on') || !(cliHost.platform === 'linux' || params.updateRemoteUserUIDOnMacOS && cliHost.platform === 'darwin')) {
const { updateRemoteUserUID } = mergedConfig;
if (params.updateRemoteUserUIDDefault === 'never' || !(typeof updateRemoteUserUID === 'boolean' ? updateRemoteUserUID : params.updateRemoteUserUIDDefault === 'on') || !(cliHost.platform === 'linux' || params.updateRemoteUserUIDOnMacOS && cliHost.platform === 'darwin')) {
return null;
}
const imageUser = (await imageDetails()).Config.User || 'root';
const remoteUser = config.remoteUser || runArgsUser || imageUser;
const details = await imageDetails();
const imageUser = details.Config.User || 'root';
const remoteUser = mergedConfig.remoteUser || runArgsUser || imageUser;
if (remoteUser === 'root' || /^\d+$/.test(remoteUser)) {

@@ -366,6 +389,6 @@ return null;

exports.getRemoteUserUIDUpdateDetails = getRemoteUserUIDUpdateDetails;
async function updateRemoteUserUID(params, config, imageName, imageDetails, runArgsUser) {
async function updateRemoteUserUID(params, mergedConfig, imageName, imageDetails, runArgsUser) {
const { common } = params;
const { cliHost } = common;
const updateDetails = await getRemoteUserUIDUpdateDetails(params, config, imageName, imageDetails, runArgsUser);
const updateDetails = await getRemoteUserUIDUpdateDetails(params, mergedConfig, imageName, imageDetails, runArgsUser);
if (!updateDetails) {

@@ -372,0 +395,0 @@ return imageName;

@@ -7,2 +7,3 @@ import { DockerResolverParameters, UpdateRemoteUserUIDDefault, BindMountConsistency } from './utils';

import { PackageConfiguration } from '../spec-utils/product';
export declare const experimentalImageMetadataDefault = true;
export interface ProvisionOptions {

@@ -41,2 +42,3 @@ dockerPath: string | undefined;

skipPostAttach: boolean;
experimentalImageMetadata: boolean;
}

@@ -43,0 +45,0 @@ export declare function launch(options: ProvisionOptions, disposables: (() => Promise<unknown> | undefined)[]): Promise<{

@@ -26,3 +26,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.createLog = exports.createDockerParams = exports.launch = void 0;
exports.createLog = exports.createDockerParams = exports.launch = exports.experimentalImageMetadataDefault = void 0;
const path = __importStar(require("path"));

@@ -40,2 +40,3 @@ const crypto = __importStar(require("crypto"));

const dockerUtils_1 = require("../spec-shutdown/dockerUtils");
exports.experimentalImageMetadataDefault = true;
async function launch(options, disposables) {

@@ -109,2 +110,3 @@ const params = await createDockerParams(options, disposables);

skipPostAttach: options.skipPostAttach,
experimentalImageMetadata: options.experimentalImageMetadata,
};

@@ -111,0 +113,0 @@ const dockerPath = options.dockerPath || 'docker';

@@ -26,3 +26,3 @@ import yargs, { Argv } from 'yargs';

};
declare function execOptions(y: Argv): yargs.Argv<yargs.Omit<{}, "config" | "user-data-folder" | "docker-path" | "docker-compose-path" | "container-data-folder" | "container-system-data-folder" | "workspace-folder" | "mount-workspace-git-root" | "container-id" | "id-label" | "override-config" | "log-level" | "log-format" | "terminal-columns" | "terminal-rows" | "default-user-env-probe" | "remote-env" | "skip-feature-auto-mapping"> & yargs.InferredOptionTypes<{
declare function execOptions(y: Argv): yargs.Argv<yargs.Omit<{}, "config" | "user-data-folder" | "docker-path" | "docker-compose-path" | "container-data-folder" | "container-system-data-folder" | "workspace-folder" | "mount-workspace-git-root" | "container-id" | "id-label" | "override-config" | "log-level" | "log-format" | "terminal-columns" | "terminal-rows" | "default-user-env-probe" | "remote-env" | "skip-feature-auto-mapping" | "experimental-image-metadata"> & yargs.InferredOptionTypes<{
'user-data-folder': {

@@ -109,2 +109,8 @@ type: "string";

};
'experimental-image-metadata': {
type: "boolean";
default: boolean;
hidden: true;
description: string;
};
}> & {

@@ -116,3 +122,3 @@ cmd: string;

export declare type ExecArgs = UnpackArgv<ReturnType<typeof execOptions>>;
export declare function doExec({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, _: restArgs, }: ExecArgs & {
export declare function doExec({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'experimental-image-metadata': experimentalImageMetadata, _: restArgs, }: ExecArgs & {
_?: string[];

@@ -119,0 +125,0 @@ }): Promise<{

@@ -55,2 +55,3 @@ "use strict";

const product_1 = require("../spec-utils/product");
const imageMetadata_1 = require("./imageMetadata");
const defaultDefaultUserEnvProbe = 'loginInteractiveShell';

@@ -120,2 +121,3 @@ const mountRegex = /^type=(bind|volume),source=([^,]+),target=([^,]+)(?:,external=(true|false))?$/;

'skip-post-attach': { type: 'boolean', default: false, description: 'Do not run postAttachCommand.' },
'experimental-image-metadata': { type: 'boolean', default: devContainers_1.experimentalImageMetadataDefault, hidden: true, description: 'Temporary option for testing.' },
})

@@ -147,3 +149,3 @@ .check(argv => {

}
async function provision({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'workspace-mount-consistency': workspaceMountConsistency, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'id-label': idLabel, config, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'update-remote-user-uid-default': updateRemoteUserUIDDefault, 'remove-existing-container': removeExistingContainer, 'build-no-cache': buildNoCache, 'expect-existing-container': expectExistingContainer, 'skip-post-create': skipPostCreate, 'skip-non-blocking-commands': skipNonBlocking, prebuild, mount, 'remote-env': addRemoteEnv, 'cache-from': addCacheFrom, 'buildkit': buildkit, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'skip-post-attach': skipPostAttach, }) {
async function provision({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'workspace-mount-consistency': workspaceMountConsistency, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'id-label': idLabel, config, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'update-remote-user-uid-default': updateRemoteUserUIDDefault, 'remove-existing-container': removeExistingContainer, 'build-no-cache': buildNoCache, 'expect-existing-container': expectExistingContainer, 'skip-post-create': skipPostCreate, 'skip-non-blocking-commands': skipNonBlocking, prebuild, mount, 'remote-env': addRemoteEnv, 'cache-from': addCacheFrom, 'buildkit': buildkit, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'skip-post-attach': skipPostAttach, 'experimental-image-metadata': experimentalImageMetadata, }) {
const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : undefined;

@@ -192,2 +194,3 @@ const addRemoteEnvs = addRemoteEnv ? (Array.isArray(addRemoteEnv) ? addRemoteEnv : [addRemoteEnv]) : [];

skipPostAttach,
experimentalImageMetadata,
};

@@ -249,2 +252,3 @@ const result = await doProvision(options);

'skip-feature-auto-mapping': { type: 'boolean', default: false, hidden: true, description: 'Temporary option for testing.' },
'experimental-image-metadata': { type: 'boolean', default: devContainers_1.experimentalImageMetadataDefault, hidden: true, description: 'Temporary option for testing.' },
});

@@ -262,3 +266,3 @@ }

}
async function doBuild({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'workspace-folder': workspaceFolderArg, 'log-level': logLevel, 'log-format': logFormat, 'no-cache': buildNoCache, 'image-name': argImageName, 'cache-from': addCacheFrom, 'buildkit': buildkit, 'platform': buildxPlatform, 'push': buildxPush, 'skip-feature-auto-mapping': skipFeatureAutoMapping, }) {
async function doBuild({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'workspace-folder': workspaceFolderArg, 'log-level': logLevel, 'log-format': logFormat, 'no-cache': buildNoCache, 'image-name': argImageName, 'cache-from': addCacheFrom, 'buildkit': buildkit, 'platform': buildxPlatform, 'push': buildxPush, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'experimental-image-metadata': experimentalImageMetadata, }) {
const disposables = [];

@@ -304,2 +308,3 @@ const dispose = async () => {

skipPostAttach: true,
experimentalImageMetadata,
}, disposables);

@@ -317,3 +322,4 @@ const { common, dockerCLI, dockerComposeCLI } = params;

}
const { config } = configs;
const configWithRaw = configs.config;
const { config } = configWithRaw;
let imageNameResult = [''];

@@ -324,3 +330,3 @@ // Support multiple use of `--image-name`

// Build the base image and extend with features etc.
let { updatedImageName } = await (0, singleContainer_1.buildNamedImageAndExtend)(params, config, imageNames);
let { updatedImageName } = await (0, singleContainer_1.buildNamedImageAndExtend)(params, configWithRaw, imageNames);
if (imageNames) {

@@ -356,5 +362,5 @@ if (!buildxPush) {

const infoParams = { ...params, common: { ...params.common, output: (0, log_1.makeLog)(buildParams.output, log_1.LogLevel.Info) } };
await (0, dockerCompose_1.buildAndExtendDockerCompose)(config, projectName, infoParams, composeFiles, envFile, composeGlobalArgs, [config.service], params.buildNoCache || false, params.common.persistedFolder, 'docker-compose.devcontainer.build', addCacheFroms);
const { overrideImageName } = await (0, dockerCompose_1.buildAndExtendDockerCompose)(configWithRaw, projectName, infoParams, composeFiles, envFile, composeGlobalArgs, [config.service], params.buildNoCache || false, params.common.persistedFolder, 'docker-compose.devcontainer.build', addCacheFroms);
const service = composeConfig.services[config.service];
const originalImageName = service.image || (0, dockerCompose_1.getDefaultImageName)(await buildParams.dockerComposeCLI(), projectName, config.service);
const originalImageName = overrideImageName || service.image || (0, dockerCompose_1.getDefaultImageName)(await buildParams.dockerComposeCLI(), projectName, config.service);
if (imageNames) {

@@ -369,4 +375,4 @@ await Promise.all(imageNames.map(imageName => (0, dockerUtils_1.dockerPtyCLI)(params, 'tag', originalImageName, imageName)));

else {
await (0, dockerUtils_1.dockerPtyCLI)(params, 'pull', config.image);
const { updatedImageName } = await (0, containerFeatures_1.extendImage)(params, config, config.image, 'image' in config);
await (0, utils_1.inspectDockerImage)(params, config.image, true);
const { updatedImageName } = await (0, containerFeatures_1.extendImage)(params, configWithRaw, config.image);
if (buildxPlatform || buildxPush) {

@@ -430,2 +436,3 @@ throw new errors_1.ContainerError({ description: '--platform or --push require dockerfilePath.' });

'skip-post-attach': { type: 'boolean', default: false, description: 'Do not run postAttachCommand.' },
'experimental-image-metadata': { type: 'boolean', default: devContainers_1.experimentalImageMetadataDefault, hidden: true, description: 'Temporary option for testing.' },
})

@@ -454,3 +461,3 @@ .check(argv => {

}
async function doRunUserCommands({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'skip-non-blocking-commands': skipNonBlocking, prebuild, 'stop-for-personalization': stopForPersonalization, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'skip-post-attach': skipPostAttach, }) {
async function doRunUserCommands({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'skip-non-blocking-commands': skipNonBlocking, prebuild, 'stop-for-personalization': stopForPersonalization, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'skip-post-attach': skipPostAttach, 'experimental-image-metadata': experimentalImageMetadata, }) {
const disposables = [];

@@ -497,2 +504,3 @@ const dispose = async () => {

skipPostAttach,
experimentalImageMetadata,
}, disposables);

@@ -515,4 +523,6 @@ const { common } = params;

}
const containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, config.remoteUser);
const updatedConfig = (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, config.configFilePath, containerProperties.env, config);
const imageMetadata = (0, imageMetadata_1.getImageMetadataFromContainer)(container, config, [], experimentalImageMetadata, output).config;
const mergedConfig = (0, imageMetadata_1.mergeConfiguration)(config.config, imageMetadata);
const containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, mergedConfig.remoteUser);
const updatedConfig = (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, config.config.configFilePath, containerProperties.env, mergedConfig);
const remoteEnv = (0, injectHeadless_1.probeRemoteEnv)(common, containerProperties, updatedConfig);

@@ -559,3 +569,5 @@ const result = await (0, injectHeadless_1.runPostCreateCommands)(common, containerProperties, updatedConfig, remoteEnv, stopForPersonalization);

'include-features-configuration': { type: 'boolean', default: false, description: 'Include features configuration.' },
'include-merged-configuration': { type: 'boolean', default: false, description: 'Include merged configuration.' },
'skip-feature-auto-mapping': { type: 'boolean', default: false, hidden: true, description: 'Temporary option for testing.' },
'experimental-image-metadata': { type: 'boolean', default: devContainers_1.experimentalImageMetadataDefault, hidden: true, description: 'Temporary option for testing.' },
})

@@ -575,3 +587,3 @@ .check(argv => {

// 'user-data-folder': persistedFolder,
'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, config: configParam, 'override-config': overrideConfig, 'container-id': containerId, 'id-label': idLabel, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'include-features-configuration': includeFeaturesConfig, 'skip-feature-auto-mapping': skipFeatureAutoMapping, }) {
'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, config: configParam, 'override-config': overrideConfig, 'container-id': containerId, 'id-label': idLabel, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'include-features-configuration': includeFeaturesConfig, 'include-merged-configuration': includeMergedConfig, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'experimental-image-metadata': experimentalImageMetadata, }) {
const disposables = [];

@@ -607,3 +619,3 @@ const dispose = async () => {

}
let { config: configuration } = configs;
let configuration = configs.config;
const dockerCLI = dockerPath || 'docker';

@@ -624,10 +636,32 @@ const dockerComposeCLI = (0, dockerCompose_1.dockerComposeCLIConfig)({

if (container) {
configuration = (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, configuration.configFilePath, (0, utils_1.envListToObj)(container.Config.Env), configuration);
const substitute1 = configuration.substitute;
const substitute2 = config => (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, configuration.config.configFilePath, (0, utils_1.envListToObj)(container.Config.Env), config);
configuration = {
config: substitute2(configuration.config),
raw: configuration.raw,
substitute: config => substitute2(substitute1(config)),
};
}
const featuresConfiguration = includeFeaturesConfig ? await (0, containerFeaturesConfiguration_1.generateFeaturesConfig)({ extensionPath, cwd, output, env: cliHost.env, skipFeatureAutoMapping }, (await (0, utils_1.createFeaturesTempFolder)({ cliHost, package: pkg })), configs.config, containerFeaturesConfiguration_1.getContainerFeaturesFolder) : undefined;
const needsFeaturesConfig = includeFeaturesConfig || (includeMergedConfig && (!container || !experimentalImageMetadata));
const featuresConfiguration = needsFeaturesConfig ? await readFeaturesConfig(params, pkg, configuration.config, extensionPath, skipFeatureAutoMapping) : undefined;
let mergedConfig;
if (includeMergedConfig) {
let imageMetadata;
if (container) {
imageMetadata = (0, imageMetadata_1.getImageMetadataFromContainer)(container, configuration, featuresConfiguration || [], experimentalImageMetadata, output).config;
const substitute2 = config => (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, configuration.config.configFilePath, (0, utils_1.envListToObj)(container.Config.Env), config);
imageMetadata = imageMetadata.map(substitute2);
}
else {
const imageBuildInfo = await (0, imageMetadata_1.getImageBuildInfo)(params, configs.config, experimentalImageMetadata);
imageMetadata = (0, imageMetadata_1.getDevcontainerMetadata)(imageBuildInfo.metadata, configs.config, featuresConfiguration || []).config;
}
mergedConfig = (0, imageMetadata_1.mergeConfiguration)(configuration.config, imageMetadata);
}
await new Promise((resolve, reject) => {
process.stdout.write(JSON.stringify({
configuration,
configuration: configuration.config,
workspace: configs.workspaceConfig,
featuresConfiguration,
mergedConfiguration: mergedConfig,
}) + '\n', err => err ? reject(err) : resolve());

@@ -649,2 +683,8 @@ });

}
async function readFeaturesConfig(params, pkg, config, extensionPath, skipFeatureAutoMapping) {
const { cliHost, output } = params;
const { cwd, env } = cliHost;
const featuresTmpFolder = await (0, utils_1.createFeaturesTempFolder)({ cliHost, package: pkg });
return (0, containerFeaturesConfiguration_1.generateFeaturesConfig)({ extensionPath, cwd, output, env, skipFeatureAutoMapping }, featuresTmpFolder, config, containerFeaturesConfiguration_1.getContainerFeaturesFolder);
}
function execOptions(y) {

@@ -670,2 +710,3 @@ return y.options({

'skip-feature-auto-mapping': { type: 'boolean', default: false, hidden: true, description: 'Temporary option for testing.' },
'experimental-image-metadata': { type: 'boolean', default: devContainers_1.experimentalImageMetadataDefault, hidden: true, description: 'Temporary option for testing.' },
})

@@ -704,3 +745,3 @@ .positional('cmd', {

}
async function doExec({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, _: restArgs, }) {
async function doExec({ 'user-data-folder': persistedFolder, 'docker-path': dockerPath, 'docker-compose-path': dockerComposePath, 'container-data-folder': containerDataFolder, 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, 'container-id': containerId, 'id-label': idLabel, config: configParam, 'override-config': overrideConfig, 'log-level': logLevel, 'log-format': logFormat, 'terminal-rows': terminalRows, 'terminal-columns': terminalColumns, 'default-user-env-probe': defaultUserEnvProbe, 'remote-env': addRemoteEnv, 'skip-feature-auto-mapping': skipFeatureAutoMapping, 'experimental-image-metadata': experimentalImageMetadata, _: restArgs, }) {
const disposables = [];

@@ -748,2 +789,3 @@ const dispose = async () => {

skipPostAttach: false,
experimentalImageMetadata,
}, disposables);

@@ -766,4 +808,6 @@ const { common } = params;

}
const containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, config.remoteUser);
const updatedConfig = (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, config.configFilePath, containerProperties.env, config);
const imageMetadata = (0, imageMetadata_1.getImageMetadataFromContainer)(container, config, [], experimentalImageMetadata, output).config;
const mergedConfig = (0, imageMetadata_1.mergeConfiguration)(config.config, imageMetadata);
const containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, mergedConfig.remoteUser);
const updatedConfig = (0, variableSubstitution_1.containerSubstitute)(cliHost.platform, config.config.configFilePath, containerProperties.env, mergedConfig);
const remoteEnv = (0, injectHeadless_1.probeRemoteEnv)(common, containerProperties, updatedConfig);

@@ -770,0 +814,0 @@ const remoteCwd = containerProperties.remoteWorkspaceFolder || containerProperties.homeFolder;

/// <reference types="node" />
import { ResolverResult, DockerResolverParameters } from './utils';
import { ResolverResult, DockerResolverParameters, SubstitutedConfig } from './utils';
import { Workspace } from '../spec-utils/workspaces';
import { DockerCLIParameters, PartialExecParameters, DockerComposeCLI } from '../spec-shutdown/dockerUtils';
import { DevContainerFromDockerComposeConfig } from '../spec-configuration/configuration';
import { CollapsedFeaturesConfig } from '../spec-configuration/containerFeaturesConfiguration';
import path from 'path';
export declare function openDockerComposeDevContainer(params: DockerResolverParameters, workspace: Workspace, config: DevContainerFromDockerComposeConfig, idLabels: string[]): Promise<ResolverResult>;
export declare function openDockerComposeDevContainer(params: DockerResolverParameters, workspace: Workspace, config: SubstitutedConfig<DevContainerFromDockerComposeConfig>, idLabels: string[]): Promise<ResolverResult>;
export declare function getRemoteWorkspaceFolder(config: DevContainerFromDockerComposeConfig): string;

@@ -19,2 +18,3 @@ export declare function getBuildInfoForService(composeService: any, cliHostPath: typeof path, localComposeFiles: string[]): {

target?: undefined;
args?: undefined;
};

@@ -27,7 +27,9 @@ } | {

target: string | undefined;
args: Record<string, string> | undefined;
};
};
export declare function buildAndExtendDockerCompose(config: DevContainerFromDockerComposeConfig, projectName: string, params: DockerResolverParameters, localComposeFiles: string[], envFile: string | undefined, composeGlobalArgs: string[], runServices: string[], noCache: boolean, overrideFilePath: string, overrideFilePrefix: string, additionalCacheFroms?: string[], noBuild?: boolean): Promise<{
collapsedFeaturesConfig: CollapsedFeaturesConfig | undefined;
export declare function buildAndExtendDockerCompose(configWithRaw: SubstitutedConfig<DevContainerFromDockerComposeConfig>, projectName: string, params: DockerResolverParameters, localComposeFiles: string[], envFile: string | undefined, composeGlobalArgs: string[], runServices: string[], noCache: boolean, overrideFilePath: string, overrideFilePrefix: string, additionalCacheFroms?: string[], noBuild?: boolean): Promise<{
imageMetadata: SubstitutedConfig<import("./imageMetadata").ImageMetadataEntry[]>;
additionalComposeOverrideFiles: string[];
overrideImageName: string | undefined;
}>;

@@ -34,0 +36,0 @@ export declare function getDefaultImageName(dockerComposeCLI: DockerComposeCLI, projectName: string, serviceName: string): string;

@@ -40,4 +40,6 @@ "use strict";

const containerFeatures_1 = require("./containerFeatures");
const product_1 = require("../spec-utils/product");
const containerFeaturesConfiguration_1 = require("../spec-configuration/containerFeaturesConfiguration");
const path_1 = __importDefault(require("path"));
const imageMetadata_1 = require("./imageMetadata");
const dockerfileUtils_1 = require("./dockerfileUtils");
const projectLabel = 'com.docker.compose.project';

@@ -49,8 +51,9 @@ const serviceLabel = 'com.docker.compose.service';

const buildParams = { cliHost, dockerCLI, dockerComposeCLI, env, output };
return _openDockerComposeDevContainer(params, buildParams, workspace, config, getRemoteWorkspaceFolder(config), idLabels);
return _openDockerComposeDevContainer(params, buildParams, workspace, config, getRemoteWorkspaceFolder(config.config), idLabels);
}
exports.openDockerComposeDevContainer = openDockerComposeDevContainer;
async function _openDockerComposeDevContainer(params, buildParams, workspace, config, remoteWorkspaceFolder, idLabels) {
async function _openDockerComposeDevContainer(params, buildParams, workspace, configWithRaw, remoteWorkspaceFolder, idLabels) {
const { common } = params;
const { cliHost: buildCLIHost } = buildParams;
const { config } = configWithRaw;
let container;

@@ -77,3 +80,3 @@ let containerProperties;

if (!container || container.State.Status !== 'running') {
const res = await startContainer(params, buildParams, config, projectName, composeFiles, envFile, container, idLabels);
const res = await startContainer(params, buildParams, configWithRaw, projectName, composeFiles, envFile, container, idLabels);
container = await (0, dockerUtils_1.inspectContainer)(params, res.containerId);

@@ -86,4 +89,6 @@ // collapsedFeaturesConfig = res.collapsedFeaturesConfig;

}
containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, remoteWorkspaceFolder, config.remoteUser);
const { remoteEnv: extensionHostEnv, } = await (0, injectHeadless_1.setupInContainer)(common, containerProperties, config);
const configs = (0, imageMetadata_1.getImageMetadataFromContainer)(container, configWithRaw, [], common.experimentalImageMetadata, common.output).config;
const mergedConfig = (0, imageMetadata_1.mergeConfiguration)(configWithRaw.config, configs);
containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, remoteWorkspaceFolder, mergedConfig.remoteUser);
const { remoteEnv: extensionHostEnv, } = await (0, injectHeadless_1.setupInContainer)(common, containerProperties, mergedConfig);
return {

@@ -151,2 +156,3 @@ params: common,

target: composeBuild.target,
args: composeBuild.args,
}

@@ -156,6 +162,7 @@ };

exports.getBuildInfoForService = getBuildInfoForService;
async function buildAndExtendDockerCompose(config, projectName, params, localComposeFiles, envFile, composeGlobalArgs, runServices, noCache, overrideFilePath, overrideFilePrefix, additionalCacheFroms, noBuild) {
var _a;
async function buildAndExtendDockerCompose(configWithRaw, projectName, params, localComposeFiles, envFile, composeGlobalArgs, runServices, noCache, overrideFilePath, overrideFilePrefix, additionalCacheFroms, noBuild) {
var _a, _b, _c, _d;
const { common, dockerCLI, dockerComposeCLI: dockerComposeCLIFunc } = params;
const { cliHost, env, output } = common;
const { config } = configWithRaw;
const cliParams = { cliHost, dockerCLI, dockerComposeCLI: dockerComposeCLIFunc, env, output };

@@ -180,3 +187,3 @@ const composeConfig = await readDockerComposeConfig(cliParams, localComposeFiles, envFile);

// Find the last line that starts with "FROM" (possibly preceeded by white-space)
const { lastStageName, modifiedDockerfile } = (0, utils_1.ensureDockerfileHasFinalStageName)(dockerfile, baseName);
const { lastStageName, modifiedDockerfile } = (0, dockerfileUtils_1.ensureDockerfileHasFinalStageName)(dockerfile, baseName);
baseName = lastStageName;

@@ -193,7 +200,14 @@ if (modifiedDockerfile) {

const noBuildKitParams = { ...params, buildKitVersion: null }; // skip BuildKit -> can't set additional build contexts with compose
const extendImageBuildInfo = await (0, containerFeatures_1.getExtendImageBuildInfo)(noBuildKitParams, config, baseName, () => (0, utils_1.getImageUser)(params, originalDockerfile));
let buildOverrideContent = null;
const imageBuildInfo = await (0, imageMetadata_1.getImageBuildInfoFromDockerfile)(params, originalDockerfile, ((_a = serviceInfo.build) === null || _a === void 0 ? void 0 : _a.args) || {}, (_b = serviceInfo.build) === null || _b === void 0 ? void 0 : _b.target, configWithRaw.substitute, common.experimentalImageMetadata);
const extendImageBuildInfo = await (0, containerFeatures_1.getExtendImageBuildInfo)(noBuildKitParams, configWithRaw, baseName, imageBuildInfo);
let overrideImageName;
let buildOverrideContent = '';
if (extendImageBuildInfo) {
// Avoid retagging a previously pulled image.
if (!serviceInfo.build) {
overrideImageName = (0, utils_1.getFolderImageName)(common);
buildOverrideContent += ` image: ${overrideImageName}\n`;
}
// Create overridden Dockerfile and generate docker-compose build override content
buildOverrideContent = ' build:\n';
buildOverrideContent += ' build:\n';
const { featureBuildInfo } = extendImageBuildInfo;

@@ -211,3 +225,3 @@ // We add a '# syntax' line at the start, so strip out any existing line

buildOverrideContent += ` target: ${featureBuildInfo.overrideTarget}\n`;
if (!((_a = serviceInfo.build) === null || _a === void 0 ? void 0 : _a.context)) {
if (!((_c = serviceInfo.build) === null || _c === void 0 ? void 0 : _c.context)) {
// need to supply a context as we don't have one inherited

@@ -242,2 +256,3 @@ const emptyDir = (0, utils_1.getEmptyContextFolder)(common);

`;
output.write(`Docker Compose override file for building image:\n${composeOverrideContent}`);
await cliHost.writeFile(composeOverrideFile, Buffer.from(composeOverrideContent));

@@ -277,4 +292,5 @@ additionalComposeOverrideFiles.push(composeOverrideFile);

return {
collapsedFeaturesConfig: extendImageBuildInfo === null || extendImageBuildInfo === void 0 ? void 0 : extendImageBuildInfo.collapsedFeaturesConfig,
imageMetadata: (0, imageMetadata_1.getDevcontainerMetadata)(imageBuildInfo.metadata, configWithRaw, ((_d = extendImageBuildInfo === null || extendImageBuildInfo === void 0 ? void 0 : extendImageBuildInfo.collapsedFeaturesConfig) === null || _d === void 0 ? void 0 : _d.allFeatures) || []),
additionalComposeOverrideFiles,
overrideImageName,
};

@@ -311,3 +327,3 @@ }

}
async function startContainer(params, buildParams, config, projectName, composeFiles, envFile, container, idLabels) {
async function startContainer(params, buildParams, configWithRaw, projectName, composeFiles, envFile, container, idLabels) {
var _a, _b, _c, _d;

@@ -317,2 +333,3 @@ const { common } = params;

const { cliHost: buildCLIHost } = buildParams;
const { config } = configWithRaw;
const featuresBuildOverrideFilePrefix = 'docker-compose.devcontainer.build';

@@ -368,11 +385,13 @@ const featuresStartOverrideFilePrefix = 'docker-compose.devcontainer.containerFeatures';

const infoParams = { ...params, common: { ...params.common, output: infoOutput } };
const { collapsedFeaturesConfig, additionalComposeOverrideFiles } = await buildAndExtendDockerCompose(config, projectName, infoParams, localComposeFiles, envFile, composeGlobalArgs, (_c = config.runServices) !== null && _c !== void 0 ? _c : [], (_d = params.buildNoCache) !== null && _d !== void 0 ? _d : false, persistedFolder, featuresBuildOverrideFilePrefix, params.additionalCacheFroms, noBuild);
const { imageMetadata, additionalComposeOverrideFiles, overrideImageName } = await buildAndExtendDockerCompose(configWithRaw, projectName, infoParams, localComposeFiles, envFile, composeGlobalArgs, (_c = config.runServices) !== null && _c !== void 0 ? _c : [], (_d = params.buildNoCache) !== null && _d !== void 0 ? _d : false, persistedFolder, featuresBuildOverrideFilePrefix, params.additionalCacheFroms, noBuild);
additionalComposeOverrideFiles.forEach(overrideFilePath => composeGlobalArgs.push('-f', overrideFilePath));
const currentImageName = overrideImageName || originalImageName;
let cache;
const imageDetails = () => cache || (cache = (0, utils_1.inspectDockerImage)(params, originalImageName, true));
const updatedImageName = noBuild ? originalImageName : await (0, containerFeatures_1.updateRemoteUserUID)(params, config, originalImageName, imageDetails, service.user);
const imageDetails = () => cache || (cache = (0, utils_1.inspectDockerImage)(params, currentImageName, true));
const mergedConfig = (0, imageMetadata_1.mergeConfiguration)(config, imageMetadata.config);
const updatedImageName = noBuild ? currentImageName : await (0, containerFeatures_1.updateRemoteUserUID)(params, mergedConfig, currentImageName, imageDetails, service.user);
// Save override docker-compose file to disk.
// Persisted folder is a path that will be maintained between sessions
// Note: As a fallback, persistedFolder is set to the build's tmpDir() directory
const overrideFilePath = await writeFeaturesComposeOverrideFile(updatedImageName, originalImageName, collapsedFeaturesConfig, config, buildParams, composeFiles, imageDetails, service, idLabels, params.additionalMounts, persistedFolder, featuresStartOverrideFilePrefix, buildCLIHost, output);
const overrideFilePath = await writeFeaturesComposeOverrideFile(updatedImageName, currentImageName, mergedConfig, config, buildParams, composeFiles, imageDetails, service, idLabels, params.additionalMounts, persistedFolder, featuresStartOverrideFilePrefix, buildCLIHost, output);
if (overrideFilePath) {

@@ -417,7 +436,7 @@ // Add file path to override file as parameter

exports.getDefaultImageName = getDefaultImageName;
async function writeFeaturesComposeOverrideFile(updatedImageName, originalImageName, collapsedFeaturesConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts, overrideFilePath, overrideFilePrefix, buildCLIHost, output) {
const composeOverrideContent = await generateFeaturesComposeOverrideContent(updatedImageName, originalImageName, collapsedFeaturesConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts);
async function writeFeaturesComposeOverrideFile(updatedImageName, originalImageName, mergedConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts, overrideFilePath, overrideFilePrefix, buildCLIHost, output) {
const composeOverrideContent = await generateFeaturesComposeOverrideContent(updatedImageName, originalImageName, mergedConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts);
const overrideFileHasContents = !!composeOverrideContent && composeOverrideContent.length > 0 && composeOverrideContent.trim() !== '';
if (overrideFileHasContents) {
output.write(`Docker Compose override file:\n${composeOverrideContent}`, log_1.LogLevel.Trace);
output.write(`Docker Compose override file for creating container:\n${composeOverrideContent}`);
const fileName = `${overrideFilePrefix}-${Date.now()}.yml`;

@@ -436,24 +455,22 @@ const composeFolder = buildCLIHost.path.join(overrideFilePath, 'docker-compose');

}
async function generateFeaturesComposeOverrideContent(updatedImageName, originalImageName, collapsedFeaturesConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts) {
async function generateFeaturesComposeOverrideContent(updatedImageName, originalImageName, mergedConfig, config, buildParams, composeFiles, imageDetails, service, additionalLabels, additionalMounts) {
const { cliHost: buildCLIHost } = buildParams;
let composeOverrideContent = '';
const overrideImage = updatedImageName !== originalImageName;
const featureCaps = [...new Set([].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.filter(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value)
.map(f => f.capAdd || [])))];
const featureSecurityOpts = [...new Set([].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.filter(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value)
.map(f => f.securityOpt || [])))];
const featureMounts = [].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.map(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.mounts)
.filter(Boolean), additionalMounts);
const volumeMounts = featureMounts.filter(m => m.type === 'volume');
const customEntrypoints = ((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.map(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.entrypoint)
.filter(Boolean);
const user = mergedConfig.containerUser;
const env = mergedConfig.containerEnv || {};
const capAdd = mergedConfig.capAdd || [];
const securityOpts = mergedConfig.securityOpt || [];
const mounts = [
...mergedConfig.mounts || [],
...additionalMounts,
].map(m => typeof m === 'string' ? (0, containerFeaturesConfiguration_1.parseMount)(m) : m);
const volumeMounts = mounts.filter(m => m.type === 'volume');
const customEntrypoints = mergedConfig.entrypoints || [];
const composeEntrypoint = typeof service.entrypoint === 'string' ? shellQuote.parse(service.entrypoint) : service.entrypoint;
const composeCommand = typeof service.command === 'string' ? shellQuote.parse(service.command) : service.command;
const userEntrypoint = config.overrideCommand ? [] : composeEntrypoint /* $ already escaped. */
const { overrideCommand } = mergedConfig;
const userEntrypoint = overrideCommand ? [] : composeEntrypoint /* $ already escaped. */
|| ((await imageDetails()).Config.Entrypoint || []).map(c => c.replace(/\$/g, '$$$$')); // $ > $$ to escape docker-compose.yml's interpolation.
const userCommand = config.overrideCommand ? [] : composeCommand /* $ already escaped. */
const userCommand = overrideCommand ? [] : composeCommand /* $ already escaped. */
|| (composeEntrypoint ? [ /* Ignore image CMD per docker-compose.yml spec. */] : ((await imageDetails()).Config.Cmd || []).map(c => c.replace(/\$/g, '$$$$'))); // $ > $$ to escape docker-compose.yml's interpolation.

@@ -468,12 +485,15 @@ composeOverrideContent = `services:

while sleep 1 & wait $$!; do :; done", "-"${userEntrypoint.map(a => `, ${JSON.stringify(a)}`).join('')}]${userCommand !== composeCommand ? `
command: ${JSON.stringify(userCommand)}` : ''}${((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || []).some(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.init) ? `
init: true` : ''}${((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || []).some(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.privileged) ? `
privileged: true` : ''}${featureCaps.length ? `
cap_add:${featureCaps.map(cap => `
- ${cap}`).join('')}` : ''}${featureSecurityOpts.length ? `
security_opt:${featureSecurityOpts.map(securityOpt => `
command: ${JSON.stringify(userCommand)}` : ''}${mergedConfig.init ? `
init: true` : ''}${user ? `
user: ${user}` : ''}${Object.keys(env).length ? `
environment:${Object.keys(env).map(key => `
- ${key}=${env[key]}`).join('')}` : ''}${mergedConfig.privileged ? `
privileged: true` : ''}${capAdd.length ? `
cap_add:${capAdd.map(cap => `
- ${cap}`).join('')}` : ''}${securityOpts.length ? `
security_opt:${securityOpts.map(securityOpt => `
- ${securityOpt}`).join('')}` : ''}${additionalLabels.length ? `
labels:${additionalLabels.map(label => `
- ${label.replace(/\$/g, '$$$$')}`).join('')}` : ''}${featureMounts.length ? `
volumes:${featureMounts.map(m => `
- ${label.replace(/\$/g, '$$$$')}`).join('')}` : ''}${mounts.length ? `
volumes:${mounts.map(m => `
- ${m.source}:${m.target}`).join('')}` : ''}${volumeMounts.length ? `

@@ -480,0 +500,0 @@ volumes:${volumeMounts.map(m => `

@@ -291,2 +291,3 @@ "use strict";

skipFeatureAutoMapping: common.skipFeatureAutoMapping,
experimentalImageMetadata: common.experimentalImageMetadata,
log: text => quiet ? null : process.stderr.write(text),

@@ -337,2 +338,3 @@ };

'skip-feature-auto-mapping': false,
'experimental-image-metadata': false,
cmd,

@@ -382,4 +384,5 @@ args,

skipPostAttach: false,
experimentalImageMetadata: false,
}, disposables);
}
//# sourceMappingURL=testCommandImpl.js.map

@@ -1,11 +0,11 @@

import { ResolverResult, DockerResolverParameters, WorkspaceConfiguration } from './utils';
import { ResolverResult, DockerResolverParameters, WorkspaceConfiguration, SubstitutedConfig } from './utils';
import { ContainerDetails, DockerCLIParameters, ImageDetails } from '../spec-shutdown/dockerUtils';
import { DevContainerFromDockerfileConfig, DevContainerFromImageConfig } from '../spec-configuration/configuration';
import { Log } from '../spec-utils/log';
import { CollapsedFeaturesConfig } from '../spec-configuration/containerFeaturesConfiguration';
import { MergedDevContainerConfig } from './imageMetadata';
export declare const hostFolderLabel = "devcontainer.local_folder";
export declare function openDockerfileDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, workspaceConfig: WorkspaceConfiguration, idLabels: string[]): Promise<ResolverResult>;
export declare function buildNamedImageAndExtend(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, argImageNames?: string[]): Promise<{
export declare function openDockerfileDevContainer(params: DockerResolverParameters, configWithRaw: SubstitutedConfig<DevContainerFromDockerfileConfig | DevContainerFromImageConfig>, workspaceConfig: WorkspaceConfiguration, idLabels: string[]): Promise<ResolverResult>;
export declare function buildNamedImageAndExtend(params: DockerResolverParameters, configWithRaw: SubstitutedConfig<DevContainerFromDockerfileConfig | DevContainerFromImageConfig>, argImageNames?: string[]): Promise<{
updatedImageName: string[];
collapsedFeaturesConfig: CollapsedFeaturesConfig | undefined;
imageMetadata: SubstitutedConfig<import("./imageMetadata").ImageMetadataEntry[]>;
imageDetails: () => Promise<ImageDetails>;

@@ -16,3 +16,3 @@ }>;

export declare function findDevContainer(params: DockerCLIParameters | DockerResolverParameters, labels: string[]): Promise<ContainerDetails | undefined>;
export declare function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, collapsedFeaturesConfig: CollapsedFeaturesConfig | undefined, imageName: string, labels: string[], workspaceMount: string | undefined, imageDetails: (() => Promise<ImageDetails>) | undefined): Promise<void>;
export declare function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageName: string, labels: string[], workspaceMount: string | undefined, imageDetails: (() => Promise<ImageDetails>) | undefined, containerUser: string | undefined): Promise<void>;
export declare function bailOut(output: Log, message: string): never;

@@ -14,6 +14,8 @@ "use strict";

const containerFeatures_1 = require("./containerFeatures");
const product_1 = require("../spec-utils/product");
const imageMetadata_1 = require("./imageMetadata");
const dockerfileUtils_1 = require("./dockerfileUtils");
exports.hostFolderLabel = 'devcontainer.local_folder'; // used to label containers created from a workspace/folder
async function openDockerfileDevContainer(params, config, workspaceConfig, idLabels) {
async function openDockerfileDevContainer(params, configWithRaw, workspaceConfig, idLabels) {
const { common } = params;
const { config } = configWithRaw;
// let collapsedFeaturesConfig: () => Promise<CollapsedFeaturesConfig | undefined>;

@@ -24,2 +26,3 @@ let container;

container = await findExistingContainer(params, idLabels);
let mergedConfig;
if (container) {

@@ -35,9 +38,14 @@ // let _collapsedFeatureConfig: Promise<CollapsedFeaturesConfig | undefined>;

await startExistingContainer(params, idLabels, container);
const imageMetadata = (0, imageMetadata_1.getImageMetadataFromContainer)(container, configWithRaw, [], common.experimentalImageMetadata, common.output).config;
mergedConfig = (0, imageMetadata_1.mergeConfiguration)(config, imageMetadata);
}
else {
const res = await buildNamedImageAndExtend(params, config);
const updatedImageName = await (0, containerFeatures_1.updateRemoteUserUID)(params, config, res.updatedImageName[0], res.imageDetails, findUserArg(config.runArgs) || config.containerUser);
const res = await buildNamedImageAndExtend(params, configWithRaw);
const imageMetadata = res.imageMetadata.config;
mergedConfig = (0, imageMetadata_1.mergeConfiguration)(config, imageMetadata);
const { containerUser } = mergedConfig;
const updatedImageName = await (0, containerFeatures_1.updateRemoteUserUID)(params, mergedConfig, res.updatedImageName[0], res.imageDetails, findUserArg(config.runArgs) || containerUser);
// collapsedFeaturesConfig = async () => res.collapsedFeaturesConfig;
try {
await spawnDevContainer(params, config, res.collapsedFeaturesConfig, updatedImageName, idLabels, workspaceConfig.workspaceMount, res.imageDetails);
await spawnDevContainer(params, config, mergedConfig, updatedImageName, idLabels, workspaceConfig.workspaceMount, res.imageDetails, containerUser);
}

@@ -53,4 +61,4 @@ finally {

}
containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, config.remoteUser);
return await setupContainer(container, params, containerProperties, config);
containerProperties = await (0, utils_1.createContainerProperties)(params, container.Id, workspaceConfig.workspaceFolder, mergedConfig.remoteUser);
return await setupContainer(container, params, containerProperties, config, mergedConfig);
}

@@ -81,5 +89,5 @@ catch (e) {

}
async function setupContainer(container, params, containerProperties, config) {
async function setupContainer(container, params, containerProperties, config, mergedConfig) {
const { common } = params;
const { remoteEnv: extensionHostEnv, } = await (0, injectHeadless_1.setupInContainer)(common, containerProperties, config);
const { remoteEnv: extensionHostEnv, } = await (0, injectHeadless_1.setupInContainer)(common, containerProperties, mergedConfig);
return {

@@ -100,16 +108,18 @@ params: common,

}
async function buildNamedImageAndExtend(params, config, argImageNames) {
async function buildNamedImageAndExtend(params, configWithRaw, argImageNames) {
var _a;
const { config } = configWithRaw;
const imageNames = argImageNames !== null && argImageNames !== void 0 ? argImageNames : [getDefaultName(config, params)];
params.common.progress(injectHeadless_1.ResolverProgress.BuildingImage);
if ((0, utils_1.isDockerFileConfig)(config)) {
return await buildAndExtendImage(params, config, imageNames, (_a = params.buildNoCache) !== null && _a !== void 0 ? _a : false);
return await buildAndExtendImage(params, configWithRaw, imageNames, (_a = params.buildNoCache) !== null && _a !== void 0 ? _a : false);
}
// image-based dev container - extend
return await (0, containerFeatures_1.extendImage)(params, config, imageNames[0], 'image' in config);
return await (0, containerFeatures_1.extendImage)(params, configWithRaw, imageNames[0]);
}
exports.buildNamedImageAndExtend = buildNamedImageAndExtend;
async function buildAndExtendImage(buildParams, config, baseImageNames, noCache) {
var _a, _b, _c, _d;
async function buildAndExtendImage(buildParams, configWithRaw, baseImageNames, noCache) {
var _a, _b, _c, _d, _e, _f, _g;
const { cliHost, output } = buildParams.common;
const { config } = configWithRaw;
const dockerfileUri = (0, utils_1.getDockerfilePath)(cliHost, config);

@@ -130,3 +140,3 @@ const dockerfilePath = await (0, utils_1.uriToWSLFsPath)(dockerfileUri, cliHost);

// Find the last line that starts with "FROM" (possibly preceeded by white-space)
const { lastStageName, modifiedDockerfile } = (0, utils_1.ensureDockerfileHasFinalStageName)(dockerfile, baseName);
const { lastStageName, modifiedDockerfile } = (0, dockerfileUtils_1.ensureDockerfileHasFinalStageName)(dockerfile, baseName);
baseName = lastStageName;

@@ -137,3 +147,4 @@ if (modifiedDockerfile) {

}
const extendImageBuildInfo = await (0, containerFeatures_1.getExtendImageBuildInfo)(buildParams, config, baseName, () => (0, utils_1.getImageUser)(buildParams, originalDockerfile));
const imageBuildInfo = await (0, imageMetadata_1.getImageBuildInfoFromDockerfile)(buildParams, originalDockerfile, ((_b = config.build) === null || _b === void 0 ? void 0 : _b.args) || {}, (_c = config.build) === null || _c === void 0 ? void 0 : _c.target, configWithRaw.substitute, buildParams.common.experimentalImageMetadata);
const extendImageBuildInfo = await (0, containerFeatures_1.getExtendImageBuildInfo)(buildParams, configWithRaw, baseName, imageBuildInfo);
let finalDockerfilePath = dockerfilePath;

@@ -182,3 +193,3 @@ const additionalBuildArgs = [];

baseImageNames.map(imageName => args.push('-t', imageName));
const target = extendImageBuildInfo ? extendImageBuildInfo.featureBuildInfo.overrideTarget : (_b = config.build) === null || _b === void 0 ? void 0 : _b.target;
const target = extendImageBuildInfo ? extendImageBuildInfo.featureBuildInfo.overrideTarget : (_d = config.build) === null || _d === void 0 ? void 0 : _d.target;
if (target) {

@@ -195,3 +206,3 @@ args.push('--target', target);

else {
const configCacheFrom = (_c = config.build) === null || _c === void 0 ? void 0 : _c.cacheFrom;
const configCacheFrom = (_e = config.build) === null || _e === void 0 ? void 0 : _e.cacheFrom;
if (buildParams.additionalCacheFroms.length || (configCacheFrom && (configCacheFrom === 'string' || configCacheFrom.length))) {

@@ -213,3 +224,3 @@ await (0, utils_1.logUMask)(buildParams);

}
const buildArgs = (_d = config.build) === null || _d === void 0 ? void 0 : _d.args;
const buildArgs = (_f = config.build) === null || _f === void 0 ? void 0 : _f.args;
if (buildArgs) {

@@ -238,3 +249,3 @@ for (const key in buildArgs) {

updatedImageName: baseImageNames,
collapsedFeaturesConfig: extendImageBuildInfo === null || extendImageBuildInfo === void 0 ? void 0 : extendImageBuildInfo.collapsedFeaturesConfig,
imageMetadata: (0, imageMetadata_1.getDevcontainerMetadata)(imageBuildInfo.metadata, configWithRaw, ((_g = extendImageBuildInfo === null || extendImageBuildInfo === void 0 ? void 0 : extendImageBuildInfo.collapsedFeaturesConfig) === null || _g === void 0 ? void 0 : _g.allFeatures) || []),
imageDetails

@@ -293,3 +304,3 @@ };

exports.findDevContainer = findDevContainer;
async function spawnDevContainer(params, config, collapsedFeaturesConfig, imageName, labels, workspaceMount, imageDetails) {
async function spawnDevContainer(params, config, mergedConfig, imageName, labels, workspaceMount, imageDetails, containerUser) {
const { common } = params;

@@ -301,35 +312,27 @@ common.progress(injectHeadless_1.ResolverProgress.StartingContainer);

const cwdMount = workspaceMount ? ['--mount', workspaceMount] : [];
const mounts = config.mounts ? [].concat(...config.mounts.map(m => ['--mount', m])) : [];
const envObj = config.containerEnv;
const containerEnv = envObj ? Object.keys(envObj)
const envObj = mergedConfig.containerEnv || {};
const containerEnv = Object.keys(envObj)
.reduce((args, key) => {
args.push('-e', `${key}=${envObj[key]}`);
return args;
}, []) : [];
const containerUser = config.containerUser ? ['-u', config.containerUser] : [];
}, []);
const containerUserArgs = containerUser ? ['-u', containerUser] : [];
const featureArgs = [];
if (((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || []).some(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.init)) {
if (mergedConfig.init) {
featureArgs.push('--init');
}
if (((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || []).some(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.privileged)) {
if (mergedConfig.privileged) {
featureArgs.push('--privileged');
}
const caps = new Set([].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.filter(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value)
.map(f => f.capAdd || [])));
for (const cap of caps) {
for (const cap of mergedConfig.capAdd || []) {
featureArgs.push('--cap-add', cap);
}
const securityOpts = new Set([].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.filter(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value)
.map(f => f.securityOpt || [])));
for (const securityOpt of securityOpts) {
for (const securityOpt of mergedConfig.securityOpt || []) {
featureArgs.push('--security-opt', securityOpt);
}
const featureMounts = [].concat(...[].concat(...((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.map(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.mounts)
.filter(Boolean), params.additionalMounts).map(m => ['--mount', `type=${m.type},src=${m.source},dst=${m.target}`]));
const customEntrypoints = ((collapsedFeaturesConfig === null || collapsedFeaturesConfig === void 0 ? void 0 : collapsedFeaturesConfig.allFeatures) || [])
.map(f => (product_1.includeAllConfiguredFeatures || f.included) && f.value && f.entrypoint)
.filter(Boolean);
const featureMounts = [].concat(...[
...mergedConfig.mounts || [],
...params.additionalMounts,
].map(m => ['--mount', typeof m === 'string' ? m : `type=${m.type},src=${m.source},dst=${m.target}`]));
const customEntrypoints = mergedConfig.entrypoints || [];
const entrypoint = ['--entrypoint', '/bin/sh'];

@@ -341,3 +344,4 @@ const cmd = ['-c', `echo Container started

while sleep 1 & wait $!; do :; done`, '-']; // `wait $!` allows for the `trap` to run (synchronous `sleep` would not).
if (config.overrideCommand === false && imageDetails) {
const overrideCommand = mergedConfig.overrideCommand;
if (overrideCommand === false && imageDetails) {
const details = await imageDetails();

@@ -354,7 +358,6 @@ cmd.push(...details.Config.Entrypoint || []);

...cwdMount,
...mounts,
...featureMounts,
...getLabels(labels),
...containerEnv,
...containerUser,
...containerUserArgs,
...(config.runArgs || []),

@@ -361,0 +364,0 @@ ...featureArgs,

@@ -8,3 +8,3 @@ /// <reference types="node" />

import { ShellServer } from '../spec-common/shellServer';
import { ContainerDetails, DockerCLIParameters, DockerComposeCLI, ImageDetails } from '../spec-shutdown/dockerUtils';
import { ContainerDetails, DockerCLIParameters, DockerComposeCLI } from '../spec-shutdown/dockerUtils';
import { DevContainerConfig, DevContainerFromDockerfileConfig } from '../spec-configuration/configuration';

@@ -14,2 +14,3 @@ import { Event } from '../spec-utils/event';

import { PackageConfiguration } from '../spec-utils/product';
import { ImageMetadataEntry } from './imageMetadata';
export { getConfigFilePath, getDockerfilePath, isDockerFileConfig, resolveConfigFilePath } from '../spec-configuration/configuration';

@@ -68,6 +69,12 @@ export { uriToFsPath, parentURI } from '../spec-configuration/configurationCommonUtils';

}
export interface SubstitutedConfig<T> {
config: T;
raw: T;
substitute: SubstituteConfig;
}
export declare type SubstituteConfig = <U extends DevContainerConfig | ImageMetadataEntry>(value: U) => U;
export declare function startEventSeen(params: DockerResolverParameters, labels: Record<string, string>, canceled: Promise<void>, output: Log, trace: boolean): Promise<{
started: Promise<void>;
}>;
export declare function inspectDockerImage(params: DockerResolverParameters, imageName: string, pullImageOnError: boolean): Promise<ImageDetails>;
export declare function inspectDockerImage(params: DockerResolverParameters | DockerCLIParameters, imageName: string, pullImageOnError: boolean): Promise<import("../spec-shutdown/dockerUtils").ImageDetails>;
export interface DevContainerAuthority {

@@ -107,7 +114,1 @@ hostPath: string;

export declare function getEmptyContextFolder(common: ResolverParameters): string;
export declare function getImageUser(params: DockerResolverParameters, dockerfile: string): Promise<string>;
export declare function internalGetImageUser(inspectDockerImage: (imageName: string) => Promise<ImageDetails>, dockerfile: string): Promise<string>;
export declare function ensureDockerfileHasFinalStageName(dockerfile: string, defaultLastStageName: string): {
lastStageName: string;
modifiedDockerfile: string | undefined;
};

@@ -26,3 +26,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureDockerfileHasFinalStageName = exports.internalGetImageUser = exports.getImageUser = exports.getEmptyContextFolder = exports.getLocalCacheFolder = exports.getCacheFolder = exports.createFeaturesTempFolder = exports.getFolderHash = exports.getFolderImageName = exports.runUserCommand = exports.envListToObj = exports.createContainerProperties = exports.getDockerContextPath = exports.getTunnelInformation = exports.getWorkspaceConfiguration = exports.getHostMountFolder = exports.isDevContainerAuthority = exports.inspectDockerImage = exports.startEventSeen = exports.logUMask = exports.uriToWSLFsPath = exports.RemoteDocuments = exports.fileDocuments = exports.createDocuments = exports.CLIHostDocuments = exports.parentURI = exports.uriToFsPath = exports.resolveConfigFilePath = exports.isDockerFileConfig = exports.getDockerfilePath = exports.getConfigFilePath = void 0;
exports.getEmptyContextFolder = exports.getLocalCacheFolder = exports.getCacheFolder = exports.createFeaturesTempFolder = exports.getFolderHash = exports.getFolderImageName = exports.runUserCommand = exports.envListToObj = exports.createContainerProperties = exports.getDockerContextPath = exports.getTunnelInformation = exports.getWorkspaceConfiguration = exports.getHostMountFolder = exports.isDevContainerAuthority = exports.inspectDockerImage = exports.startEventSeen = exports.logUMask = exports.uriToWSLFsPath = exports.RemoteDocuments = exports.fileDocuments = exports.createDocuments = exports.CLIHostDocuments = exports.parentURI = exports.uriToFsPath = exports.resolveConfigFilePath = exports.isDockerFileConfig = exports.getDockerfilePath = exports.getConfigFilePath = void 0;
const path = __importStar(require("path"));

@@ -154,7 +154,8 @@ const crypto = __importStar(require("crypto"));

catch (_err) {
const output = 'cliHost' in params ? params.output : params.common.output;
if (err.stdout) {
params.common.output.write(err.stdout.toString());
output.write(err.stdout.toString());
}
if (err.stderr) {
params.common.output.write((0, errors_1.toErrorText)(err.stderr.toString()));
output.write((0, errors_1.toErrorText)(err.stderr.toString()));
}

@@ -338,59 +339,2 @@ throw err;

exports.getEmptyContextFolder = getEmptyContextFolder;
const findFromLines = new RegExp(/^(?<line>\s*FROM.*)/, 'gm');
const parseFromLine = /FROM\s+(?<platform>--platform=\S+\s+)?(?<image>\S+)(\s+[Aa][Ss]\s+(?<label>[^\s]+))?/;
const findUserLines = new RegExp(/^\s*USER\s+(?<user>\S+)/, 'gm');
async function getImageUser(params, dockerfile) {
return internalGetImageUser(imageName => inspectDockerImage(params, imageName, true), dockerfile);
}
exports.getImageUser = getImageUser;
async function internalGetImageUser(inspectDockerImage, dockerfile) {
var _a, _b, _c, _d;
// TODO: Other targets.
const userLines = [...dockerfile.matchAll(findUserLines)];
if (userLines.length) {
const user = (_a = userLines[userLines.length - 1].groups) === null || _a === void 0 ? void 0 : _a.user;
if (user && user.indexOf('$') === -1) { // Ignore variables.
return user;
}
}
const fromLine = [...dockerfile.matchAll(findFromLines)][0];
const fromMatch = (_c = (_b = fromLine === null || fromLine === void 0 ? void 0 : fromLine.groups) === null || _b === void 0 ? void 0 : _b.line) === null || _c === void 0 ? void 0 : _c.match(parseFromLine);
const imageName = (_d = fromMatch === null || fromMatch === void 0 ? void 0 : fromMatch.groups) === null || _d === void 0 ? void 0 : _d.image;
if (!(imageName && imageName.indexOf('$') === -1)) { // Ignore variables.
return 'root';
}
const imageDetails = await inspectDockerImage(imageName);
return imageDetails.Config.User || 'root';
}
exports.internalGetImageUser = internalGetImageUser;
// not expected to be called externally (exposed for testing)
function ensureDockerfileHasFinalStageName(dockerfile, defaultLastStageName) {
var _a, _b;
// Find the last line that starts with "FROM" (possibly preceeded by white-space)
const fromLines = [...dockerfile.matchAll(findFromLines)];
const lastFromLineMatch = fromLines[fromLines.length - 1];
const lastFromLine = (_a = lastFromLineMatch.groups) === null || _a === void 0 ? void 0 : _a.line;
// Test for "FROM [--platform=someplat] base [as label]"
// That is, match against optional platform and label
const fromMatch = lastFromLine.match(parseFromLine);
if (!fromMatch) {
throw new Error('Error parsing Dockerfile: failed to parse final FROM line');
}
if ((_b = fromMatch.groups) === null || _b === void 0 ? void 0 : _b.label) {
return {
lastStageName: fromMatch.groups.label,
modifiedDockerfile: undefined,
};
}
// Last stage doesn't have a name, so modify the Dockerfile to set the name to defaultLastStageName
const lastLineStartIndex = lastFromLineMatch.index + fromMatch.index;
const lastLineEndIndex = lastLineStartIndex + lastFromLine.length;
const matchedFromText = fromMatch[0];
let modifiedDockerfile = dockerfile.slice(0, lastLineStartIndex + matchedFromText.length);
modifiedDockerfile += ` AS ${defaultLastStageName}`;
const remainingFromLineLength = lastFromLine.length - matchedFromText.length;
modifiedDockerfile += dockerfile.slice(lastLineEndIndex - remainingFromLineLength);
return { lastStageName: defaultLastStageName, modifiedDockerfile: modifiedDockerfile };
}
exports.ensureDockerfileHasFinalStageName = ensureDockerfileHasFinalStageName;
//# sourceMappingURL=utils.js.map

@@ -107,3 +107,3 @@ /// <reference types="node" />

export declare function isPodman(params: DockerCLIParameters | DockerResolverParameters): Promise<boolean>;
export declare function dockerPtyCLI(params: PartialPtyExecParameters | DockerResolverParameters, ...args: string[]): Promise<{
export declare function dockerPtyCLI(params: PartialPtyExecParameters | DockerResolverParameters | DockerCLIParameters, ...args: string[]): Promise<{
cmdOutput: string;

@@ -110,0 +110,0 @@ }>;

{
"name": "@devcontainers/cli",
"description": "Dev Containers CLI",
"version": "0.15.0",
"version": "0.16.0",
"bin": {

@@ -6,0 +6,0 @@ "devcontainer": "devcontainer.js"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc