compose2pod 0.1.8 — interpolation moves to run time
This release reverses 0.1.7: Compose ${VAR} interpolation now happens when
the generated script runs, not when compose2pod generates it. compose2pod
emits a script that runs later — typically in CI, where the real secrets and
config live — so resolving against compose2pod's own environment at generation
time baked any variable that was unset then to an empty string. References are
now emitted as live POSIX-shell fragments that the runtime shell expands.
Change
${VAR}is interpolated at script-run time, not generation time.
to_shell()(newcompose2pod/shell.py) re-encodes every compose-derived
value (environment,image,command,volumes,tmpfs,env_file,
healthchecktest) into a double-quoted shell fragment whose variable
references stay live, so the generated script's own shell resolves them
against its runtime environment. All the 0.1.7 forms still work —$VAR,
${VAR},${VAR:-default},${VAR-default},${VAR:?msg},${VAR?msg},
${VAR:+alt},${VAR+alt},$$— mapping onto identical POSIXsh
parameter expansion. A bare$VAR/${VAR}is emitted as${VAR-}so an
unset variable expands to empty under the script'sset -euinstead of
aborting;${VAR:?msg}now fails the script at run time (not at generation).
Command substitution and other shell metacharacters in literal text are
escaped, so nothing but a variable reference is ever live. The CLI prints one
informational note listing the variables the script references at run time.
Fix
environmentnull value is host passthrough. A null mapping value
(KEY:) now emits a bare-e KEY— passingKEYthrough from the host,
identical to the list form- KEY— instead of the literal-e KEY=None.- Malformed braced references are rejected. A braced reference whose text
after the name is not a valid operator (e.g.${FOO!bar}) now raises a clear
UnsupportedComposeErrorinstead of silently dropping the trailing text.
Packaging
- Public API: the generation-time
interpolatefunction is removed;
to_shellis exported in its place.to_shell(value)returns a
runtime-expandable shell fragment, not a resolved value.
Why
The 0.1.7 model resolved ${VAR} against os.environ at generation time. For
compose2pod's actual use — generate a script now, run it later in CI — that was
the wrong moment: the variables are supposed to be unset at generation and set
at run time, so 0.1.7 baked them to empty and emitted a spurious "variable not
set" warning. Deferring to the runtime shell resolves them where they exist.
Downstream
- Compose files: set
${VAR}values in the runtime environment of the
generated script (e.g. your CI job), not in the environment that runs
compose2pod. An unset bare${VAR}now expands to empty at run time; write
${VAR:?message}where you want a missing variable to fail the run. - API consumers:
compose2pod.interpolateis gone. Use
compose2pod.to_shell(value), which emits a runtime-expandable shell fragment
rather than resolving the value in-process.
Internals
architecture/supported-subset.mdrewritten to describe run-time expansion,
the${VAR-}/$$/${VAR:?}semantics,env_fileinterpolation, the
reference note, null-value passthrough, and malformed-reference rejection.- 123 tests at 100% line coverage (enforced);
ruff select=ALL,ty, and
eof-fixerclean.test_shell.pyexecutes emitted fragments under real
sh -eucto prove run-time expansion, the${VAR:?}abort, and
command-substitution inertness.