get2knowio / get2knowio/deacon

fix(build): no `# syntax=docker/dockerfile:1.4` injection on Docker engines older than 23.0.0, where the reference injects one

Open
#732 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2
Forks
0
Avg merge
2h 36m
Merged PRs (30d)
78

Description

## What is missing

The reference computes a `dockerfilePrefixContent` for the Dockerfile it generates to install Features (`src/spec-node/containerFeatures.ts:272-277`, pinned oracle `v0.87.0`), and it is a four-way decision:

```js
const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax;
const omitSyntaxDirective = common.omitSyntaxDirective;
const dockerfilePrefixContent = `${
omitSyntaxDirective ? ''
: skipDefaultSyntax ? (syntax ? `# syntax=${syntax}` : '')
: useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4'
: syntax ? `# syntax=${syntax}` : ''}
ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder
`;
```

deacon emits no syntax directive of its own. It preserves whatever the user wrote, because `merge_dockerfile_with_feature_stage` copies the base document verbatim and a `# syntax=` line stays on line 1.

## Reachability, measured rather than assumed

The injecting branch requires **all three**:

1. `useBuildKitBuildContexts` — BuildKit >= 0.8.0;
2. `!skipDefaultSyntax` — and `skipDefaultSyntax = engine >= 23.0.0` (`containerFeatures.ts:249-250`, `minDockerEngineVersion = [23, 0, 0]`). So the **Docker engine must be older than 23.0.0** (February 2023), or its version unparseable;
3. the user's Dockerfile must not already declare a frontend >= 1.4.

**On a current engine there is no divergence, and this was measured, not reasoned.** A fixture whose Dockerfile pins `# syntax=docker/dockerfile:1.2` and declares a local Feature was built with both CLIs on docker 29.7.2:

- deacon: `ERROR: failed to build: current frontend does not support --build-context. Named contexts are supported since Dockerfile v1.4.`
- reference `@devcontainers/cli@0.87.0`: **the identical BuildKit error**, `{"outcome":"error"}`.

Both honor the user's pinned 1.2 frontend and both fail. The hypothesis that deacon fails where the reference succeeds on a modern setup is **refuted**.

**It is also inert on podman.** `podman version --format '{{.Server.Version}}'` reports `4.9.3`, which is below 23.0.0, so the reference takes the injecting branch there — but `# syntax=` selects a BuildKit *frontend image*, which buildah does not fetch, so emitting it changes nothing on podman.

So the divergence is confined to: **BuildKit >= 0.8 with a Docker engine < 23.0.0.** There, a Dockerfile with no syntax directive (or one below 1.4) builds under the reference and fails under deacon.

## What has already landed

`supports_build_contexts` — the reference's `supportsBuildContexts` (`dockerfileUtils.ts:263`) — is now ported and measured, so the decision this gap needs is available. `crates/core/tests/dockerfile_utils_parity.rs` asserts it across all 91 cases, 26 of which are upstream's own `supportsBuildContexts` table, at zero divergences. That closes the [#686](https://github.com/get2knowio/deacon/issues/686) full-port obligation for this function.

What remains is only the **caller**.

## Why it was not implemented with the port

Implementing it means reading two versions deacon does not read today — `docker version --format '{{.Server.Version}}'` and `docker buildx version` — on the build path, to gate a branch that cannot fire on any environment available for testing here. The decision could be table-tested hermetically against the oracle's expression, but the end-to-end consequence on a pre-23 engine would ship unverified. That trade was taken deliberately and is recorded rather than silently skipped.

## Acceptance

- [ ] The prefix decision matches the reference's four-way expression, including `omitSyntaxDirective` and `skipDefaultSyntax`, table-tested against the oracle.
- [ ] The engine and BuildKit versions are read where the reference reads them, and the branch is gated on the same thresholds.
- [ ] Some end-to-end evidence on an engine older than 23.0.0 (docker-in-docker at `docker:20.10-dind` is the obvious vehicle and needs a privileged runner), or an explicit note in the ledger row that it remains unmeasured.
- [ ] The `parity/SPEC_STATUS.md` row moves off open nonconformance.

Contributor guide

Open the contributing guide

Research direction

Start with the build path that generates the Dockerfile and compare it with the four-way prefix expression in src/spec-node/containerFeatures.ts:249-277. Read the existing supports_build_contexts coverage in crates/core/tests/dockerfile_utils_parity.rs, then add table tests for the version-gated decision and update parity/SPEC_STATUS.md. Done means the engine and BuildKit thresholds, omitSyntaxDirective handling, and documented end-to-end evidence match the acceptance checklist.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, rust
Domain
build-system, devops
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.