Skip to content

Commit

Permalink
Merge branch 'main' into change-config-layer-to-empty-descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
ych-tnk committed May 10, 2024
2 parents a83bb7e + be1f203 commit 8efda6c
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Notable changes.

## May 2024

### [0.60.0]
- Support project name attribute. (https://github.com/microsoft/vscode-remote-release/issues/512)

## April 2024

### [0.59.1]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@devcontainers/cli",
"description": "Dev Containers CLI",
"version": "0.59.1",
"version": "0.60.0",
"bin": {
"devcontainer": "devcontainer.js"
},
Expand Down
4 changes: 2 additions & 2 deletions src/spec-node/devContainersSpecCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,9 @@ async function doBuild({
if (envFile) {
composeGlobalArgs.push('--env-file', envFile);
}
const projectName = await getProjectName(params, workspace, composeFiles);


const composeConfig = await readDockerComposeConfig(buildParams, composeFiles, envFile);
const projectName = await getProjectName(params, workspace, composeFiles, composeConfig);
const services = Object.keys(composeConfig.services || {});
if (services.indexOf(config.service) === -1) {
throw new Error(`Service '${config.service}' configured in devcontainer.json not found in Docker Compose configuration.`);
Expand Down
23 changes: 13 additions & 10 deletions src/spec-node/dockerCompose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ async function _openDockerComposeDevContainer(params: DockerResolverParameters,
const composeFiles = await getDockerComposeFilePaths(buildCLIHost, config, buildCLIHost.env, buildCLIHost.cwd);
const cwdEnvFile = buildCLIHost.path.join(buildCLIHost.cwd, '.env');
const envFile = Array.isArray(config.dockerComposeFile) && config.dockerComposeFile.length === 0 && await buildCLIHost.isFile(cwdEnvFile) ? cwdEnvFile : undefined;
const projectName = await getProjectName(buildParams, workspace, composeFiles);
const composeConfig = await readDockerComposeConfig(buildParams, composeFiles, envFile);
const projectName = await getProjectName(buildParams, workspace, composeFiles, composeConfig);
const containerId = await findComposeContainer(params, projectName, config.service);
if (params.expectExistingContainer && !containerId) {
throw new ContainerError({ description: 'The expected container does not exist.' });
Expand All @@ -60,7 +61,7 @@ async function _openDockerComposeDevContainer(params: DockerResolverParameters,

// let collapsedFeaturesConfig: CollapsedFeaturesConfig | undefined;
if (!container || container.State.Status !== 'running') {
const res = await startContainer(params, buildParams, configWithRaw, projectName, composeFiles, envFile, container, idLabels, additionalFeatures);
const res = await startContainer(params, buildParams, configWithRaw, projectName, composeFiles, envFile, composeConfig, container, idLabels, additionalFeatures);
container = await inspectContainer(params, res.containerId);
// collapsedFeaturesConfig = res.collapsedFeaturesConfig;
// } else {
Expand Down Expand Up @@ -327,7 +328,8 @@ async function checkForPersistedFile(cliHost: CLIHost, output: Log, files: strin
foundLabel: false
};
}
async function startContainer(params: DockerResolverParameters, buildParams: DockerCLIParameters, configWithRaw: SubstitutedConfig<DevContainerFromDockerComposeConfig>, projectName: string, composeFiles: string[], envFile: string | undefined, container: ContainerDetails | undefined, idLabels: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>) {

async function startContainer(params: DockerResolverParameters, buildParams: DockerCLIParameters, configWithRaw: SubstitutedConfig<DevContainerFromDockerComposeConfig>, projectName: string, composeFiles: string[], envFile: string | undefined, composeConfig: any, container: ContainerDetails | undefined, idLabels: string[], additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>) {
const { common } = params;
const { persistedFolder, output } = common;
const { cliHost: buildCLIHost } = buildParams;
Expand All @@ -337,15 +339,13 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc

common.progress(ResolverProgress.StartingContainer);

const localComposeFiles = composeFiles;
// If dockerComposeFile is an array, add -f <file> in order. https://docs.docker.com/compose/extends/#multiple-compose-files
const composeGlobalArgs = ([] as string[]).concat(...localComposeFiles.map(composeFile => ['-f', composeFile]));
const composeGlobalArgs = ([] as string[]).concat(...composeFiles.map(composeFile => ['-f', composeFile]));
if (envFile) {
composeGlobalArgs.push('--env-file', envFile);
}

const infoOutput = makeLog(buildParams.output, LogLevel.Info);
const composeConfig = await readDockerComposeConfig(buildParams, localComposeFiles, envFile);
const services = Object.keys(composeConfig.services || {});
if (services.indexOf(config.service) === -1) {
throw new ContainerError({ description: `Service '${config.service}' configured in devcontainer.json not found in Docker Compose configuration.`, data: { fileWithError: composeFiles[0] } });
Expand Down Expand Up @@ -391,9 +391,9 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc
if (!container || !didRestoreFromPersistedShare) {
const noBuild = !!container; //if we have an existing container, just recreate override files but skip the build

const versionPrefix = await readVersionPrefix(buildCLIHost, localComposeFiles);
const versionPrefix = await readVersionPrefix(buildCLIHost, composeFiles);
const infoParams = { ...params, common: { ...params.common, output: infoOutput } };
const { imageMetadata, additionalComposeOverrideFiles, overrideImageName, labels } = await buildAndExtendDockerCompose(configWithRaw, projectName, infoParams, localComposeFiles, envFile, composeGlobalArgs, config.runServices ?? [], params.buildNoCache ?? false, persistedFolder, featuresBuildOverrideFilePrefix, versionPrefix, additionalFeatures, true, params.additionalCacheFroms, noBuild);
const { imageMetadata, additionalComposeOverrideFiles, overrideImageName, labels } = await buildAndExtendDockerCompose(configWithRaw, projectName, infoParams, composeFiles, envFile, composeGlobalArgs, config.runServices ?? [], params.buildNoCache ?? false, persistedFolder, featuresBuildOverrideFilePrefix, versionPrefix, additionalFeatures, true, params.additionalCacheFroms, noBuild);
additionalComposeOverrideFiles.forEach(overrideFilePath => composeGlobalArgs.push('-f', overrideFilePath));

const currentImageName = overrideImageName || originalImageName;
Expand Down Expand Up @@ -439,7 +439,7 @@ async function startContainer(params: DockerResolverParameters, buildParams: Doc
description = err.cmdOutput;
}

throw new ContainerError({ description, originalError: err, data: { fileWithError: localComposeFiles[0] } });
throw new ContainerError({ description, originalError: err, data: { fileWithError: composeFiles[0] } });
}

await started;
Expand Down Expand Up @@ -632,7 +632,7 @@ export async function findComposeContainer(params: DockerCLIParameters | DockerR
return list && list[0];
}

export async function getProjectName(params: DockerCLIParameters | DockerResolverParameters, workspace: Workspace, composeFiles: string[]) {
export async function getProjectName(params: DockerCLIParameters | DockerResolverParameters, workspace: Workspace, composeFiles: string[], composeConfig: any) {
const { cliHost } = 'cliHost' in params ? params : params.common;
const newProjectName = await useNewProjectName(params);
const envName = toProjectName(cliHost.env.COMPOSE_PROJECT_NAME || '', newProjectName);
Expand All @@ -653,6 +653,9 @@ export async function getProjectName(params: DockerCLIParameters | DockerResolve
throw err;
}
}
if (composeConfig?.name) {
return toProjectName(composeConfig.name, newProjectName);
}
const configDir = workspace.configFolderPath;
const workingDir = composeFiles[0] ? cliHost.path.dirname(composeFiles[0]) : cliHost.cwd; // From https://github.com/docker/compose/blob/79557e3d3ab67c3697641d9af91866d7e400cfeb/compose/config/config.py#L290
if (equalPaths(cliHost.platform, workingDir, cliHost.path.join(configDir, '.devcontainer'))) {
Expand Down
13 changes: 13 additions & 0 deletions src/test/cli.up.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ describe('Dev Containers CLI', function () {
assert.equal(upResult!.outcome, 'success');
});
});
describe('for docker-compose with image without features with custom project name', () => {
let upResult: UpResult | null = null;
const testFolder = `${__dirname}/configs/compose-with-name`;
before(async () => {
// build and start the container
upResult = await devContainerUp(cli, testFolder, { 'logLevel': 'trace', extraArgs: `--docker-compose-path trigger-compose-v2` });
});
after(async () => await devContainerDown({ composeProjectName: upResult?.composeProjectName }));
it('should succeed', () => {
assert.equal(upResult!.outcome, 'success');
assert.equal(upResult!.composeProjectName, 'custom-project-name');
});
});

// Additional tests to verify the handling of persisted files
describe('for docker-compose with Dockerfile with features', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3.8'

name: custom-project-name

services:
app:
image: ubuntu:latest
volumes:
- ..:/workspace:cached
command: sleep infinity

0 comments on commit 8efda6c

Please sign in to comment.