mirror of
https://github.com/usestrix/strix.git
synced 2026-08-28 05:25:00 +00:00
fix(update): strip PyInstaller bootloader env vars when re-execing after self-update
This commit is contained in:
parent
187f41f36f
commit
b3859ec489
3 changed files with 59 additions and 2 deletions
|
|
@ -6,7 +6,6 @@ Strix Agent Interface
|
|||
import argparse
|
||||
import asyncio
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -36,6 +35,7 @@ from strix.interface.update_check import (
|
|||
is_binary_install,
|
||||
notify_update,
|
||||
prompt_update_if_available,
|
||||
restart_after_update,
|
||||
start_background_check,
|
||||
)
|
||||
from strix.interface.utils import (
|
||||
|
|
@ -440,7 +440,7 @@ def main() -> None:
|
|||
start_background_check()
|
||||
if not args.non_interactive and prompt_update_if_available(Console()):
|
||||
if is_binary_install() and sys.platform != "win32":
|
||||
os.execv(sys.executable, sys.argv) # noqa: S606 # nosec B606
|
||||
restart_after_update()
|
||||
sys.exit(0)
|
||||
|
||||
check_docker_installed()
|
||||
|
|
|
|||
|
|
@ -264,6 +264,36 @@ def prompt_update_if_available(console: Console) -> bool:
|
|||
return run_package_upgrade(console, method)
|
||||
|
||||
|
||||
def restart_env() -> dict[str, str]:
|
||||
"""Environment for re-exec'ing the binary after a self-update.
|
||||
|
||||
The PyInstaller bootloader marks its child process via environment
|
||||
variables (``_MEIPASS2`` on older versions, ``_PYI_*`` on 6.x) that
|
||||
point at the already-extracted archive of the *running* version. If
|
||||
they leak into the re-exec'd process, the new binary skips extraction
|
||||
and runs the old code, so the update never appears to take effect.
|
||||
Library-path variables the bootloader overrode are restored from the
|
||||
``*_ORIG`` copies it saved.
|
||||
"""
|
||||
env = {
|
||||
key: value
|
||||
for key, value in os.environ.items()
|
||||
if key != "_MEIPASS2" and not key.startswith("_PYI_")
|
||||
}
|
||||
for var in ("LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"):
|
||||
orig = env.pop(f"{var}_ORIG", None)
|
||||
if orig is not None:
|
||||
env[var] = orig
|
||||
elif var in os.environ:
|
||||
env.pop(var, None)
|
||||
return env
|
||||
|
||||
|
||||
def restart_after_update() -> None:
|
||||
"""Replace the current process with the freshly updated binary."""
|
||||
os.execve(sys.executable, sys.argv, restart_env()) # noqa: S606 # nosec B606
|
||||
|
||||
|
||||
def _release_target() -> str | None:
|
||||
raw_os = platform.system().lower()
|
||||
os_name = {"darwin": "macos", "linux": "linux", "windows": "windows"}.get(raw_os)
|
||||
|
|
|
|||
|
|
@ -153,6 +153,33 @@ def test_self_update_already_latest(monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
assert update_check.self_update() is True
|
||||
|
||||
|
||||
def test_restart_env_strips_pyinstaller_vars(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("_MEIPASS2", "/stale/_MEIold")
|
||||
monkeypatch.setenv("_PYI_APPLICATION_HOME_DIR", "/stale/_MEIold")
|
||||
monkeypatch.setenv("_PYI_ARCHIVE_FILE", "/old/strix")
|
||||
monkeypatch.setenv("_PYI_PARENT_PROCESS_LEVEL", "1")
|
||||
monkeypatch.setenv("SOME_OTHER_VAR", "kept")
|
||||
|
||||
env = update_check.restart_env()
|
||||
|
||||
assert "SOME_OTHER_VAR" in env
|
||||
assert "_MEIPASS2" not in env
|
||||
assert not any(key.startswith("_PYI_") for key in env)
|
||||
|
||||
|
||||
def test_restart_env_restores_library_paths(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("LD_LIBRARY_PATH", "/stale/_MEIold/lib")
|
||||
monkeypatch.setenv("LD_LIBRARY_PATH_ORIG", "/usr/lib/custom")
|
||||
monkeypatch.setenv("DYLD_LIBRARY_PATH", "/stale/_MEIold/lib")
|
||||
monkeypatch.delenv("DYLD_LIBRARY_PATH_ORIG", raising=False)
|
||||
|
||||
env = update_check.restart_env()
|
||||
|
||||
assert env["LD_LIBRARY_PATH"] == "/usr/lib/custom"
|
||||
assert "LD_LIBRARY_PATH_ORIG" not in env
|
||||
assert "DYLD_LIBRARY_PATH" not in env
|
||||
|
||||
|
||||
def test_sha256_file(tmp_path: Path) -> None:
|
||||
path = tmp_path / "blob"
|
||||
path.write_bytes(b"strix")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue