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)
56 lines
1.8 KiB
Markdown
56 lines
1.8 KiB
Markdown
# Design: Mount Specificity Ordering
|
|
|
|
## Helper Function
|
|
|
|
Add `sort_volumes_by_specificity` to a shared utilities module. The most appropriate location is `apps/api/src/services/docker.py` since it already contains Docker/Compose helpers, or a new small module. We'll add it to `apps/api/src/services/docker.py` to keep the change minimal.
|
|
|
|
## Sorting Logic
|
|
|
|
```python
|
|
def _target_depth(vol: str) -> int:
|
|
parts = vol.split(":")
|
|
if len(parts) < 2:
|
|
return 0
|
|
target = parts[1].rstrip("/")
|
|
if not target or target == "/":
|
|
return 0
|
|
return target.count("/")
|
|
```
|
|
|
|
Stable sort: `sorted(volumes, key=_target_depth)`.
|
|
|
|
## Warning Logic
|
|
|
|
```python
|
|
def sort_volumes_by_specificity(volumes: list[str]) -> list[str]:
|
|
from collections import Counter
|
|
targets = [v.split(":")[1] if ":" in v else "" for v in volumes]
|
|
duplicates = [t for t, c in Counter(targets).items() if c > 1]
|
|
if duplicates:
|
|
logger.warning("Duplicate mount targets detected: %s", duplicates)
|
|
return sorted(volumes, key=_target_depth)
|
|
```
|
|
|
|
## Call Sites
|
|
|
|
### manifest_compiler.py
|
|
In `compile_compose()`, after building the `volumes` list and before assigning:
|
|
```python
|
|
from src.services.docker import sort_volumes_by_specificity
|
|
volumes = sort_volumes_by_specificity(volumes)
|
|
service["volumes"] = volumes
|
|
```
|
|
|
|
### tool_instances.py
|
|
In `_modify_compose_file()`, after appending `extra_volumes` and before write:
|
|
```python
|
|
from src.services.docker import sort_volumes_by_specificity
|
|
service_config["volumes"] = sort_volumes_by_specificity(service_config["volumes"])
|
|
```
|
|
|
|
## Files Changed
|
|
- `apps/api/src/services/docker.py` — add helper
|
|
- `apps/api/src/services/manifest_compiler.py` — sort manifest volumes
|
|
- `apps/api/src/api/tool_instances.py` — sort legacy volumes
|
|
- `apps/api/tests/unit/test_docker_service.py` — add tests
|