fix install v2.15.X

This commit is contained in:
Mateusz Gruszczyński
2026-06-06 00:01:12 +02:00
parent f9aa0428e0
commit 6901ac8f2d
+77 -10
View File
@@ -1053,10 +1053,51 @@ def _install_certbot_stack(pip_path: Path, certbot_path: Path, env_build: dict):
_ensure_certbot_symlink(certbot_path) _ensure_certbot_symlink(certbot_path)
def _venv_entrypoint_usable(path: Path, args: list[str] | None = None) -> tuple[bool, str]:
"""Return whether a venv script/binary can be executed.
Debian/Ubuntu upgrades can leave /opt/certbot/bin/* wrappers with a stale
shebang, for example /opt/certbot/bin/python3.11 no longer exists. In that
case subprocess may raise FileNotFoundError even though the wrapper file
itself exists. Treat that as a broken venv and rebuild it.
"""
args = args or ["--version"]
if not path.exists():
return False, f"missing: {path}"
try:
first_line = path.read_bytes().splitlines()[0].decode("utf-8", "ignore")
except Exception:
first_line = ""
if first_line.startswith("#!"):
interpreter = first_line[2:].strip().split()[0]
if interpreter and interpreter.startswith("/") and not Path(interpreter).exists():
return False, f"stale interpreter in {path}: {interpreter}"
try:
result = subprocess.run(
[str(path)] + args,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=20,
check=False,
)
if result.returncode == 0:
return True, "ok"
return False, f"{path} exited with code {result.returncode}"
except FileNotFoundError as e:
return False, f"cannot execute {path}: {e}"
except Exception as e:
return False, f"cannot execute {path}: {e}"
def ensure_certbot_venv_ready(venv_dir: Path = Path("/opt/certbot"), force_rebuild: bool = False): def ensure_certbot_venv_ready(venv_dir: Path = Path("/opt/certbot"), force_rebuild: bool = False):
"""Fresh install: build full venv. Update: keep a complete existing venv. """Fresh install: build full venv. Update: keep a complete existing venv.
Rebuild only when forced or when certbot/plugin packages are missing. Rebuild when forced, when packages are missing, or when the existing venv
has stale entrypoints after an OS/Python upgrade.
""" """
certbot_path = venv_dir / "bin" / "certbot" certbot_path = venv_dir / "bin" / "certbot"
pip_path = venv_dir / "bin" / "pip" pip_path = venv_dir / "bin" / "pip"
@@ -1065,23 +1106,45 @@ def ensure_certbot_venv_ready(venv_dir: Path = Path("/opt/certbot"), force_rebui
with step("Removing certbot venv for forced rebuild"): with step("Removing certbot venv for forced rebuild"):
shutil.rmtree(venv_dir, ignore_errors=True) shutil.rmtree(venv_dir, ignore_errors=True)
needs_rebuild = force_rebuild or not certbot_path.exists() or not pip_path.exists() needs_rebuild = force_rebuild
rebuild_reason = "forced rebuild" if force_rebuild else ""
if not needs_rebuild:
pip_ok, pip_reason = _venv_entrypoint_usable(pip_path, ["--version"])
certbot_ok, certbot_reason = _venv_entrypoint_usable(certbot_path, ["--version"])
if not pip_ok or not certbot_ok:
needs_rebuild = True
rebuild_reason = pip_reason if not pip_ok else certbot_reason
if not needs_rebuild: if not needs_rebuild:
for pkg in CERTBOT_REQUIRED_PACKAGES: for pkg in CERTBOT_REQUIRED_PACKAGES:
result = subprocess.run( try:
[str(pip_path), "show", pkg], result = subprocess.run(
stdout=subprocess.DEVNULL, [str(pip_path), "show", pkg],
stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
check=False, stderr=subprocess.DEVNULL,
) check=False,
)
except FileNotFoundError as e:
needs_rebuild = True
rebuild_reason = f"broken pip wrapper: {e}"
break
except Exception as e:
needs_rebuild = True
rebuild_reason = f"cannot verify certbot venv: {e}"
break
if result.returncode != 0: if result.returncode != 0:
needs_rebuild = True needs_rebuild = True
rebuild_reason = f"missing package: {pkg}"
if DEBUG: if DEBUG:
print(f" Certbot venv missing package: {pkg}") print(f" Certbot venv missing package: {pkg}")
break break
if needs_rebuild: if needs_rebuild:
if venv_dir.exists():
with step(f"Removing broken certbot venv ({rebuild_reason})"):
shutil.rmtree(venv_dir, ignore_errors=True)
setup_certbot_venv(venv_dir) setup_certbot_venv(venv_dir)
else: else:
_ensure_certbot_symlink(certbot_path) _ensure_certbot_symlink(certbot_path)
@@ -3851,8 +3914,12 @@ def update_only(
print(" ✓ Updated /etc/angie/angie.conf") print(" ✓ Updated /etc/angie/angie.conf")
if shutil.which("angie"): if shutil.which("angie"):
subprocess.run(["angie", "-t"], check=False) try:
print(" ✓ Config syntax OK") run(["angie", "-t"], check=True)
print(" ✓ Config syntax OK")
except subprocess.CalledProcessError:
print(" ✖ Config syntax failed - keeping backup for manual rollback")
raise
else: else:
if DEBUG: if DEBUG:
print(" ⚠ /etc/angie/angie.conf unchanged (install mode)") print(" ⚠ /etc/angie/angie.conf unchanged (install mode)")