From 61a7b9620d018a4a79e737cea60acba8fd37c048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Fri, 24 Oct 2025 15:38:24 +0200 Subject: [PATCH] pyenv --- npm_install.py | 191 ++++++++++++++++++++++++------------------------- 1 file changed, 93 insertions(+), 98 deletions(-) diff --git a/npm_install.py b/npm_install.py index ccead1d..0fb65a2 100644 --- a/npm_install.py +++ b/npm_install.py @@ -214,127 +214,122 @@ def sync_backup_nginx_conf(): def setup_certbot_venv(venv_dir: Path = Path("/opt/certbot")): PYENV_ROOT = Path("/opt/npm/.pyenv") PYENV_OWNER = "npm" - PARENT_HOME = Path("/opt/npm") PYTHON_VERSION = "3.11.11" + PYENV_BIN_CANDIDATES = ["pyenv", "/usr/bin/pyenv", "/usr/lib/pyenv/bin/pyenv"] - def _is_ubuntu() -> bool: - try: - with open("/etc/os-release") as f: - return any(line.strip().lower() == "id=ubuntu" for line in f) - except Exception: - return False + try: + apt_try_install([ + "pyenv", "build-essential", "gcc", "make", "pkg-config", + "libssl-dev", "zlib1g-dev", "libbz2-dev", "libreadline-dev", + "libsqlite3-dev", "tk-dev", "libncursesw5-dev", "libgdbm-dev", + "libffi-dev", "uuid-dev", "liblzma-dev", "ca-certificates", "curl" + ]) + except Exception: + run(["apt-get", "update"], check=False) + run(["apt-get", "install", "-y", + "pyenv", "build-essential", "gcc", "make", "pkg-config", + "libssl-dev", "zlib1g-dev", "libbz2-dev", "libreadline-dev", + "libsqlite3-dev", "tk-dev", "libncursesw5-dev", "libgdbm-dev", + "libffi-dev", "uuid-dev", "liblzma-dev", "ca-certificates", "curl" + ], check=False) - def ensure_pyenv_installed_with_installer(pyenv_root: Path, owner: str): - if (pyenv_root / "bin" / "pyenv").exists(): - return - apt_try_install(["curl", "git", "ca-certificates"]) - PARENT_HOME.mkdir(parents=True, exist_ok=True) - run(["chown", "-R", f"{owner}:{owner}", str(PARENT_HOME)], check=False) - run([ - "sudo","-u", owner, "-H", "env", - f"HOME={PARENT_HOME}", - "bash","-lc", - "curl -fsSL https://pyenv.run | bash" - ], check=True) - src = PARENT_HOME / ".pyenv" - if src.exists() and str(src) != str(pyenv_root): - run(["sudo","-u", owner, "-H", "bash","-lc", - f'mv "{src}" "{pyenv_root}"'], check=True) - - build_deps = [ - "build-essential","gcc","make","pkg-config","libssl-dev","zlib1g-dev", - "libbz2-dev","libreadline-dev","libsqlite3-dev","tk-dev","libncursesw5-dev", - "libgdbm-dev","libffi-dev","uuid-dev","liblzma-dev","ca-certificates","curl","git" - ] - if _is_ubuntu(): - apt_try_install(build_deps) - else: - try: - apt_try_install(["pyenv"] + build_deps) - except Exception: - run(["apt-get","update"], check=False) - run(["apt-get","install","-y","pyenv"] + build_deps, check=False) - - PARENT_HOME.mkdir(parents=True, exist_ok=True) + Path("/opt/npm").mkdir(parents=True, exist_ok=True) PYENV_ROOT.mkdir(parents=True, exist_ok=True) - run(["chown","-R", f"{PYENV_OWNER}:{PYENV_OWNER}", str(PARENT_HOME)], check=False) + run(["chown", "-R", f"{PYENV_OWNER}:{PYENV_OWNER}", "/opt/npm"], check=False) - if _is_ubuntu(): - ensure_pyenv_installed_with_installer(PYENV_ROOT, PYENV_OWNER) - - PYENV_BIN_CANDIDATES = [ - str(PYENV_ROOT / "bin" / "pyenv"), - "/usr/lib/pyenv/bin/pyenv", - "/usr/bin/pyenv", - "pyenv", - ] pyenv_bin = next((c for c in PYENV_BIN_CANDIDATES if shutil.which(c)), None) - if not pyenv_bin and (PYENV_ROOT / "bin" / "pyenv").exists(): - pyenv_bin = str(PYENV_ROOT / "bin" / "pyenv") if not pyenv_bin: - raise RuntimeError("Nie znaleziono 'pyenv' (APT lub installer).") + raise RuntimeError("No 'pyenv' (try /usr/bin/pyenv or /usr/lib/pyenv/bin/pyenv).") + + env_pyenv = os.environ.copy() + env_pyenv.update({ + "HOME": "/opt/npm", + "PYENV_ROOT": str(PYENV_ROOT), + "PATH": "/usr/lib/pyenv/bin:/usr/bin:/bin" + }) + with step(f"Installing Python {PYTHON_VERSION} via pyenv into {PYENV_ROOT}"): + run(["mkdir", "-p", str(PYENV_ROOT)]) + run(["chown", "-R", f"{PYENV_OWNER}:{PYENV_OWNER}", "/opt/npm"], check=False) + + run([ + "sudo", "-u", PYENV_OWNER, "bash", "-lc", + 'if [ ! -x "/opt/npm/.pyenv/bin/pyenv" ]; then ' + ' command -v git >/dev/null 2>&1 || sudo apt-get install -y git; ' + ' git clone --depth=1 https://github.com/pyenv/pyenv.git /opt/npm/.pyenv; ' + "fi" + ]) + + install_cmd = ( + 'export HOME=/opt/npm; ' + 'export PYENV_ROOT=/opt/npm/.pyenv; ' + 'export PATH="$PYENV_ROOT/bin:/usr/bin:/bin"; ' + 'mkdir -p "$PYENV_ROOT"; cd "$HOME"; ' + f'pyenv install -s {PYTHON_VERSION}' + ) + run([ + "sudo", "-u", PYENV_OWNER, "env", "-i", + "HOME=/opt/npm", + f"PYENV_ROOT={PYENV_ROOT}", + f"PATH={PYENV_ROOT}/bin:/usr/bin:/bin", + "bash", "-lc", install_cmd + ]) + profile_snippet = f"""# Auto-generated by setup_certbot_venv -# Ustawienia pyenv dla uzytkownika '{PYENV_OWNER}' +# pyenv for '{PYENV_OWNER}' if [ -d "{PYENV_ROOT}" ]; then export PYENV_ROOT="{PYENV_ROOT}" - case ":$PATH:" in *":{PYENV_ROOT}/bin:"*) ;; *) PATH="{PYENV_ROOT}/bin:$PATH" ;; esac + case ":$PATH:" in *":$PYENV_ROOT/bin:"*) ;; *) PATH="$PYENV_ROOT/bin:$PATH";; esac + case ":$PATH:" in *":/usr/lib/pyenv/bin:"*) ;; *) PATH="/usr/lib/pyenv/bin:$PATH";; esac export PATH - eval "$({pyenv_bin} init -)" + case "$-" in *i*) _interactive=1 ;; *) _interactive=0 ;; esac + if [ "$_interactive" = 1 ] && {{ [ "${{USER:-}}" = "{PYENV_OWNER}" ] || [ "${{SUDO_USER:-}}" = "{PYENV_OWNER}" ]; }}; then + if command -v pyenv >/dev/null 2>&1; then + eval "$(pyenv init -)" + elif [ -x "{PYENV_ROOT}/bin/pyenv" ]; then + eval "$("{PYENV_ROOT}/bin/pyenv" init -)" + fi + fi fi """ write_file(Path("/etc/profile.d/npm-pyenv.sh"), profile_snippet, 0o644) - run([ - "sudo","-u", PYENV_OWNER, "-H", "env", - f"HOME={PARENT_HOME}", - "bash","-lc", - f'export PYENV_ROOT="{PYENV_ROOT}"; ' - f'export PATH="$PYENV_ROOT/bin:$PATH"; ' - f'eval "$({pyenv_bin} init -)"; ' - f'"{pyenv_bin}" install -s {PYTHON_VERSION}; ' - f'"{pyenv_bin}" versions' - ], check=True) + python311 = PYENV_ROOT / "versions" / PYTHON_VERSION / "bin" / "python3.11" + if not python311.exists(): + python311 = PYENV_ROOT / "versions" / PYTHON_VERSION / "bin" / "python3" + if not python311.exists(): + raise RuntimeError(f"No python {PYTHON_VERSION} in {PYENV_ROOT}/versions/.") - py_cmd = run([ - "sudo","-u", PYENV_OWNER, "-H", "env", - f"HOME={PARENT_HOME}", - "bash","-lc", - f'export PYENV_ROOT="{PYENV_ROOT}"; ' - f'export PATH="$PYENV_ROOT/bin:$PATH"; ' - f'echo "$({pyenv_bin} prefix {PYTHON_VERSION})/bin/python3"' - ], check=True) - python_path = (py_cmd.stdout.decode().strip() if hasattr(py_cmd, "stdout") else "").strip() - if not python_path: - raise RuntimeError("Nie udało się ustalić ścieżki do python3 z pyenv.") + venv_bin = venv_dir / "bin" + pip_path = venv_bin / "pip" + certbot_path = venv_bin / "certbot" - venv_dir.mkdir(parents=True, exist_ok=True) - run([python_path, "-m", "venv", str(venv_dir)], check=True) + with step(f"Preparing Certbot venv at {venv_dir} (Python {PYTHON_VERSION})"): + venv_dir.mkdir(parents=True, exist_ok=True) + if not venv_dir.exists() or not pip_path.exists(): + run([str(python311), "-m", "venv", str(venv_dir)]) - pip_path = venv_dir / "bin" / "pip" - certbot_bin = venv_dir / "bin" / "certbot" + env_build = os.environ.copy() + env_build["SETUPTOOLS_USE_DISTUTILS"] = "local" - 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) - run([str(venv_dir / "bin" / "python"), "-m", "pip", "install", "-U", "pip", "setuptools", "wheel"], env=env_build, check=True) - run([str(pip_path), "install", "-U", "cryptography", "cffi", "certbot", "tldextract"], env=env_build, check=True) + 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) - 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_bin) - - cb_ver = run_out([str(certbot_bin), "--version"], check=False) or "" - pip_ver = run_out([str(pip_path), "--version"], check=False) or "" - print(f"Certbot: {cb_ver.strip()} | Pip: {pip_ver.strip()}") - - run(["chown","-R", f"{PYENV_OWNER}:{PYENV_OWNER}", str(PYENV_ROOT)], check=False) + cb_ver = run_out([str(certbot_path), "--version"], check=False) or "" + pip_ver = run_out([str(pip_path), "--version"], check=False) or "" + print(f"Certbot: {cb_ver.strip()} | Pip: {pip_ver.strip()}") + run(["chown", "-R", f"{PYENV_OWNER}:{PYENV_OWNER}", str(PYENV_ROOT)], check=False) def configure_letsencrypt():