<#23470 Docker: FROM `$ARG` base-image build args ...
# github-notifications
q
#23470 Docker: FROM `$ARG` base-image build args mispaired when mixing a target-address base with a literal image ref Issue created by fathom-piotr Describe the bug When a single Dockerfile declares two
FROM $ARG
base images — one whose default is an in-repo
docker_image
target address and one whose default is a plain registry reference — Pants substitutes the built base image onto the wrong build arg, and leaves the real address-valued arg unsubstituted. Its literal
//path:target
default then leaks to the Docker build:
Copy code
failed to solve: failed to parse stage name "//path:target": invalid reference format
Reproduce # src/upstream/BUILD docker_image( name="image", repository="upstream/{name}", image_tags=["1.0"], instructions=["FROM alpine:3.16.1"], ) # src/downstream/BUILD docker_image(name="image") # src/downstream/Dockerfile ARG AAA_LITERAL=registry.example.com/base:1.0 ARG ZZZ_UPSTREAM=src/upstream:image FROM $AAA_LITERAL AS first FROM $ZZZ_UPSTREAM
Copy code
pants package src/downstream:image
Expected:
ZZZ_UPSTREAM
is substituted with the built
upstream/image:1.0
, and
AAA_LITERAL
is left as a normal literal (as in
test_from_image_build_arg_not_in_repo_issue_15585
). Actual: Pants emits
--build-arg AAA_LITERAL=upstream/image:1.0
(wrong arg) and no
--build-arg ZZZ_UPSTREAM
, so
ZZZ_UPSTREAM
keeps its
src/upstream:image
default and Docker fails with
invalid reference format
. Root cause In
src/python/pants/backend/docker/util_rules/docker_build_context.py
(
create_docker_build_context
): 1. Both args are classified as "from image" build args — the literal registry ref also matches the loose
valid_address
regex in
subsystems/dockerfile_wrapper_script.py
. 2.
from_image_build_args
is stored sorted by
"NAME=VALUE"
(
KeyValueSequenceUtil.from_strings
→
sorted(...)
in
backend/docker/utils.py
), so
AAA_LITERAL
sorts before
ZZZ_UPSTREAM
. 3.
resolve_unparsed_address_inputs(..., skip_invalid_addresses=True)
drops the non-resolvable literal, shortening the resolved-address list. 4. The result is then zipped against the full, sorted arg names: from_image_build_args = [ f"{arg_name}={address_to_built_image_tag[addr]}" for arg_name, addr in zip(dockerfile_build_args.keys(), from_image_addresses) ] Because a non-last value was dropped,
zip
mispairs the names with the addresses: the upstream image lands on the sorted-first
AAA_LITERAL
, and
ZZZ_UPSTREAM
is silently dropped. Any Dockerfile where an earlier-sorting FROM-image build arg is a non-resolvable value (a plain image ref, or an address not in the repo) hits this. The COPY-args path (
fill_args_from_copy
, same file) uses the same
zip(keys(), resolved_addresses)
pattern and appears to have the same latent misalignment, though I haven't reproduced that one. Pants version Reproduced on 2.31.0 and on
main
(the relevant code — classification,
sorted()
storage, and the
zip
— is identical on both). OS Both — this is pure resolution/build-arg logic, independent of platform (reproduced on macOS arm64). Additional info I have a PR with a failing reproducer test (
test_from_image_build_arg_mixed_address_and_literal
in
docker_build_context_test.py
) plus a fix that resolves each build-arg value independently so a skipped value can't shift the alignment, keyed by name: #23471 pantsbuild/pants