Support large target repos with with bind-mount option. (#577)
* fix: resolve pre-commit check failures - Change RuntimeError to TypeError for type validation in report/writer.py - Update pyupgrade to v3.21.2 for Python 3.14 compatibility * chore: add pytest test infrastructure Mirror the layout introduced on feature/438-token_budget: pytest + pytest-asyncio dev deps, asyncio_mode auto, a tests.* mypy override, and pytest in the mypy pre-commit hook deps so the tests/ package type-checks. * feat: add --mount and large-target pre-flight for local repos (#492) Large local targets were copied into the sandbox file-by-file via the SDK LocalDir entry, which stalls on big repos and could leave /workspace empty. - --mount <path> bind-mounts a host directory read-only at /workspace/<subdir> instead of copying it, bypassing the per-file stream. - A size pre-flight (STRIX_MAX_LOCAL_COPY_MB, default 1024) fails fast with a clear message suggesting --mount when a non-mounted local target is too big. * fix: reject empty --mount paths An empty or whitespace-only --mount value resolves to the current working directory and would silently bind-mount it into the sandbox. Reject it. * fix: dedupe local targets so a dir is never both copied and mounted If the same directory is passed via --target and --mount (or as duplicate values), it previously produced two targets — copied AND bind-mounted, and the copied one could trip the size pre-flight. Dedupe by resolved path, preferring the bind mount. * fix: treat non-positive STRIX_MAX_LOCAL_COPY_MB as disabled Previously a value of 0 (or negative) made every local target count as oversized, aborting all local scans. Now <= 0 disables the pre-flight. * fix: log unreadable subtrees during size pre-flight os.walk silently swallowed directory-listing errors, so a permission-denied subtree could make a large repo under-count and slip past the pre-flight. Surface such omissions via an onerror warning. * docs: document --mount and STRIX_MAX_LOCAL_COPY_MB Add CLI reference + example for --mount, document the size pre-flight env var, note the read-only-is-not-a-hard-boundary caveat and that remote repos are not size-checked, and clarify the backends docstring on when bind mounts apply. * Update strix/interface/main.py * Update strix/runtime/docker_client.py ---------
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Tests for build_session_entries: splitting copied vs bind-mounted sources."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from agents.sandbox.entries import LocalDir
|
||||
|
||||
from strix.runtime.session_manager import build_session_entries
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _source(subdir: str, path: str, *, mount: bool = False) -> dict[str, Any]:
|
||||
return {"source_path": path, "workspace_subdir": subdir, "mount": mount}
|
||||
|
||||
|
||||
def test_copied_source_becomes_localdir_entry(tmp_path: Path) -> None:
|
||||
entries, bind_mounts = build_session_entries([_source("repo", str(tmp_path))])
|
||||
|
||||
assert bind_mounts == []
|
||||
assert isinstance(entries["repo"], LocalDir)
|
||||
assert entries["repo"].src == tmp_path.resolve()
|
||||
|
||||
|
||||
def test_mounted_source_becomes_bind_mount(tmp_path: Path) -> None:
|
||||
entries, bind_mounts = build_session_entries([_source("repo", str(tmp_path), mount=True)])
|
||||
|
||||
assert entries == {}
|
||||
assert bind_mounts == [
|
||||
{
|
||||
"source": str(tmp_path.resolve()),
|
||||
"target": "/workspace/repo",
|
||||
"read_only": True,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_mixed_sources_split_correctly(tmp_path: Path) -> None:
|
||||
copied = tmp_path / "copied"
|
||||
mounted = tmp_path / "mounted"
|
||||
copied.mkdir()
|
||||
mounted.mkdir()
|
||||
|
||||
entries, bind_mounts = build_session_entries(
|
||||
[
|
||||
_source("copied", str(copied)),
|
||||
_source("mounted", str(mounted), mount=True),
|
||||
]
|
||||
)
|
||||
|
||||
assert list(entries) == ["copied"]
|
||||
assert isinstance(entries["copied"], LocalDir)
|
||||
assert [m["target"] for m in bind_mounts] == ["/workspace/mounted"]
|
||||
|
||||
|
||||
def test_incomplete_sources_are_skipped() -> None:
|
||||
entries, bind_mounts = build_session_entries(
|
||||
[
|
||||
{"source_path": "", "workspace_subdir": "x"},
|
||||
{"source_path": "/p", "workspace_subdir": ""},
|
||||
]
|
||||
)
|
||||
assert entries == {}
|
||||
assert bind_mounts == []
|
||||
Reference in New Issue
Block a user