Files
RustProxy/tools/checks/release_0317.py
T
2026-10-11 22:55:22 +02:00

102 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""0.31.7 offline update-retention regression without systemd or root privileges."""
from __future__ import annotations
import os
from pathlib import Path
import subprocess
import tempfile
import time
ROOT = Path(__file__).resolve().parents[2]
UPDATER = ROOT / "update.sh"
def mkdir_age(path: Path, days: int) -> None:
path.mkdir(parents=True)
stamp = time.time() - days * 86400
os.utime(path, (stamp, stamp))
def call(update_prefix: Path, cargo_target: Path, mockbin: Path, *, fail: bool = False, plan: bool = False) -> subprocess.CompletedProcess[str]:
env = os.environ.copy()
env["PATH"] = f"{mockbin}:{env.get('PATH', '')}"
env["CARGO_TARGET_DIR"] = str(cargo_target)
env["RP_TEST_UPDATE_FAIL"] = "1" if fail else "0"
args = ["bash", str(UPDATER), "--prefix", str(update_prefix)]
if plan:
args.append("--plan")
return subprocess.run(args, env=env, capture_output=True, text=True, timeout=15, check=False)
def main() -> None:
subprocess.run(["bash", "-n", str(UPDATER)], check=True)
with tempfile.TemporaryDirectory(prefix="rp-release-0317-") as tmp:
root = Path(tmp)
prefix = root / "installed rustproxy"
releases = prefix / "releases"
backups = prefix / "backups"
releases.mkdir(parents=True)
backups.mkdir()
for name, age in [("r-new", 0), ("r-second", 2), ("r-live", 8), ("r-ancient", 30), ("r-older extra", 40)]:
mkdir_age(releases / name, age)
(prefix / "current").symlink_to(releases / "r-live", target_is_directory=True)
for days in range(1, 6):
mkdir_age(backups / f"before-{days}", days)
target = root / "target"
for profile in ["release", "debug"]:
(target / profile / "deps").mkdir(parents=True)
(target / profile / ".fingerprint").mkdir()
(target / profile / "build").mkdir()
(target / profile / "rust-proxy").write_text("compiled binary")
mkdir_age(target / profile / "incremental" / "old", 50)
mkdir_age(target / profile / "incremental" / "new", 1)
mockbin = root / "bin"
mockbin.mkdir()
stub = mockbin / "python3"
stub.write_text("#!/bin/sh\n[ \"${RP_TEST_UPDATE_FAIL:-0}\" != 1 ] || exit 17\nexit 0\n")
stub.chmod(0o755)
result = call(prefix, target, mockbin, fail=True)
assert result.returncode == 17, result.stderr
assert (releases / "r-older extra").exists(), "failed update cleaned releases"
assert (target / "release" / "incremental" / "old").exists(), "failed update cleaned target"
result = call(prefix, target, mockbin, plan=True)
assert result.returncode == 0, result.stderr
assert (releases / "r-older extra").exists(), "planned update cleaned releases"
result = call(prefix, target, mockbin)
assert result.returncode == 0, (result.stdout, result.stderr)
assert sorted(path.name for path in releases.iterdir()) == ["r-live", "r-new"], result.stdout
assert sorted(path.name for path in backups.iterdir()) == ["before-1", "before-2", "before-3"], result.stdout
assert (prefix / "current").resolve() == releases / "r-live", "current symlink changed"
for profile in ["debug", "release"]:
assert not (target / profile / "incremental" / "old").exists(), "old incremental retained"
assert (target / profile / "incremental" / "new").exists(), "recent incremental removed"
for artifact in ["deps", ".fingerprint", "build", "rust-proxy", ".cargo-lock"]:
assert (target / profile / artifact).exists(), f"removed Cargo artifact: {profile}/{artifact}"
# Safety: do not touch target if a parent directory is a symlink.
target_symlink_root = root / "symlink"
target_symlink_root.symlink_to(target, target_is_directory=True)
mkdir_age(target / "release" / "incremental" / "symlink-test", 50)
result = call(prefix, target_symlink_root, mockbin)
assert result.returncode == 0, result.stderr
assert (target / "release" / "incremental" / "symlink-test").exists(), "followed symlinked target"
# Static source contracts for the managed-update cache handoff.
deploy = (ROOT / "scripts/deploy/deploy_runtime.py").read_text()
runtime = (ROOT / "src/main/admin_runtime.rs").read_text()
demote = (ROOT / "src/cache/cold/demotion.rs").read_text()
assert "stop_for_managed_update(data)" in deploy
assert "run(['systemctl', 'stop', SERVICE])" in deploy
assert "data_dir.join(\".managed-update\").is_file()" in runtime
assert "state.cache.flush_hot_to_cold_for_update()" in runtime
assert "self.flush_hot_to_cold_inner(true).await" in demote
print("PASS: 0.31.7 failed/plan/successful update, two releases + three backups, Cargo safety/symlink guards, managed-update flush wiring.")
if __name__ == "__main__":
main()