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
- 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
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