Files
headquarter/openspec/designs/home-path-expansion.md
Alex Blank 29a12bb102 feat: expand ~ and $HOME in mount target paths
- Add expand_container_path() helper that resolves ~/ and $HOME/ prefixes
- Add get_manifest_home_dir() to compute /home/{user.name} or /root from manifest
- Set ENV HOME=... and ENV USER=... in generated Dockerfile for runtime compatibility
- Pass home_dir through instance creation and startup pipeline
- Expand mount targets in apply_resolved_profile() for regular profile mounts
- Expand mapping targets in _resolve_git_mount_mappings() for git mounts
- Expand working_directory and volume targets in _modify_compose_file()
- Update _prepare_manifest_instance to return home_dir alongside image tag
- Fetch tool_type early in start_instance to determine home_dir before profile application

Quality gates: pytest 188 passed, frontend typecheck clean

Addresses: home-path-expansion
2026-05-29 00:01:04 +02:00

103 lines
2.9 KiB
Markdown

# Design: ~ / $HOME Expansion in Mount Paths
## Architecture
### New Helpers
#### `expand_container_path(path: str, home_dir: str) -> str`
Located in `config_profile_resolver.py` (or new shared module).
```python
def expand_container_path(path: str, home_dir: str) -> str:
if path.startswith("~/"):
return os.path.join(home_dir, path[2:])
if path == "~":
return home_dir
path = path.replace("$HOME/", home_dir + "/")
path = path.replace("$HOME", home_dir)
return path
```
#### `get_manifest_home_dir(manifest: dict) -> str`
Located in `manifest_compiler.py`.
```python
def get_manifest_home_dir(manifest: dict) -> str:
user = manifest.get("user")
if user and user.get("name"):
return f"/home/{user['name']}"
return "/root"
```
#### `get_tool_home_dir(tool_type: ToolType, manifest: dict | None) -> str`
Located in `tool_instances.py` or `manifest_compiler.py`.
```python
def get_tool_home_dir(tool_type: ToolType, manifest: dict | None = None) -> str:
if tool_type.definition_type == "manifest" and manifest:
return get_manifest_home_dir(manifest)
return "/root"
```
### Pipeline Changes
#### `create_instance` flow
1. Determine `home_dir` from tool type + manifest (if manifest-based)
2. Pass `home_dir` to `_modify_compose_file()` — expand mount targets in compose
#### `start_instance` flow
1. Determine `home_dir` from tool type + resolved manifest
2. Pass `home_dir` to `apply_resolved_profile()` — expand profile mount targets
3. Pass `home_dir` to `_resolve_git_mounts()` — expand git mount mapping targets
#### `apply_resolved_profile()`
```python
def apply_resolved_profile(
instance_dir: str,
resolved: ResolvedProfile,
home_dir: str = "/root",
) -> tuple[...]:
...
for mount in resolved.mounts.values():
target = expand_container_path(mount.target, home_dir)
...
```
#### `_resolve_git_mount_mappings()`
```python
def _resolve_git_mount_mappings(
repo_path: str,
mappings: list[dict],
working_directory: str | None,
home_dir: str = "/root",
) -> list[dict]:
...
final_target = expand_container_path(target_path, home_dir)
...
```
### Dockerfile Change
In `compile_dockerfile()`, after user creation, set `HOME`:
```python
if user:
home = f"/home/{user['name']}"
lines.append(f"ENV HOME={home}")
lines.append(f"ENV USER={user['name']}")
```
### File Changes
| File | Change |
|------|--------|
| `apps/api/src/services/config_profile_resolver.py` | Add `expand_container_path()`, apply in `apply_resolved_profile()` |
| `apps/api/src/services/manifest_compiler.py` | Add `get_manifest_home_dir()`, set `HOME`/`USER` env in Dockerfile |
| `apps/api/src/api/tool_instances.py` | Determine `home_dir`, pass through all mount resolution functions |
| `apps/api/tests/unit/test_home_path_expansion.py` | New unit tests |
| `apps/api/tests/unit/test_manifest_compiler.py` | Add tests for `get_manifest_home_dir` and Dockerfile `HOME` env |