mirror of
https://github.com/usestrix/strix.git
synced 2026-08-16 09:26:39 +02:00
runtime: resolve staged local-dir path to avoid symlink rejection on macOS (#857)
This commit is contained in:
@@ -110,7 +110,7 @@ def stage_symlink_safe_dir(src_root: Path) -> tuple[Path, Path | None]:
|
|||||||
if not tree_has_symlink(root):
|
if not tree_has_symlink(root):
|
||||||
return root, None
|
return root, None
|
||||||
|
|
||||||
staged = Path(tempfile.mkdtemp(prefix=_STAGING_PREFIX))
|
staged = Path(tempfile.mkdtemp(prefix=_STAGING_PREFIX)).resolve()
|
||||||
try:
|
try:
|
||||||
_stage_dir(root, staged, root, frozenset({root}))
|
_stage_dir(root, staged, root, frozenset({root}))
|
||||||
except OSError:
|
except OSError:
|
||||||
|
|||||||
@@ -106,3 +106,38 @@ def test_nested_symlinks_inside_linked_dir(tmp_path: Path) -> None:
|
|||||||
assert (staged / "pkg" / "shared_link" / "conf.json").read_text() == "{}\n"
|
assert (staged / "pkg" / "shared_link" / "conf.json").read_text() == "{}\n"
|
||||||
assert not (staged / "pkg" / "shared_link" / "escape").exists()
|
assert not (staged / "pkg" / "shared_link" / "escape").exists()
|
||||||
assert not (staged / "shared" / "escape").exists()
|
assert not (staged / "shared" / "escape").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_staged_path_has_no_symlink_ancestor(tmp_path: Path, monkeypatch) -> None: # noqa: ANN001
|
||||||
|
"""The staging directory itself must never sit behind a symlink.
|
||||||
|
|
||||||
|
``tempfile.mkdtemp()`` honors ``$TMPDIR``, and on macOS the default
|
||||||
|
``$TMPDIR`` resolves through ``/var``, which is itself a symlink to
|
||||||
|
``/private/var``. ``LocalDir`` rejects any symlink component in its
|
||||||
|
source path, so returning the raw ``mkdtemp()`` result breaks every
|
||||||
|
local-dir upload on macOS whenever the source tree contains a symlink.
|
||||||
|
This reproduces that shape without depending on the host OS layout.
|
||||||
|
"""
|
||||||
|
repo = _make_repo(tmp_path)
|
||||||
|
(repo / "link.py").symlink_to(repo / "pkg" / "mod.py")
|
||||||
|
|
||||||
|
real_tmp_root = tmp_path / "real_tmp"
|
||||||
|
real_tmp_root.mkdir()
|
||||||
|
symlinked_tmp_root = tmp_path / "tmp_symlink"
|
||||||
|
symlinked_tmp_root.symlink_to(real_tmp_root)
|
||||||
|
|
||||||
|
def fake_mkdtemp(prefix: str = "") -> str:
|
||||||
|
real_dir = real_tmp_root / f"{prefix}fake"
|
||||||
|
real_dir.mkdir()
|
||||||
|
return str(symlinked_tmp_root / real_dir.name)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"strix.runtime.local_dir_staging.tempfile.mkdtemp", fake_mkdtemp
|
||||||
|
)
|
||||||
|
|
||||||
|
upload_path, staged = stage_symlink_safe_dir(repo)
|
||||||
|
|
||||||
|
assert staged is not None
|
||||||
|
assert upload_path == staged
|
||||||
|
for path in (staged, *staged.parents):
|
||||||
|
assert not path.is_symlink(), f"staged path has a symlink ancestor: {path}"
|
||||||
|
|||||||
Reference in New Issue
Block a user