fix: make the self-repo git guard Windows-only
The live-checkout git mutation guard blocked history-rewriting git ops (checkout, reset --hard, rebase, cherry-pick, ...) in the running source checkout and its worktrees on every platform. The hazard it protects against is only real on Windows, where NTFS locks loaded module files and an in-place rewrite can corrupt the running process. On POSIX, open file handles pin the old inodes, so a checkout swap under a running process is safe, and the guard mostly taxed normal dev/salvage workflows with clone workarounds. - tools/self_repo_guard.py: add guard_active() -> os.name == "nt" - tools/terminal_tool.py: consult guard_active() before running the detector; detector logic and block message unchanged for Windows - tests: wiring tests force the guard on; new tests cover the POSIX pass-through and the platform predicate
This commit is contained in:
@@ -32,10 +32,12 @@ def repo(tmp_path):
|
||||
return root.resolve()
|
||||
|
||||
|
||||
def _run(command, config, monkeypatch, repo_root, session_cwds=None, **kwargs):
|
||||
def _run(command, config, monkeypatch, repo_root, session_cwds=None,
|
||||
guard_on=True, **kwargs):
|
||||
from tools.terminal_tool import terminal_tool
|
||||
|
||||
monkeypatch.setattr(self_repo_guard, "get_running_source_root", lambda: repo_root)
|
||||
monkeypatch.setattr(self_repo_guard, "guard_active", lambda: guard_on)
|
||||
mock_env = MagicMock()
|
||||
mock_env.execute.return_value = {"output": "ok", "returncode": 0}
|
||||
mock_env.cwd = config["cwd"]
|
||||
@@ -116,3 +118,19 @@ class TestSelfRepoGuardWiring:
|
||||
result, env = _run("git checkout main", config, monkeypatch, None)
|
||||
assert result.get("status") != "blocked"
|
||||
env.execute.assert_called_once()
|
||||
|
||||
def test_guard_inactive_passes_through(self, repo, monkeypatch):
|
||||
"""POSIX (guard_active() False): mutations in the source repo run."""
|
||||
config = _make_env_config(cwd=str(repo))
|
||||
result, env = _run(
|
||||
"git reset --hard origin/main", config, monkeypatch, repo,
|
||||
guard_on=False,
|
||||
)
|
||||
assert result.get("status") != "blocked"
|
||||
env.execute.assert_called_once()
|
||||
|
||||
def test_guard_active_matches_platform(self):
|
||||
"""guard_active() is True exactly on Windows."""
|
||||
import os
|
||||
|
||||
assert self_repo_guard.guard_active() == (os.name == "nt")
|
||||
|
||||
@@ -695,6 +695,19 @@ def _find_mutation(command: str, cwd: Path, root: Path, depth: int = 0) -> str |
|
||||
return None
|
||||
|
||||
|
||||
def guard_active() -> bool:
|
||||
"""Whether the self-repo git guard applies on this platform.
|
||||
|
||||
Windows-only: NTFS locks loaded .py/.pyd files and an in-place overwrite
|
||||
of the live checkout can corrupt the running process. On POSIX, open file
|
||||
handles keep the old inode alive, so a checkout swap under a running
|
||||
process is safe — already-imported modules keep executing the old code
|
||||
and the mixed-module hazard is limited to later lazy imports, which is
|
||||
not worth blocking every git workflow for.
|
||||
"""
|
||||
return os.name == "nt"
|
||||
|
||||
|
||||
def detect_self_repo_git_mutation(
|
||||
command: str,
|
||||
cwd: str | None,
|
||||
|
||||
+12
-5
@@ -2967,18 +2967,25 @@ def terminal_tool(
|
||||
"status": "blocked"
|
||||
}, ensure_ascii=False)
|
||||
|
||||
# Non-bypassable: rewriting the local checkout backing this interpreter
|
||||
# can mix module versions. Remote backends cannot reach that checkout.
|
||||
# Windows-only: NTFS locks loaded module files, so rewriting the local
|
||||
# checkout backing this interpreter can corrupt the running process.
|
||||
# POSIX keeps old inodes alive for open handles, so the guard is off
|
||||
# there. Remote backends cannot reach that checkout.
|
||||
if env_type == "local":
|
||||
from tools.self_repo_guard import detect_self_repo_git_mutation
|
||||
from tools.self_repo_guard import (
|
||||
detect_self_repo_git_mutation,
|
||||
guard_active,
|
||||
)
|
||||
|
||||
guard_cwd = _resolve_command_cwd(
|
||||
workdir=workdir,
|
||||
default_cwd=cwd,
|
||||
session_key=session_key,
|
||||
)
|
||||
_self_repo_hit, _self_repo_msg = detect_self_repo_git_mutation(
|
||||
command, guard_cwd
|
||||
_self_repo_hit, _self_repo_msg = (
|
||||
detect_self_repo_git_mutation(command, guard_cwd)
|
||||
if guard_active()
|
||||
else (False, None)
|
||||
)
|
||||
if _self_repo_hit:
|
||||
logger.warning(
|
||||
|
||||
Reference in New Issue
Block a user