Skip to content

fix: read YAML the way Docker reads it (1.2 booleans)#62

Merged
lesnik512 merged 1 commit into
mainfrom
fix/yaml-12-booleans
Jul 14, 2026
Merged

fix: read YAML the way Docker reads it (1.2 booleans)#62
lesnik512 merged 1 commit into
mainfrom
fix/yaml-12-booleans

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Closes the last known divergence from docker compose. Design: planning/changes/2026-07-14.09-yaml-12-booleans.md.

One root cause, two bugs

PyYAML implements YAML 1.1, where a bare on/off/yes/no is a boolean. Docker parses YAML 1.2, where each is an ordinary string.

Silent corruption, exit 0:

environment:
  SSL: on
podman run ... -e "SSL=true"        # docker: -e SSL=on

The application reads "true" where its author wrote on. Nothing is reported.

A working file rejected:

environment:
  on: 1                             # docker compose renders `on: 1` and runs fine
compose2pod: error: ... key True must be a string

PyYAML resolves the key to the boolean True, and the string-key rule refuses it. Refusing a file Docker runs is the one direction that must never happen for a drop-in replacement.

Neither is repairable downstream: once the loader has turned on into True, the spelling is gone. "on" is not recoverable from True. So it is fixed at the only place it can be — the loader. The CLI now reads YAML with a SafeLoader whose boolean resolver matches only true/false: the YAML 1.2 core schema Docker's parser implements.

I also had a false claim in the architecture doc

It said rejecting a non-string key was "a deliberate divergence from Docker — environment: {3306: db} is valid Compose and Docker accepts it."

Docker refuses it too: non-string key in services.app.environment: 3306. The rule was never a divergence; it matches Docker. That paragraph is corrected here. What Docker never sees is a non-string on: key — because to Docker, on is a string.

Verification

Measured against docker compose config v5.1.2, both axes:

  • Values: onon, offoff, yesyes, nono, truetrue, falsefalse. All six match Docker.
  • Keys: on/off/yes/no accepted (emit on=1); 3306:/true: refused — by both.

The loader surgery is the risk, so I diffed our loader against plain yaml.safe_load across booleans, ints, floats, nulls, quoted strings, timestamps, octal, anchors and merge keys: the only differences are the intended ones (on/off/yes/no, including where they arrive via an anchor). true/false still resolve as booleans; y/n were already strings in PyYAML and are untouched. The Loader is a genuine SafeLoader subclass.

Scope holds: the core stays stdlib-only (import compose2pod works with PyYAML absent; only cli.py touches yaml), and the JSON path is unchanged — JSON keys are strings by construction.

Three existing tests used on: as their "non-string key" example, which is now a legal string key; they were switched to an int key (3306:), which is non-string under YAML 1.2 and refused by Docker too — so they still test what they claim.

650 tests at 100% coverage; integration green against real podman.

🤖 Generated with Claude Code

PyYAML implements YAML 1.1, where a bare on/off/yes/no is a boolean. Docker
parses YAML 1.2, where each is an ordinary string. One difference, two bugs:

  environment:
    SSL: on        -> emitted -e "SSL=true"   (docker: -e SSL=on)   silent, exit 0
    on: 1          -> key resolved to True, refused as a non-string key
                      -- rejecting a file docker compose runs fine

Neither is repairable downstream: once PyYAML has resolved `on` to True, the
spelling is gone. Fixed at the only place it can be -- the loader. The CLI now
reads YAML with a SafeLoader whose bool resolver matches only true/false, the
YAML 1.2 core schema Docker's parser implements.

The non-string-key rule itself stands and MATCHES Docker, which refuses one too
("non-string key in services.app.environment: 3306"). The architecture doc
claimed the opposite -- that Docker accepts {3306: db} and compose2pod
deliberately diverges. That claim was false and is corrected.

Verified against docker compose config: on/off/yes/no now accepted as keys and
preserved as values; 3306/true still refused as keys, by both.
@lesnik512 lesnik512 merged commit 8d78027 into main Jul 14, 2026
7 checks passed
@lesnik512 lesnik512 deleted the fix/yaml-12-booleans branch July 14, 2026 17:38
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