0952aa8217
When git repo mounts and regular file mounts have overlapping target paths, broader parent mounts hide deeper child mounts because Docker Compose applies volumes in array order. - Add sort_volumes_by_specificity() to docker.py: - Sorts by target path depth (parent paths first, child paths last) - Logs warnings for duplicate targets - Handles :bind and :ro suffixes correctly - Integrate into manifest flow (compile_compose): - Sorts manifest mounts + EXTRA_VOLUMES before writing compose - Integrate into legacy flow (_modify_compose_file): - Sorts after appending extra_volumes to existing template volumes - Add 6 unit tests covering parent/child ordering, stable sort, type suffixes, empty list, single volume, and duplicate warnings. Quality gates: pytest (214 passed, 6 pre-existing), tsc --noEmit (clean)
46 lines
1.7 KiB
Markdown
46 lines
1.7 KiB
Markdown
# Spec: Mount Specificity Ordering
|
|
|
|
## Requirements
|
|
|
|
1. All volume mount entries written to compose files must be sorted by target path depth.
|
|
2. Shorter / parent target paths appear **before** deeper / child target paths.
|
|
3. Deeper mounts are applied later by Docker, overlaying parent mounts correctly.
|
|
4. Exact same-target overlaps are logged as warnings.
|
|
5. Works for both manifest and legacy compose generation paths.
|
|
|
|
## Algorithm
|
|
|
|
```python
|
|
def sort_volumes_by_specificity(volumes: list[str]) -> list[str]:
|
|
"""
|
|
Sort volume strings so parent paths come before child paths.
|
|
Volume format: source:target or source:target:type
|
|
"""
|
|
```
|
|
|
|
1. Parse each volume string to extract the target path (second colon-delimited field).
|
|
2. Normalize the target: strip trailing `/`, collapse `//`.
|
|
3. Compute depth = number of `/`-separated segments.
|
|
4. Sort ascending by depth. Stable sort preserves input order for equal depths.
|
|
5. Detect exact same-target strings and log warnings.
|
|
|
|
## Compose Format Handling
|
|
|
|
- `source:target` → target is second field
|
|
- `source:target:bind` → target is second field
|
|
- `source:target:ro` → target is second field
|
|
- Split by `:` into at most 3 parts. Target is always index 1.
|
|
|
|
## Integration Points
|
|
|
|
- `compile_compose()` in `manifest_compiler.py`: sort `volumes` list before assigning to `service["volumes"]`.
|
|
- `_modify_compose_file()` in `tool_instances.py`: sort `service_config["volumes"]` after appending extra volumes.
|
|
|
|
## Tests
|
|
|
|
- Repo mount `/workspace/x` + file mount `/workspace/x/y/config.json` → file mount comes after.
|
|
- Same depth mounts → stable order preserved.
|
|
- Exact same target → warning logged.
|
|
- Empty volumes list → no-op.
|
|
- Volume with `type` suffix → parsed correctly.
|