fix install v2.15.X
This commit is contained in:
+70
-3
@@ -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:
|
||||||
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[str(pip_path), "show", pkg],
|
[str(pip_path), "show", pkg],
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
check=False,
|
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:
|
||||||
|
run(["angie", "-t"], check=True)
|
||||||
print(" ✓ Config syntax OK")
|
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)")
|
||||||
|
|||||||
Reference in New Issue
Block a user