fix install v2.15.X
This commit is contained in:
+141
-90
@@ -972,6 +972,125 @@ def sync_backup_nginx_conf():
|
||||
print(f"Warning: sync failed for {p} -> {target}: {e}")
|
||||
|
||||
|
||||
|
||||
CERTBOT_REQUIRED_PACKAGES = [
|
||||
"certbot",
|
||||
"acme",
|
||||
"certbot-dns-cloudflare",
|
||||
"certbot-dns-rfc2136",
|
||||
]
|
||||
|
||||
|
||||
def _ensure_certbot_symlink(certbot_path: Path):
|
||||
Path("/usr/local/bin").mkdir(parents=True, exist_ok=True)
|
||||
target = Path("/usr/local/bin/certbot")
|
||||
if target.exists() or target.is_symlink():
|
||||
try:
|
||||
target.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
target.symlink_to(certbot_path)
|
||||
|
||||
|
||||
def _detect_certbot_version(certbot_path: Path) -> str:
|
||||
cb_ver = run_out([str(certbot_path), "--version"], check=False).strip()
|
||||
m = re.search(r"(\d+\.\d+\.\d+)", cb_ver)
|
||||
if not m:
|
||||
raise RuntimeError(f"Cannot detect certbot version from: {cb_ver!r}")
|
||||
return m.group(1)
|
||||
|
||||
|
||||
def _install_certbot_stack(pip_path: Path, certbot_path: Path, env_build: dict):
|
||||
"""Install Certbot and DNS plugins in one venv with matching versions.
|
||||
|
||||
NPM installs DNS plugins on startup when they are missing. On non-docker
|
||||
installs this can fail with 'acme==undefined'. We prevent that by making
|
||||
the venv complete before npm.service starts.
|
||||
"""
|
||||
run(
|
||||
[str(pip_path), "install", "-U", "pip", "setuptools", "wheel"],
|
||||
env=env_build,
|
||||
)
|
||||
run(
|
||||
[
|
||||
str(pip_path),
|
||||
"install",
|
||||
"-U",
|
||||
"cryptography",
|
||||
"cffi",
|
||||
"certbot",
|
||||
"tldextract",
|
||||
],
|
||||
env=env_build,
|
||||
)
|
||||
|
||||
certbot_ver = _detect_certbot_version(certbot_path)
|
||||
run(
|
||||
[
|
||||
str(pip_path),
|
||||
"install",
|
||||
"-U",
|
||||
f"acme=={certbot_ver}",
|
||||
f"certbot-dns-cloudflare=={certbot_ver}",
|
||||
f"certbot-dns-rfc2136=={certbot_ver}",
|
||||
],
|
||||
env=env_build,
|
||||
)
|
||||
|
||||
missing = []
|
||||
for pkg in CERTBOT_REQUIRED_PACKAGES:
|
||||
result = subprocess.run(
|
||||
[str(pip_path), "show", pkg],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
missing.append(pkg)
|
||||
if missing:
|
||||
raise RuntimeError(f"Certbot venv incomplete, missing: {', '.join(missing)}")
|
||||
|
||||
_ensure_certbot_symlink(certbot_path)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Rebuild only when forced or when certbot/plugin packages are missing.
|
||||
"""
|
||||
certbot_path = venv_dir / "bin" / "certbot"
|
||||
pip_path = venv_dir / "bin" / "pip"
|
||||
|
||||
if force_rebuild and venv_dir.exists():
|
||||
with step("Removing certbot venv for forced rebuild"):
|
||||
shutil.rmtree(venv_dir, ignore_errors=True)
|
||||
|
||||
needs_rebuild = force_rebuild or not certbot_path.exists() or not pip_path.exists()
|
||||
|
||||
if not needs_rebuild:
|
||||
for pkg in CERTBOT_REQUIRED_PACKAGES:
|
||||
result = subprocess.run(
|
||||
[str(pip_path), "show", pkg],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
needs_rebuild = True
|
||||
if DEBUG:
|
||||
print(f" Certbot venv missing package: {pkg}")
|
||||
break
|
||||
|
||||
if needs_rebuild:
|
||||
setup_certbot_venv(venv_dir)
|
||||
else:
|
||||
_ensure_certbot_symlink(certbot_path)
|
||||
cb_ver = run_out([str(certbot_path), "--version"], check=False).strip()
|
||||
print(f"✓ Existing certbot venv is complete: {cb_ver}")
|
||||
|
||||
run(["chown", "-R", "npm:npm", str(venv_dir)], check=False)
|
||||
return True
|
||||
|
||||
def setup_certbot_venv(venv_dir: Path = Path("/opt/certbot")):
|
||||
info = os_release()
|
||||
distro_id = (info.get("ID") or "").lower()
|
||||
@@ -1010,31 +1129,7 @@ def setup_certbot_venv(venv_dir: Path = Path("/opt/certbot")):
|
||||
env_build = os.environ.copy()
|
||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||
|
||||
run(
|
||||
[str(pip_path), "install", "-U", "pip", "setuptools", "wheel"],
|
||||
env=env_build,
|
||||
)
|
||||
run(
|
||||
[
|
||||
str(pip_path),
|
||||
"install",
|
||||
"-U",
|
||||
"cryptography",
|
||||
"cffi",
|
||||
"certbot",
|
||||
"tldextract",
|
||||
],
|
||||
env=env_build,
|
||||
)
|
||||
|
||||
Path("/usr/local/bin").mkdir(parents=True, exist_ok=True)
|
||||
target = Path("/usr/local/bin/certbot")
|
||||
if target.exists() or target.is_symlink():
|
||||
try:
|
||||
target.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
target.symlink_to(certbot_path)
|
||||
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||
|
||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||
pip_ver = run_out([str(pip_path), "--version"], check=False) or ""
|
||||
@@ -1073,31 +1168,7 @@ def setup_certbot_venv(venv_dir: Path = Path("/opt/certbot")):
|
||||
env_build = os.environ.copy()
|
||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||
|
||||
run(
|
||||
[str(pip_path), "install", "-U", "pip", "setuptools", "wheel"],
|
||||
env=env_build,
|
||||
)
|
||||
run(
|
||||
[
|
||||
str(pip_path),
|
||||
"install",
|
||||
"-U",
|
||||
"cryptography",
|
||||
"cffi",
|
||||
"certbot",
|
||||
"tldextract",
|
||||
],
|
||||
env=env_build,
|
||||
)
|
||||
|
||||
Path("/usr/local/bin").mkdir(parents=True, exist_ok=True)
|
||||
target = Path("/usr/local/bin/certbot")
|
||||
if target.exists() or target.is_symlink():
|
||||
try:
|
||||
target.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
target.symlink_to(certbot_path)
|
||||
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||
|
||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||
pip_ver = run_out([str(pip_path), "--version"], check=False) or ""
|
||||
@@ -1249,31 +1320,7 @@ fi
|
||||
env_build = os.environ.copy()
|
||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||
|
||||
run(
|
||||
[str(pip_path), "install", "-U", "pip", "setuptools", "wheel"],
|
||||
env=env_build,
|
||||
)
|
||||
run(
|
||||
[
|
||||
str(pip_path),
|
||||
"install",
|
||||
"-U",
|
||||
"cryptography",
|
||||
"cffi",
|
||||
"certbot",
|
||||
"tldextract",
|
||||
],
|
||||
env=env_build,
|
||||
)
|
||||
|
||||
Path("/usr/local/bin").mkdir(parents=True, exist_ok=True)
|
||||
target = Path("/usr/local/bin/certbot")
|
||||
if target.exists() or target.is_symlink():
|
||||
try:
|
||||
target.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
target.symlink_to(certbot_path)
|
||||
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||
|
||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||
pip_ver = run_out([str(pip_path), "--version"], check=False) or ""
|
||||
@@ -1289,10 +1336,7 @@ def configure_letsencrypt():
|
||||
run(["chown", "-R", "npm:npm", "/opt/certbot"], check=False)
|
||||
Path("/etc/letsencrypt").mkdir(parents=True, exist_ok=True)
|
||||
run(["chown", "-R", "npm:npm", "/etc/letsencrypt"], check=False)
|
||||
run(
|
||||
["apt-get", "install", "-y", "--no-install-recommends", "certbot"],
|
||||
check=False,
|
||||
)
|
||||
# Do not install distro certbot here; use /opt/certbot venv only.
|
||||
ini = """text = True
|
||||
non-interactive = True
|
||||
webroot-path = /data/letsencrypt-acme-challenge
|
||||
@@ -1625,6 +1669,9 @@ def write_metrics_files():
|
||||
|
||||
"""Create /etc/angie/metrics.conf (port 82/8282 with console & status)."""
|
||||
with step("Adding Angie metrics & console on :82 / :8282 (https)"):
|
||||
if NPM_ADMIN_ENABLE_SSL:
|
||||
generate_selfsigned_cert()
|
||||
|
||||
metrics = f"""include /etc/angie/prometheus_all.conf;
|
||||
server {{
|
||||
listen 8282 ssl;
|
||||
@@ -2697,12 +2744,12 @@ def deploy_npm_app_from_release(version: str | None) -> str:
|
||||
version = github_latest_release_tag(repo, override=None)
|
||||
print(f"✓ Latest stable version: {version}")
|
||||
|
||||
version_parsed = parse_version(version)
|
||||
if version_parsed < (2, 13, 0):
|
||||
error(f"Version {version} is not supported. Minimum version: 2.13.0")
|
||||
sys.exit(1)
|
||||
|
||||
# Check if version >= 2.13.0 - if so, use git instead (releases missing /global)
|
||||
version_parsed = parse_version(version)
|
||||
if version_parsed >= (2, 13, 0):
|
||||
print(
|
||||
f" Version {version} >= 2.13.0: using git source (release archive incomplete)"
|
||||
@@ -2944,6 +2991,11 @@ exec /usr/sbin/logrotate -s {state_file} "$@"
|
||||
|
||||
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()
|
||||
|
||||
unit_lines = [
|
||||
"[Unit]",
|
||||
"Description=Nginx Proxy Manager (backend)",
|
||||
@@ -2955,6 +3007,7 @@ def create_systemd_units(ipv6_enabled: bool):
|
||||
"Group=npm",
|
||||
"WorkingDirectory=/opt/npm",
|
||||
"Environment=NODE_ENV=production",
|
||||
"Environment=PATH=/opt/certbot/bin:/usr/local/bin:/usr/bin:/bin",
|
||||
]
|
||||
if not ipv6_enabled:
|
||||
unit_lines.append("Environment=DISABLE_IPV6=true")
|
||||
@@ -2971,9 +3024,12 @@ 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/angie.service"), ANGIE_UNIT, 0o644)
|
||||
subprocess.run(["systemctl", "daemon-reload"], check=False)
|
||||
|
||||
# Validate configuration before touching the running service.
|
||||
run(["/usr/sbin/angie", "-t"], check=True)
|
||||
|
||||
subprocess.run(["systemctl", "restart", "angie.service"], check=False)
|
||||
subprocess.run(["systemctl", "enable", "angie.service"], check=False)
|
||||
run(["/usr/sbin/angie", "-t"], check=False)
|
||||
|
||||
subprocess.run(["systemctl", "restart", "npm.service"], check=False)
|
||||
subprocess.run(["systemctl", "enable", "npm.service"], check=False)
|
||||
@@ -3892,6 +3948,8 @@ def update_only(
|
||||
run(["yarn", "install"])
|
||||
|
||||
patch_npm_backend_commands()
|
||||
ensure_certbot_venv_ready()
|
||||
configure_letsencrypt()
|
||||
create_systemd_units(ipv6_enabled=ipv6_enabled)
|
||||
|
||||
with step("Setting owners"):
|
||||
@@ -3907,14 +3965,6 @@ def update_only(
|
||||
except Exception as e:
|
||||
print(f" ⚠ Warning: Could not remove dev.conf: {e}")
|
||||
|
||||
certbot_venv = Path("/opt/certbot")
|
||||
if certbot_venv.exists:
|
||||
print(f"♻ Removing stale certbot venv for rebuild...")
|
||||
shutil.rmtree(certbot_venv, ignore_errors=True)
|
||||
|
||||
setup_certbot_venv()
|
||||
configure_letsencrypt()
|
||||
|
||||
with step("Restarting services after update"):
|
||||
run(["systemctl", "restart", "angie.service"], check=False)
|
||||
run(["systemctl", "restart", "npm.service"], check=False)
|
||||
@@ -4127,7 +4177,7 @@ def main():
|
||||
ensure_minimum_nodejs(user_requested_version=args.node_version)
|
||||
ensure_user_and_dirs()
|
||||
create_sudoers_for_npm()
|
||||
setup_certbot_venv()
|
||||
ensure_certbot_venv_ready()
|
||||
configure_letsencrypt()
|
||||
|
||||
# ========== INSTALLATION ==========
|
||||
@@ -4262,3 +4312,4 @@ def main():
|
||||
if __name__ == "__main__":
|
||||
signal.signal(signal.SIGINT, lambda s, f: sys.exit(130))
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user