Skip to content

feat(earthly): pass custom Docker build args via CUSTOM_ARG_ prefix - #801

Open
Yogeshwar-Pawade wants to merge 1 commit into
mainfrom
feat/custom-dockerfile-build-args
Open

feat(earthly): pass custom Docker build args via CUSTOM_ARG_ prefix#801
Yogeshwar-Pawade wants to merge 1 commit into
mainfrom
feat/custom-dockerfile-build-args

Conversation

@Yogeshwar-Pawade

Copy link
Copy Markdown
Contributor

Issue

Custom Docker build arguments added to ./Dockerfile never received a value and silently fell back to their defaults — with no build-time indication.

  • The Earthfile's base-image target forwards only a fixed --build-arg list into the user Dockerfile (BASE, OS_*, proxies, DRBD_VERSION), and Earthly's FROM DOCKERFILE cannot forward build args dynamically.
  • So a user who added, e.g., ARG BRAND / ARG DHCP_VENDOR_CLASS to the Dockerfile and set the value in .arg got the default, not their value.
  • Impact: per-brand / per-site customization (one image serving KFC US, KFC UK, Taco Bell, Pizza Hut) was discarded on every build. It surfaced as nodes failing to obtain a DHCP lease and never registering, and could only be detected by inspecting a running node's filesystem — never from the build output.

Solution

earthly.sh now bridges the gap the Earthfile can't: before Earthly runs, it injects .arg values into the matching ARG defaults of a staged Dockerfile, then restores the original on exit. The upstream Earthfile is not modified (it survives CanvOS upgrades untouched).

Custom args are declared in .arg with a CUSTOM_ARG_ prefix, which makes intent explicit and lets typos be caught (a plain .arg key can't be distinguished from a normal Earthfile arg):

# .arg
CUSTOM_ARG_BRAND=kfc-uk
CUSTOM_ARG_DHCP_VENDOR_CLASS=retail
# Dockerfile (customization section)
ARG BRAND
ARG DHCP_VENDOR_CLASS=default-class

The wrapper prints exactly what it did (killing the original "no build-time indication"):

Custom Docker build args:
  injected:        BRAND=kfc-uk DHCP_VENDOR_CLASS=retail

Fail-closed guards (silent drop → loud error)

# Condition Level
1 CUSTOM_ARG_<NAME> with no matching ARG <NAME> in the Dockerfile (typo) ERROR
2 CUSTOM_ARG_<NAME> where <NAME> is Earthfile-managed (BASE, OS_DISTRIBUTION, proxies, …) ERROR
3 CUSTOM_ARG_<NAME> with an empty value ERROR
4 injection did not take effect ERROR
5 no ./Dockerfile present skip cleanly
6 a custom ARG with no default and no value, on an image/ISO target ERROR (non-Dockerfile target → WARNING)

Normal Earthfile .arg keys (CUSTOM_TAG, ISO_NAME, ARCH, …) are ignored — no false positives. Secrets must not be passed this way (build args leak into docker history / the build cache); the existing --secret flow is unchanged.

Files

  • scripts/custom-build-args.sh — sourceable mechanism + guards (new)
  • earthly.sh — sources the lib and calls inject_custom_build_args "$1" before the build (+11 lines)
  • test/custom-build-args_test.sh — 9 unit tests, no Docker required (new)
  • README.md — new "Custom Docker Build Arguments" section; corrected the misleading "Earthfile merges custom packages" line
  • .arg.template — documented CUSTOM_ARG_ usage

Manual Test Plan (what was verified)

All run on this branch against a real dev .arg.

Check Command / action Result
Unit tests (all 6 guards) bash test/custom-build-args_test.sh PASS: 9 FAIL: 0
Injection report ./earthly.sh +base-image ✅ prints Custom Docker build args: injected: BRAND=kfc-uk
Typo guard CUSTOM_ARG_BRAN=… with ARG BRAND in Dockerfile ✅ aborts before build: "no 'ARG BRAN'"
Reserved guard CUSTOM_ARG_OS_DISTRIBUTION=… ✅ aborts: "managed by CanvOS/Earthfile"
Empty guard CUSTOM_ARG_BRAND= ✅ aborts: "set but empty"
Value reaches the real build added ARG BRAND + RUN echo "CANVOS-BRAND-CHECK: [$BRAND]", ran +base-image ✅ build log shows CANVOS-BRAND-CHECK: [kfc-uk]
Injected Dockerfile builds full ./earthly.sh +base-image 🌍 Earthly Build ✅ SUCCESS (exit 0)
Auto-restore inspect ./Dockerfile after the run ✅ restored to original; no Dockerfile.canvos.bak left behind

The contrast case (plain BRAND=… without the prefix) reproduces the old empty-value behavior, confirming why the mechanism is needed.

Notes

  • Pre-existing, unrelated: fatal: No tags can describe … from git describe in earthly.sh on a branch with no reachable tags — harmless (only affects the version tag on a full build).

The Earthfile's base-image target forwards only a fixed --build-arg list
into the user Dockerfile, and Earthly's `FROM DOCKERFILE` cannot forward
build args dynamically. Any custom `ARG` a user added to ./Dockerfile
therefore never received a value from .arg and silently resolved to its
default, with no build-time indication -- discarding per-build
customization (e.g. BRAND, DHCP_VENDOR_CLASS).

earthly.sh now injects values into the matching Dockerfile `ARG` defaults
before Earthly runs, driven by CUSTOM_ARG_<NAME> entries in .arg. The
upstream Earthfile is untouched (survives CanvOS upgrades) and the
original Dockerfile is restored on exit.

Fail-closed guards turn the old silent drop into loud errors: typo /
undeclared ARG, Earthfile-managed name, empty value, injection that did
not take effect, and an unsatisfied bare ARG on an image/ISO build.
Normal Earthfile .arg keys are ignored (no false positives). A build-time
report prints exactly which args were injected vs left at default.

- scripts/custom-build-args.sh: sourceable mechanism + guards
- test/custom-build-args_test.sh: 9 unit tests (no Docker required)
- README + .arg.template: document the CUSTOM_ARG_ workflow

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant