fix install v2.15.X

This commit is contained in:
Mateusz Gruszczyński
2026-06-06 00:37:24 +02:00
parent 82afe662b3
commit 9ececc5843
+110 -6
View File
@@ -1074,6 +1074,79 @@ def _detect_certbot_version(certbot_path: Path) -> str:
return m.group(1) return m.group(1)
def certbot_version_for_npm() -> str:
"""Return the certbot version that NPM must expose as CERTBOT_VERSION.
NPM v2.15.x replaces {{certbot-version}} in dns-plugins.json from the
CERTBOT_VERSION environment variable. If it is missing, Node turns it into
"undefined" and pip receives invalid requirements such as acme==undefined.
"""
candidates = [
Path("/opt/certbot/bin/certbot"),
Path("/usr/local/bin/certbot"),
]
for certbot_path in candidates:
if certbot_path.exists():
try:
return _detect_certbot_version(certbot_path)
except Exception:
pass
if shutil.which("certbot"):
try:
out = run_out(["certbot", "--version"], check=False).strip()
m = re.search(r"(\d+\.\d+\.\d+)", out)
if m:
return m.group(1)
except Exception:
pass
return ""
def patch_npm_certbot_plugins_config() -> str:
"""Patch NPM DNS plugin metadata with a concrete certbot version.
This is a non-docker install. NPM normally expects CERTBOT_VERSION in the
container environment. We set the env in systemd and also replace the JSON
placeholders after every install/update so startup cannot generate
acme==undefined even if the process environment is incomplete.
"""
certbot_ver = certbot_version_for_npm()
if not certbot_ver:
print("⚠ Could not detect Certbot version for NPM DNS plugin metadata")
return ""
os.environ["CERTBOT_VERSION"] = certbot_ver
patched = []
for path in [
Path("/opt/npm/certbot/dns-plugins.json"),
Path("/opt/npm/backend/certbot/dns-plugins.json"),
]:
if not path.exists():
continue
try:
txt = path.read_text(encoding="utf-8")
new = txt.replace("{{certbot-version}}", certbot_ver)
new = new.replace("acme==undefined", f"acme=={certbot_ver}")
if new != txt:
path.write_text(new, encoding="utf-8")
patched.append(str(path))
except Exception as e:
print(f"⚠ Could not patch {path}: {e}")
if patched:
print(f"✔ Patched NPM Certbot plugin metadata: Certbot {certbot_ver}")
if DEBUG:
for path in patched:
print(f" - {path}")
else:
print(f"✔ NPM Certbot plugin metadata ready: Certbot {certbot_ver}")
return certbot_ver
def run_logged(cmd, log_path: Path, timeout=1200, check=True, env=None): def run_logged(cmd, log_path: Path, timeout=1200, check=True, env=None):
"""Run command with stdout/stderr saved to a log file. """Run command with stdout/stderr saved to a log file.
@@ -3123,12 +3196,18 @@ exec /usr/sbin/logrotate -s {state_file} "$@"
print(f"⚠ Warning: could not fix {logrotate_dir} permissions: {e}") print(f"⚠ Warning: could not fix {logrotate_dir} permissions: {e}")
def create_systemd_units(ipv6_enabled: bool):
with step("Creating and starting systemd services (angie, npm)"): def write_npm_service_unit(ipv6_enabled: bool, include_certbot_version: bool = True):
# Some configs may already reference the admin/metrics SSL certificate. """Write npm.service.
# Ensure it exists before the first Angie config test/restart.
if NPM_ADMIN_ENABLE_SSL: During update this refreshes an older unit so NPM receives
generate_selfsigned_cert() CERTBOT_VERSION and does not expand DNS plugin requirements to
acme==undefined. Fresh installs also use this helper when creating
the service from scratch.
"""
certbot_ver = ""
if include_certbot_version:
certbot_ver = patch_npm_certbot_plugins_config()
unit_lines = [ unit_lines = [
"[Unit]", "[Unit]",
@@ -3143,6 +3222,8 @@ def create_systemd_units(ipv6_enabled: bool):
"Environment=NODE_ENV=production", "Environment=NODE_ENV=production",
"Environment=PATH=/opt/certbot/bin:/usr/local/bin:/usr/bin:/bin", "Environment=PATH=/opt/certbot/bin:/usr/local/bin:/usr/bin:/bin",
] ]
if certbot_ver:
unit_lines.append(f"Environment=CERTBOT_VERSION={certbot_ver}")
if not ipv6_enabled: if not ipv6_enabled:
unit_lines.append("Environment=DISABLE_IPV6=true") unit_lines.append("Environment=DISABLE_IPV6=true")
unit_lines += [ unit_lines += [
@@ -3156,6 +3237,26 @@ def create_systemd_units(ipv6_enabled: bool):
] ]
write_file(Path("/etc/systemd/system/npm.service"), "\n".join(unit_lines), 0o644) write_file(Path("/etc/systemd/system/npm.service"), "\n".join(unit_lines), 0o644)
subprocess.run(["systemctl", "daemon-reload"], check=False)
if certbot_ver:
print(f"✔ npm.service updated with CERTBOT_VERSION={certbot_ver}")
else:
print("✔ npm.service updated")
return certbot_ver
def refresh_npm_service_for_update(ipv6_enabled: bool):
with step("Updating npm.service for update (CERTBOT_VERSION)"):
return write_npm_service_unit(ipv6_enabled=ipv6_enabled, include_certbot_version=True)
def create_systemd_units(ipv6_enabled: bool):
with step("Creating and starting systemd services (angie, npm)"):
# Some configs may already reference the admin/metrics SSL certificate.
# Ensure it exists before the first Angie config test/restart.
if NPM_ADMIN_ENABLE_SSL:
generate_selfsigned_cert()
write_npm_service_unit(ipv6_enabled=ipv6_enabled, include_certbot_version=True)
write_file(Path("/etc/systemd/system/angie.service"), ANGIE_UNIT, 0o644) write_file(Path("/etc/systemd/system/angie.service"), ANGIE_UNIT, 0o644)
subprocess.run(["systemctl", "daemon-reload"], check=False) subprocess.run(["systemctl", "daemon-reload"], check=False)
ensure_angie_log_include_files() ensure_angie_log_include_files()
@@ -4098,6 +4199,8 @@ def update_only(
patch_npm_backend_commands() patch_npm_backend_commands()
ensure_certbot_venv_ready() ensure_certbot_venv_ready()
patch_npm_certbot_plugins_config()
refresh_npm_service_for_update(ipv6_enabled=ipv6_enabled)
configure_letsencrypt() configure_letsencrypt()
create_systemd_units(ipv6_enabled=ipv6_enabled) create_systemd_units(ipv6_enabled=ipv6_enabled)
@@ -4415,6 +4518,7 @@ def main():
"branch": args.branch if installed_from_branch else None, "branch": args.branch if installed_from_branch else None,
}) })
patch_npm_certbot_plugins_config()
create_systemd_units(ipv6_enabled=args.enable_ipv6) create_systemd_units(ipv6_enabled=args.enable_ipv6)
ensure_nginx_symlink() ensure_nginx_symlink()