🥇 export from upstream (3b6ef04)

This commit is contained in:
2026-05-23 17:11:31 +02:00
parent 1e5cdf2476
commit 047fd9512d
9 changed files with 665 additions and 90 deletions
@@ -0,0 +1,74 @@
# Mount Tilde Expansion
**Date:** 2026-05-23
**Status:** Approved
## Problem
Mount paths in `.sandcage.yml` use `~` for portability (e.g. `~/.ssh:/home/agent/.ssh:ro`).
Sandcage passes these strings verbatim as `-v` flags to `docker compose run` via Rust's
`Command::new()`, which does not invoke a shell. Since `~` is a shell expansion feature,
Docker receives the literal string `~/.ssh` and either:
- **Windows:** Errors with "invalid characters for a local volume name"
- **Linux/macOS:** Creates a literal `~` directory relative to CWD
## Platform Matrix
| Environment | `dirs::home_dir()` | Docker path format | Notes |
|---|---|---|---|
| Native Windows (PowerShell/cmd) | `C:\Users\<user>` | `C:/Users/<user>/...` | Forward slashes required |
| MINGW / Git Bash | `C:\Users\<user>` (via Rust) | `C:/Users/<user>/...` | Rust bypasses MSYS path mangling |
| WSL (Linux binary) | `/home/<user>` | `/home/<user>/...` | Native Linux paths |
| Native Linux | `/home/<user>` | `/home/<user>/...` | Straightforward |
| macOS | `/Users/<user>` | `/Users/<user>/...` | Straightforward |
Key insight: Rust's `Command::new()` spawns Docker directly — no shell involved — so
the environment (PowerShell, Git Bash, etc.) is irrelevant. `dirs::home_dir()` returns
the correct native path on every platform.
## Design
### Approach: Expand at mount-pass-through time
Config files keep the portable `~` form. Expansion happens only when building Docker
CLI args.
### Implementation
Add `expand_mount_path(mount: &str) -> String` in `docker.rs`:
1. Split mount on `:` to extract host path, container path, and optional mode.
2. If host path starts with `~/` or equals `~`, replace the `~` prefix with
`dirs::home_dir()`.
3. On Windows (`cfg!(windows)`), normalize host path separators to forward slashes.
4. Rejoin parts with `:` and return.
Call site: `build_run_args()` in `docker.rs`, replacing the current
`args.push(mount.clone())` with `args.push(expand_mount_path(mount))`.
### Edge Cases
| Input | Output (Linux) | Output (Windows) |
|---|---|---|
| `~/.ssh:/home/agent/.ssh:ro` | `/home/user/.ssh:/home/agent/.ssh:ro` | `C:/Users/user/.ssh:/home/agent/.ssh:ro` |
| `~:/container` | `/home/user:/container` | `C:/Users/user:/container` |
| `/absolute:/container:ro` | `/absolute:/container:ro` (unchanged) | `/absolute:/container:ro` (unchanged) |
| `./relative:/container` | `./relative:/container` (unchanged) | `./relative:/container` (unchanged) |
### Not in Scope
- `$HOME` / `${HOME}` expansion
- `~otheruser/` expansion
- Environment variable substitution in mount paths
### Testing
- Unit tests for `expand_mount_path` covering all edge cases above.
- Update existing `build_run_args` tests that assert literal `~/.ssh` to assert the
expanded absolute path.
### Files Changed
- `crates/sandcage/src/docker.rs` — add `expand_mount_path`, call it in `build_run_args`
- Existing tests in `docker.rs` — update mount assertions