fix(install): abort Windows venv recreate when rename-aside fails

When Rename-Item on the live venv is denied, do not fall back to an
in-place Remove-Item that can gut site-packages and leave no rollback.
Also mark venv-blocker probe failures with probe_failed so they cannot
be read as a clear scan (#83149).
This commit is contained in:
HexLab98
2026-08-10 19:24:17 +07:00
committed by Teknium
parent d82dcf5da0
commit 18b442cdeb
2 changed files with 30 additions and 8 deletions
+18 -4
View File
@@ -28,14 +28,28 @@ _SENSITIVE_LONG_FLAGS: list[str] = [
]
def _probe_fail_json() -> str:
"""Return the standard probe-failure JSON document."""
return json.dumps({"ok": False, "blocked": False, "processes": []})
def _probe_fail_json(diagnostic: str = "probe failed") -> str:
"""Return the standard probe-failure JSON document.
``ok: false`` plus ``probe_failed: true`` means the detector itself could
not run — this is *not* a clear scan. Callers must treat
``ok is not True`` / non-zero exit as probe failure, never as
``blocked: false`` "clear" (#83149).
"""
return json.dumps(
{
"ok": False,
"probe_failed": True,
"blocked": False,
"processes": [],
"error": diagnostic,
}
)
def _emit_probe_fail(diagnostic: str) -> NoReturn:
"""Print one JSON to stdout, diagnostic to stderr, exit non-zero."""
print(_probe_fail_json())
print(_probe_fail_json(diagnostic))
print(diagnostic, file=sys.stderr)
sys.exit(1)
+12 -4
View File
@@ -2460,15 +2460,23 @@ function Install-Venv {
}
# Move the old venv aside before creating its replacement. A directory
# rename is atomic on the same volume and does not require deleting
# files mapped as DLLs. Never fall back to deleting the live venv:
# Windows can remove unlocked files first, then fail on one locked
# file, leaving no usable interpreter and no rollback source.
# files mapped as DLLs. NEVER fall back to deleting the live venv
# (#83149): Remove-Item -Recurse can delete most of site-packages and
# then fail on one locked .pyd, leaving a gutted venv with no usable
# interpreter and no rollback source. Abort with the previous install
# intact so the user can close holders and retry.
$venvBackupName = "venv.stale.{0}-{1}" -f (Get-Date -Format "yyyyMMddHHmmss"), ([Guid]::NewGuid().ToString("N"))
try {
Rename-Item -LiteralPath "venv" -NewName $venvBackupName -ErrorAction Stop
$venvParked = $true
} catch {
throw "Could not move existing venv aside without deleting it. Close running Hermes processes and retry. $($_.Exception.Message)"
$renameErr = $_.Exception.Message
throw (
"Could not move the existing venv aside ($renameErr). " +
"A process still has the install directory open (often a non-Hermes " +
"python.exe that resolved into this venv via PATH). Close those " +
"processes and retry - the previous install was left intact."
)
}
}