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}")
|
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")):
|
def setup_certbot_venv(venv_dir: Path = Path("/opt/certbot")):
|
||||||
info = os_release()
|
info = os_release()
|
||||||
distro_id = (info.get("ID") or "").lower()
|
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 = os.environ.copy()
|
||||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||||
|
|
||||||
run(
|
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||||
[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)
|
|
||||||
|
|
||||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||||
pip_ver = run_out([str(pip_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 = os.environ.copy()
|
||||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||||
|
|
||||||
run(
|
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||||
[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)
|
|
||||||
|
|
||||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||||
pip_ver = run_out([str(pip_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 = os.environ.copy()
|
||||||
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
env_build["SETUPTOOLS_USE_DISTUTILS"] = "local"
|
||||||
|
|
||||||
run(
|
_install_certbot_stack(pip_path, certbot_path, env_build)
|
||||||
[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)
|
|
||||||
|
|
||||||
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
cb_ver = run_out([str(certbot_path), "--version"], check=False) or ""
|
||||||
pip_ver = run_out([str(pip_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)
|
run(["chown", "-R", "npm:npm", "/opt/certbot"], check=False)
|
||||||
Path("/etc/letsencrypt").mkdir(parents=True, exist_ok=True)
|
Path("/etc/letsencrypt").mkdir(parents=True, exist_ok=True)
|
||||||
run(["chown", "-R", "npm:npm", "/etc/letsencrypt"], check=False)
|
run(["chown", "-R", "npm:npm", "/etc/letsencrypt"], check=False)
|
||||||
run(
|
# Do not install distro certbot here; use /opt/certbot venv only.
|
||||||
["apt-get", "install", "-y", "--no-install-recommends", "certbot"],
|
|
||||||
check=False,
|
|
||||||
)
|
|
||||||
ini = """text = True
|
ini = """text = True
|
||||||
non-interactive = True
|
non-interactive = True
|
||||||
webroot-path = /data/letsencrypt-acme-challenge
|
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)."""
|
"""Create /etc/angie/metrics.conf (port 82/8282 with console & status)."""
|
||||||
with step("Adding Angie metrics & console on :82 / :8282 (https)"):
|
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;
|
metrics = f"""include /etc/angie/prometheus_all.conf;
|
||||||
server {{
|
server {{
|
||||||
listen 8282 ssl;
|
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)
|
version = github_latest_release_tag(repo, override=None)
|
||||||
print(f"✓ Latest stable version: {version}")
|
print(f"✓ Latest stable version: {version}")
|
||||||
|
|
||||||
|
version_parsed = parse_version(version)
|
||||||
if version_parsed < (2, 13, 0):
|
if version_parsed < (2, 13, 0):
|
||||||
error(f"Version {version} is not supported. Minimum version: 2.13.0")
|
error(f"Version {version} is not supported. Minimum version: 2.13.0")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Check if version >= 2.13.0 - if so, use git instead (releases missing /global)
|
# 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):
|
if version_parsed >= (2, 13, 0):
|
||||||
print(
|
print(
|
||||||
f" Version {version} >= 2.13.0: using git source (release archive incomplete)"
|
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):
|
def create_systemd_units(ipv6_enabled: bool):
|
||||||
with step("Creating and starting systemd services (angie, npm)"):
|
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_lines = [
|
||||||
"[Unit]",
|
"[Unit]",
|
||||||
"Description=Nginx Proxy Manager (backend)",
|
"Description=Nginx Proxy Manager (backend)",
|
||||||
@@ -2955,6 +3007,7 @@ def create_systemd_units(ipv6_enabled: bool):
|
|||||||
"Group=npm",
|
"Group=npm",
|
||||||
"WorkingDirectory=/opt/npm",
|
"WorkingDirectory=/opt/npm",
|
||||||
"Environment=NODE_ENV=production",
|
"Environment=NODE_ENV=production",
|
||||||
|
"Environment=PATH=/opt/certbot/bin:/usr/local/bin:/usr/bin:/bin",
|
||||||
]
|
]
|
||||||
if not ipv6_enabled:
|
if not ipv6_enabled:
|
||||||
unit_lines.append("Environment=DISABLE_IPV6=true")
|
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/npm.service"), "\n".join(unit_lines), 0o644)
|
||||||
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)
|
||||||
|
|
||||||
|
# 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", "restart", "angie.service"], check=False)
|
||||||
subprocess.run(["systemctl", "enable", "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", "restart", "npm.service"], check=False)
|
||||||
subprocess.run(["systemctl", "enable", "npm.service"], check=False)
|
subprocess.run(["systemctl", "enable", "npm.service"], check=False)
|
||||||
@@ -3892,6 +3948,8 @@ def update_only(
|
|||||||
run(["yarn", "install"])
|
run(["yarn", "install"])
|
||||||
|
|
||||||
patch_npm_backend_commands()
|
patch_npm_backend_commands()
|
||||||
|
ensure_certbot_venv_ready()
|
||||||
|
configure_letsencrypt()
|
||||||
create_systemd_units(ipv6_enabled=ipv6_enabled)
|
create_systemd_units(ipv6_enabled=ipv6_enabled)
|
||||||
|
|
||||||
with step("Setting owners"):
|
with step("Setting owners"):
|
||||||
@@ -3907,14 +3965,6 @@ def update_only(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠ Warning: Could not remove dev.conf: {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"):
|
with step("Restarting services after update"):
|
||||||
run(["systemctl", "restart", "angie.service"], check=False)
|
run(["systemctl", "restart", "angie.service"], check=False)
|
||||||
run(["systemctl", "restart", "npm.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_minimum_nodejs(user_requested_version=args.node_version)
|
||||||
ensure_user_and_dirs()
|
ensure_user_and_dirs()
|
||||||
create_sudoers_for_npm()
|
create_sudoers_for_npm()
|
||||||
setup_certbot_venv()
|
ensure_certbot_venv_ready()
|
||||||
configure_letsencrypt()
|
configure_letsencrypt()
|
||||||
|
|
||||||
# ========== INSTALLATION ==========
|
# ========== INSTALLATION ==========
|
||||||
@@ -4262,3 +4312,4 @@ def main():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
signal.signal(signal.SIGINT, lambda s, f: sys.exit(130))
|
signal.signal(signal.SIGINT, lambda s, f: sys.exit(130))
|
||||||
main()
|
main()
|
||||||
|
|
||||||
Reference in New Issue
Block a user