Skip to content

Commit 4c68780

Browse files
authored
refactor: share sandbox tar exclude arg generation (#2987)
This pull request improves sandbox workspace persistence internals by sharing the shell `tar --exclude` argument generation used by Blaxel, Daytona, and E2B sandbox sessions. It adds a small common helper under `src/agents/sandbox/session/` and keeps each provider's existing private `_tar_exclude_args()` surface as a thin delegate, preserving the generated command strings while removing duplicated quoting, sorting, and path-normalization logic. Direct unit coverage was added for empty/dot paths, stable sorting, shell quoting, dot-prefixed patterns, and absolute-path normalization.
1 parent 3aad7eb commit 4c68780

5 files changed

Lines changed: 52 additions & 24 deletions

File tree

src/agents/extensions/sandbox/blaxel/sandbox.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
)
5757
from ....sandbox.session.runtime_helpers import RESOLVE_WORKSPACE_PATH_HELPER, RuntimeHelperScript
5858
from ....sandbox.session.sandbox_client import BaseSandboxClient
59+
from ....sandbox.session.tar_workspace import shell_tar_exclude_args
5960
from ....sandbox.snapshot import SnapshotBase, SnapshotSpec, resolve_snapshot
6061
from ....sandbox.types import ExecResult, ExposedPortEndpoint, User
6162
from ....sandbox.util.retry import (
@@ -510,14 +511,7 @@ async def running(self) -> bool:
510511
# -- workspace persistence -----------------------------------------------
511512

512513
def _tar_exclude_args(self) -> list[str]:
513-
excludes: list[str] = []
514-
for rel in sorted(self._persist_workspace_skip_relpaths(), key=lambda p: p.as_posix()):
515-
rel_posix = rel.as_posix().lstrip("/")
516-
if not rel_posix or rel_posix in {".", "/"}:
517-
continue
518-
excludes.append(f"--exclude={shlex.quote(rel_posix)}")
519-
excludes.append(f"--exclude={shlex.quote(f'./{rel_posix}')}")
520-
return excludes
514+
return shell_tar_exclude_args(self._persist_workspace_skip_relpaths())
521515

522516
@retry_async(
523517
retry_if=lambda exc, self: (

src/agents/extensions/sandbox/daytona/sandbox.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
)
5656
from ....sandbox.session.runtime_helpers import RESOLVE_WORKSPACE_PATH_HELPER, RuntimeHelperScript
5757
from ....sandbox.session.sandbox_client import BaseSandboxClient, BaseSandboxClientOptions
58+
from ....sandbox.session.tar_workspace import shell_tar_exclude_args
5859
from ....sandbox.snapshot import SnapshotBase, SnapshotSpec, resolve_snapshot
5960
from ....sandbox.types import ExecResult, ExposedPortEndpoint, User
6061
from ....sandbox.util.retry import (
@@ -878,14 +879,7 @@ async def running(self) -> bool:
878879
return False
879880

880881
def _tar_exclude_args(self) -> list[str]:
881-
excludes: list[str] = []
882-
for rel in sorted(self._persist_workspace_skip_relpaths(), key=lambda p: p.as_posix()):
883-
rel_posix = rel.as_posix().lstrip("/")
884-
if not rel_posix or rel_posix in {".", "/"}:
885-
continue
886-
excludes.append(f"--exclude={shlex.quote(rel_posix)}")
887-
excludes.append(f"--exclude={shlex.quote(f'./{rel_posix}')}")
888-
return excludes
882+
return shell_tar_exclude_args(self._persist_workspace_skip_relpaths())
889883

890884
@retry_async(
891885
retry_if=lambda exc, self, tar_cmd, tar_path: (

src/agents/extensions/sandbox/e2b/sandbox.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
)
6464
from ....sandbox.session.runtime_helpers import RESOLVE_WORKSPACE_PATH_HELPER, RuntimeHelperScript
6565
from ....sandbox.session.sandbox_client import BaseSandboxClient, BaseSandboxClientOptions
66+
from ....sandbox.session.tar_workspace import shell_tar_exclude_args
6667
from ....sandbox.snapshot import SnapshotBase, SnapshotSpec, resolve_snapshot
6768
from ....sandbox.types import ExecResult, ExposedPortEndpoint, User
6869
from ....sandbox.util.retry import (
@@ -1226,14 +1227,7 @@ async def _terminate_pty_entry(self, entry: _E2BPtyProcessEntry) -> None:
12261227
pass
12271228

12281229
def _tar_exclude_args(self) -> list[str]:
1229-
excludes: list[str] = []
1230-
for rel in sorted(self._persist_workspace_skip_relpaths(), key=lambda p: p.as_posix()):
1231-
rel_posix = rel.as_posix().lstrip("/")
1232-
if not rel_posix or rel_posix in {".", "/"}:
1233-
continue
1234-
excludes.append(f"--exclude={shlex.quote(rel_posix)}")
1235-
excludes.append(f"--exclude={shlex.quote(f'./{rel_posix}')}")
1236-
return excludes
1230+
return shell_tar_exclude_args(self._persist_workspace_skip_relpaths())
12371231

12381232
@retry_async(
12391233
retry_if=lambda exc, self, tar_cmd: (
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import annotations
2+
3+
import shlex
4+
from collections.abc import Iterable
5+
from pathlib import Path
6+
7+
__all__ = ["shell_tar_exclude_args"]
8+
9+
10+
def shell_tar_exclude_args(skip_relpaths: Iterable[Path]) -> list[str]:
11+
excludes: list[str] = []
12+
for rel in sorted(skip_relpaths, key=lambda p: p.as_posix()):
13+
rel_posix = rel.as_posix().lstrip("/")
14+
if not rel_posix or rel_posix in {".", "/"}:
15+
continue
16+
excludes.append(f"--exclude={shlex.quote(rel_posix)}")
17+
excludes.append(f"--exclude={shlex.quote(f'./{rel_posix}')}")
18+
return excludes
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from pathlib import Path
2+
3+
from agents.sandbox.session.tar_workspace import shell_tar_exclude_args
4+
5+
6+
def test_shell_tar_exclude_args_skips_empty_and_dot_paths() -> None:
7+
assert shell_tar_exclude_args([Path(""), Path("."), Path("/")]) == []
8+
9+
10+
def test_shell_tar_exclude_args_sorts_and_adds_plain_and_dot_prefixed_patterns() -> None:
11+
assert shell_tar_exclude_args(
12+
[
13+
Path("logs/events.jsonl"),
14+
Path("cache dir/file.txt"),
15+
]
16+
) == [
17+
"--exclude='cache dir/file.txt'",
18+
"--exclude='./cache dir/file.txt'",
19+
"--exclude=logs/events.jsonl",
20+
"--exclude=./logs/events.jsonl",
21+
]
22+
23+
24+
def test_shell_tar_exclude_args_normalizes_absolute_paths() -> None:
25+
assert shell_tar_exclude_args([Path("/tmp/workspace/cache")]) == [
26+
"--exclude=tmp/workspace/cache",
27+
"--exclude=./tmp/workspace/cache",
28+
]

0 commit comments

Comments
 (0)