Skip to content

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

Description

@pofallon

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:

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions