fix: accept Compose's 'host=ip' separator in extra_hosts#59
Merged
Conversation
Compose documents extra_hosts list entries as `host=ip` and also accepts the legacy `host:ip`. compose2pod only ever split on ':', so the documented form was accepted by the gate and then silently mangled: extra_hosts: ["somehost=162.242.195.82"] -> podman pod create ... --add-host "somehost=162.242.195.82:" The whole string became the hostname and the address came out empty. An entry with no separator at all had the same face: accepted, emitted as `--add-host "no-separator:"`. keys.split_extra_host is now the one reader every site shares -- the gate, the emitter, and the extends merge -- so they cannot disagree about where an entry divides. '=' wins when present, because an IPv6 address is full of colons and splitting on the first one would tear it apart; the colon form still splits on the first colon only, so `myhost:::1` keeps working. An entry with neither separator now raises.
The first pass re-split every entry -- including mapping-form ones -- through
split_extra_host, after extra_host_pairs had joined them into 'host:address'.
With '=' now winning over ':', a mapping value containing an '=' re-divided at
the wrong character:
extra_hosts: {somehost: "weird=address"}
-> --add-host "somehost:weird:address" (host and address both wrong)
Silent, exit 0 -- the same corruption class this change exists to close,
reintroduced one layer up.
The mapping form arrives already divided, so it should never be joined and
re-parsed. keys.extra_host_entries yields (host, address) pairs from either
form: the mapping straight through (via _render_scalar, so a boolean address
still normalizes like docker compose config), the list via split_extra_host.
extra_host_pairs had no other caller and is gone.
5296ee2 to
6575909
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #58 (it shares the
extra_hostsnormalizer). Design:planning/changes/2026-07-14.06-extra-hosts-equals-separator.md.The bug: silent corruption, exit 0
Compose documents
extra_hostslist entries ashost=ipand also accepts the legacyhost:ip. compose2pod only ever split on:, so the documented form was accepted by the gate and then mangled:The whole string became the hostname; the address came out empty. An entry with no separator at all (
- "no-separator") had the same face — accepted, emitted as--add-host "no-separator:".The fix
keys.split_extra_hostis the single reader shared by every site that parses an entry — the gate, the emitter, and theextendsmerge — so they cannot disagree about where an entry divides.=wins when present, and that disambiguation is the crux: an IPv6 address is full of colons, so splitting on the first one would tear it apart.myhostv6=::1→('myhostv6', '::1'). The colon form still splits on the first colon, somyhost:::1keeps working. An entry with neither separator now raises instead of emitting a broken flag.A regression I introduced, and caught
The first pass re-split every entry — including mapping-form ones — because
_add_host_flagsjoined{host: addr}into"host:addr"and then re-parsed it. With=now winning, a mapping value containing an=re-divided at the wrong character:That is the exact corruption class this PR exists to close, reintroduced one layer up. Round-tripping structured data through a string is the flaw: the mapping form arrives already divided and must never be joined and re-parsed.
keys.extra_host_entriesnow yields(host, address)pairs from either form — the mapping straight through, the list viasplit_extra_host.extra_host_pairshad no other caller and is deleted.Verification
Every form emits identical flags:
host=ip,host:ip, and mapping all produce--add-host "somehost:162.242.195.82". IPv6 survives both separators. Mapping values containing=are preserved. Conflict detection,${VAR}interpolation, and the mapping form's Docker-style boolean normalization all unchanged.593 tests at 100% coverage; integration green against real podman.
🤖 Generated with Claude Code