devcontainers / devcontainers/cli

Multi-variable ARG declared via line continuation loses its second variable

Đang mở
#1,286 0 bình luận 0 reaction 1 người được giao Được @v-Kaniska244 nhận Xem trên GitHub
Ngôn ngữ chính
TypeScript
Star
3k
Fork
457
Merge trung bình
13 giờ 17 phút
Pull request đã merge (30 ngày)
6

Mô tả

## Summary

When a Dockerfile declares two `ARG`s using a single instruction with a backslash line continuation (valid, real Docker/BuildKit syntax):

```dockerfile
ARG VERSION_UNUSED=x \
VERSION_BASE=latest
FROM alpine:${VERSION_BASE}
```

`docker build` resolves `${VERSION_BASE}` to `latest` correctly. But `@devcontainers/cli`'s own lightweight Dockerfile scanner (used to pre-fetch base-image details before invoking the real build) never sees `VERSION_BASE` as a declared `ARG` at all, so `${VERSION_BASE}` resolves to an **empty string**. The image reference becomes `alpine:` (no tag), which is rejected by the Docker daemon as `invalid reference format`. In our case this made `Reopen in Container` fail outright, before the actual build (which would have succeeded) ever ran.

## Environment

- Dev Containers extension: 0.466.0
- VS Code: 1.134.0
- `@devcontainers/cli` (bundled): 0.88.0
- Docker Desktop: 4.87.0 (Engine 29.7.2)
- macOS (arm64)

## Minimal reproduction

`.devcontainer/Dockerfile`:
```dockerfile
ARG VERSION_UNUSED=x \
VERSION_BASE=latest
FROM alpine:${VERSION_BASE}
```

`.devcontainer/devcontainer.json`:
```jsonc
{
"name": "arg-continuation-repro",
"dockerComposeFile": "../docker-compose.yml",
"service": "web",
"workspaceFolder": "/workspace"
}
```

`docker-compose.yml`:
```yaml
services:
web:
build:
context: .
dockerfile: .devcontainer/Dockerfile
args:
VERSION_BASE: "3.20"
command: ["sleep", "infinity"]
```

Steps:
1. Save the three files above in an otherwise empty folder.
2. Open the folder in VS Code and run **Dev Containers: Reopen in Container**.

## Expected result

The container builds using `alpine:3.20` (from the compose `build.args` override) or `alpine:latest` (the Dockerfile default) — either way, a valid tag.

## Actual result (from our real project, same root cause, different image name)

```
[...] Start: Run: docker inspect --type image futureys/gatsby:
[...] Stop (11 ms): Run: docker inspect --type image futureys/gatsby:
[...] Error fetching image details: No manifest found for docker.io/futureys/gatsby:.
[...] Start: Run: docker pull futureys/gatsby:
[...] invalid reference format
[...] Stop (137 ms): Run: docker pull futureys/gatsby:
[...] Retrying (Attempt 0) with error
'Command failed: docker pull futureys/gatsby:
'
... (retries 1-4, same error) ...
[...] Command failed: docker inspect --type image futureys/gatsby:
[...] []
Error response from daemon: invalid reference format

[...] Command failed: docker pull futureys/gatsby:
Error: Command failed: docker pull futureys/gatsby:
```

`Reopen in Container` fails with this error before the real `docker compose build` is ever invoked, even though `docker build` on the same Dockerfile (outside of Dev Containers) succeeds and resolves the ARG correctly.

## Root cause

The bundled `devContainersSpecCLI.js` parses ARG/ENV/USER declarations with this regex (used to compute the default value of an `ARG` referenced in `FROM ...:${ARG}` before the real build runs, e.g. to fetch base-image metadata):

```
/^\s*(?ARG|ENV|USER)\s+(?[^\s=]+)([ =]+("(?\S+)"|(?\S+)))?/gmi
```

Because of the `m` (multiline) flag, `^` anchors to the start of *each physical line*. For:

```dockerfile
ARG VERSION_UNUSED=x \
VERSION_BASE=latest
```

only the first line matches (`name=VERSION_UNUSED`). The second physical line (` VERSION_BASE=latest`) doesn't start with `ARG`/`ENV`/`USER`, so it's invisible to this scanner — even though real Docker/BuildKit joins the two lines via the trailing `\` into one `ARG` instruction that declares **both** variables (verified: `docker build` on the same Dockerfile resolves `${VERSION_BASE}` to `latest`/the passed `--build-arg` correctly).

Downstream, the variable-resolution walk (roughly `Iv` → `wg` → `LV` → `Cv` in the minified bundle) looks for an instruction whose `name` matches the referenced variable. Since no such instruction was ever recorded for the second, continuation-declared name, the lookup falls through and resolves to `undefined` → `""`, which gets substituted into the `FROM` line, producing `image:` with no tag.

## Workaround

Declare each `ARG` on its own line instead of combining them into one instruction via `\` continuation:

```dockerfile
ARG VERSION_UNUSED=x
ARG VERSION_BASE=latest
```

This is recognized correctly by both real Docker/BuildKit and the extension's scanner.

## Suggested fix

The Dockerfile-instruction regex (and/or the code that walks continuation lines) should join `\`-continued lines before matching, the same way BuildKit's own parser does — or, at minimum, should recognize additional `name[=value]` pairs appearing on continuation lines of an `ARG`/`ENV` instruction.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.