|
| 1 | +# compose2pod 0.1.8 — interpolation moves to run time |
| 2 | + |
| 3 | +This release reverses 0.1.7: Compose `${VAR}` interpolation now happens when |
| 4 | +the *generated script* runs, not when compose2pod generates it. compose2pod |
| 5 | +emits a script that runs later — typically in CI, where the real secrets and |
| 6 | +config live — so resolving against compose2pod's own environment at generation |
| 7 | +time baked any variable that was unset then to an empty string. References are |
| 8 | +now emitted as live POSIX-shell fragments that the runtime shell expands. |
| 9 | + |
| 10 | +## Change |
| 11 | + |
| 12 | +- **`${VAR}` is interpolated at script-run time, not generation time.** |
| 13 | + `to_shell()` (new `compose2pod/shell.py`) re-encodes every compose-derived |
| 14 | + value (`environment`, `image`, `command`, `volumes`, `tmpfs`, `env_file`, |
| 15 | + healthcheck `test`) into a double-quoted shell fragment whose variable |
| 16 | + references stay live, so the generated script's own shell resolves them |
| 17 | + against its runtime environment. All the 0.1.7 forms still work — `$VAR`, |
| 18 | + `${VAR}`, `${VAR:-default}`, `${VAR-default}`, `${VAR:?msg}`, `${VAR?msg}`, |
| 19 | + `${VAR:+alt}`, `${VAR+alt}`, `$$` — mapping onto identical POSIX `sh` |
| 20 | + parameter expansion. A bare `$VAR`/`${VAR}` is emitted as `${VAR-}` so an |
| 21 | + unset variable expands to empty under the script's `set -eu` instead of |
| 22 | + aborting; `${VAR:?msg}` now fails the script at run time (not at generation). |
| 23 | + Command substitution and other shell metacharacters in literal text are |
| 24 | + escaped, so nothing but a variable reference is ever live. The CLI prints one |
| 25 | + informational note listing the variables the script references at run time. |
| 26 | + |
| 27 | +## Fix |
| 28 | + |
| 29 | +- **`environment` null value is host passthrough.** A null mapping value |
| 30 | + (`KEY:`) now emits a bare `-e KEY` — passing `KEY` through from the host, |
| 31 | + identical to the list form `- KEY` — instead of the literal `-e KEY=None`. |
| 32 | +- **Malformed braced references are rejected.** A braced reference whose text |
| 33 | + after the name is not a valid operator (e.g. `${FOO!bar}`) now raises a clear |
| 34 | + `UnsupportedComposeError` instead of silently dropping the trailing text. |
| 35 | + |
| 36 | +## Packaging |
| 37 | + |
| 38 | +- Public API: the generation-time `interpolate` function is **removed**; |
| 39 | + `to_shell` is exported in its place. `to_shell(value)` returns a |
| 40 | + runtime-expandable shell fragment, not a resolved value. |
| 41 | + |
| 42 | +## Why |
| 43 | + |
| 44 | +The 0.1.7 model resolved `${VAR}` against `os.environ` at generation time. For |
| 45 | +compose2pod's actual use — generate a script now, run it later in CI — that was |
| 46 | +the wrong moment: the variables are supposed to be unset at generation and set |
| 47 | +at run time, so 0.1.7 baked them to empty and emitted a spurious "variable not |
| 48 | +set" warning. Deferring to the runtime shell resolves them where they exist. |
| 49 | + |
| 50 | +## Downstream |
| 51 | + |
| 52 | +- **Compose files:** set `${VAR}` values in the *runtime* environment of the |
| 53 | + generated script (e.g. your CI job), not in the environment that runs |
| 54 | + compose2pod. An unset bare `${VAR}` now expands to empty at run time; write |
| 55 | + `${VAR:?message}` where you want a missing variable to fail the run. |
| 56 | +- **API consumers:** `compose2pod.interpolate` is gone. Use |
| 57 | + `compose2pod.to_shell(value)`, which emits a runtime-expandable shell fragment |
| 58 | + rather than resolving the value in-process. |
| 59 | + |
| 60 | +## Internals |
| 61 | + |
| 62 | +- `architecture/supported-subset.md` rewritten to describe run-time expansion, |
| 63 | + the `${VAR-}`/`$$`/`${VAR:?}` semantics, `env_file` interpolation, the |
| 64 | + reference note, null-value passthrough, and malformed-reference rejection. |
| 65 | +- 123 tests at 100% line coverage (enforced); `ruff select=ALL`, `ty`, and |
| 66 | + `eof-fixer` clean. `test_shell.py` executes emitted fragments under real |
| 67 | + `sh -euc` to prove run-time expansion, the `${VAR:?}` abort, and |
| 68 | + command-substitution inertness. |
0 commit comments