|
| 1 | +--- |
| 2 | +summary: Emit absolute anonymous volumes as `-v <path>` instead of crashing; reject non-absolute colon-less entries loudly. |
| 3 | +--- |
| 4 | + |
| 5 | +# Change: Support anonymous volumes |
| 6 | + |
| 7 | +**Lane:** lightweight — one source file (`emit.py`), a resolved design choice, |
| 8 | +two tests, plus the capability-doc promotion. |
| 9 | + |
| 10 | +## Goal |
| 11 | + |
| 12 | +A Compose short-form volume can be a single container path with no |
| 13 | +`source:target` (an *anonymous volume*, e.g. `- /var/cache/models`). Such an |
| 14 | +entry passes `validate()` (its path starts with `/`) but crashes |
| 15 | +`emit.run_flags` at `volume.split(":", 1)` with |
| 16 | +`ValueError: not enough values to unpack (expected 2, got 1)`. This shipped in |
| 17 | +0.1.2 and breaks real compose files that use anonymous volumes to shadow |
| 18 | +subdirectories of a bind mount. Fix: emit an anonymous volume as `-v <path>`. |
| 19 | + |
| 20 | +## Approach |
| 21 | + |
| 22 | +In `run_flags` (`compose2pod/emit.py`), branch on whether the volume string |
| 23 | +contains a `:`. No colon → anonymous volume: append `["-v", volume]` verbatim |
| 24 | +(no `project_dir` translation — an anonymous volume names a container path, not |
| 25 | +a host source) and continue. With a colon → the existing bind-mount path |
| 26 | +(source translated against `project_dir` when relative). |
| 27 | + |
| 28 | +Design choice (resolved): **support**, not reject. Anonymous volumes are valid |
| 29 | +Compose; podman's `-v <path>` creates an anonymous volume at that path, which is |
| 30 | +exactly the compose semantics. Rejecting in `validate()` would fix the crash but |
| 31 | +break compose files that rely on them. |
| 32 | + |
| 33 | +`_validate_service_volumes` also gains a guard: a colon-less entry that is *not* |
| 34 | +absolute (e.g. `./cache`) is malformed Compose and now raises |
| 35 | +`UnsupportedComposeError` rather than slipping through to emit a broken |
| 36 | +`-v ./cache`. This keeps the project's "refuse loudly, never emit a silently |
| 37 | +broken script" contract. Absolute anonymous volumes (`/var/cache/models`) |
| 38 | +continue to be accepted. |
| 39 | + |
| 40 | +Promote into `architecture/supported-subset.md`: the Volumes section currently |
| 41 | +says "Short bind-mount syntax only (`source:target`)" — extend it to note that a |
| 42 | +single absolute container path is accepted as an anonymous volume and emitted as |
| 43 | +`-v <path>`. |
| 44 | + |
| 45 | +## Files |
| 46 | + |
| 47 | +- `compose2pod/emit.py` — `run_flags`: handle the no-colon (anonymous) volume case. |
| 48 | +- `tests/test_emit.py` — anonymous volume emits `-v <path>`; regression for the crash. |
| 49 | +- `tests/test_parsing.py` — anonymous volume passes `validate()` (pins the accept). |
| 50 | +- `architecture/supported-subset.md` — Volumes section promotion. |
| 51 | + |
| 52 | +## Verification |
| 53 | + |
| 54 | +- [ ] Failing test first: `run_flags` with `volumes: ["/var/cache/models"]` — expected RED `ValueError: not enough values to unpack`. |
| 55 | +- [ ] Apply the `emit.py` branch. |
| 56 | +- [ ] Test passes: the emitted flags contain `["-v", "/var/cache/models"]`. |
| 57 | +- [ ] `just test-ci` — full suite green at 100% line coverage. |
| 58 | +- [ ] `just lint-ci` — clean (ruff ALL, ruff format, ty, eof-fixer, planning check). |
0 commit comments