diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b898c034..a047d330e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Notable changes. +## May 2025 + +### [0.77.0] +- Fix: --uidmap/--gidmap conflict with --userns (https://github.com/microsoft/vscode-remote-release/10954) +- Fix: Omit --userns=keep-id for root (https://github.com/devcontainers/cli/pull/1004) + ## April 2025 ### [0.76.0] diff --git a/package.json b/package.json index 530c6b472..3af1ba169 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.76.0", + "version": "0.77.0", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/src/spec-node/singleContainer.ts b/src/spec-node/singleContainer.ts index d42b837b9..4bb159d4a 100644 --- a/src/spec-node/singleContainer.ts +++ b/src/spec-node/singleContainer.ts @@ -344,7 +344,7 @@ export async function extraRunArgs(common: ResolverParameters, params: DockerRes return extraArguments; } -export async function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageName: string, labels: string[], workspaceMount: string | undefined, imageDetails: (() => Promise) | undefined, containerUser: string | undefined, extraLabels: Record) { +export async function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageName: string, labels: string[], workspaceMount: string | undefined, imageDetails: () => Promise, containerUser: string | undefined, extraLabels: Record) { const { common } = params; common.progress(ResolverProgress.StartingContainer); @@ -392,7 +392,7 @@ ${customEntrypoints.join('\n')} exec "$@" while sleep 1 & wait $!; do :; done`, '-']; // `wait $!` allows for the `trap` to run (synchronous `sleep` would not). const overrideCommand = mergedConfig.overrideCommand; - if (overrideCommand === false && imageDetails) { + if (overrideCommand === false) { const details = await imageDetails(); cmd.push(...details.Config.Entrypoint || []); cmd.push(...details.Config.Cmd || []); @@ -409,7 +409,7 @@ while sleep 1 & wait $!; do :; done`, '-']; // `wait $!` allows for the `trap` t ...getLabels(labels), ...containerEnv, ...containerUserArgs, - ...getPodmanArgs(params, config), + ...await getPodmanArgs(params, config, mergedConfig, imageDetails), ...(config.runArgs || []), ...(await extraRunArgs(common, params, config) || []), ...featureArgs, @@ -434,12 +434,15 @@ while sleep 1 & wait $!; do :; done`, '-']; // `wait $!` allows for the `trap` t common.output.stop(text, start); } -function getPodmanArgs(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig): string[] { +async function getPodmanArgs(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageDetails: () => Promise): Promise { if (params.isPodman && params.common.cliHost.platform === 'linux') { const args = ['--security-opt', 'label=disable']; const hasIdMapping = (config.runArgs || []).some(arg => /--[ug]idmap(=|$)/.test(arg)); if (!hasIdMapping) { - args.push('--userns=keep-id'); + const remoteUser = mergedConfig.remoteUser || findUserArg(config.runArgs) || (await imageDetails()).Config.User || 'root'; + if (remoteUser !== 'root' && remoteUser !== '0') { + args.push('--userns=keep-id'); + } } return args; } diff --git a/src/test/container-features/configs/lockfile/.devcontainer.json b/src/test/container-features/configs/lockfile/.devcontainer.json index 99bc651bc..eb3c40b1c 100644 --- a/src/test/container-features/configs/lockfile/.devcontainer.json +++ b/src/test/container-features/configs/lockfile/.devcontainer.json @@ -3,6 +3,6 @@ "features": { "ghcr.io/codspace/features/flower:1.0.0": {}, "ghcr.io/codspace/features/color:1.0.4": {}, - "https://github.com/codspace/features/releases/download/tarball02/devcontainer-feature-docker-in-docker.tgz": {} + "https://github.com/codspace/tgz-features-with-dependson/releases/download/0.0.2/devcontainer-feature-D.tgz": {} } } diff --git a/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json b/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json index b3271f861..dd3136f9d 100644 --- a/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json @@ -10,10 +10,10 @@ "resolved": "ghcr.io/codspace/features/flower@sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43", "integrity": "sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43" }, - "https://github.com/codspace/features/releases/download/tarball02/devcontainer-feature-docker-in-docker.tgz": { - "version": "1.0.0", - "resolved": "https://github.com/codspace/features/releases/download/tarball02/devcontainer-feature-docker-in-docker.tgz", - "integrity": "sha256:9cf3f2a17c1bb2b599b6027cfa975d2fb28234df88ba33ff5e276fa052aac7ae" + "https://github.com/codspace/tgz-features-with-dependson/releases/download/0.0.2/devcontainer-feature-D.tgz": { + "version": "2.0.0", + "resolved": "https://github.com/codspace/tgz-features-with-dependson/releases/download/0.0.2/devcontainer-feature-D.tgz", + "integrity": "sha256:41607bd6aba3975adcd0641cc479e67b04abd21763ba8a41ea053bcc04a6a818" } } } \ No newline at end of file