mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
Debian trixie ships nodejs 20.19, whose release line reached its end of life on 2026-04-30. `@mermaid-js/mermaid-cli` 12 and its puppeteer declare `node >=22.13.0`, so `make install-lint` failed in every fresh Debian stack container with `All providers failed for chrome-headless-shell` (#1088). `ensure_node_present()` still installs the distro package first, then reads the release line of `node` on PATH and looks it up in the nodejs/Release schedule. A line past its `end` date is replaced by the newest LTS from nodejs.org/dist/index.json: SHASUMS256.txt is verified with gpgv against the active Node.js release keys from nodejs/release-keys, the tarball against its SHA-256 entry, and the release is unpacked into /usr/local, after which `node` on PATH must be that release. Nothing pins a major: the next LTS line is picked up the same way. Ubuntu 26.04's Node.js 22, supported until 2027-04-30, stays. `ensure_npm_present()` calls it first, so a container that already holds the end-of-life node is upgraded too rather than skipped because npm exists. The call runs once per process. Closes #1088 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
67 lines
1.9 KiB
Python
67 lines
1.9 KiB
Python
"""npm-based installer helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from utils.install.primitives import ensure_dir_on_path, install_with_optional_sudo, log
|
|
|
|
|
|
def ensure_npm_present() -> None:
|
|
from utils.install.node import ensure_node_present
|
|
|
|
ensure_node_present()
|
|
if shutil.which("npm") is not None:
|
|
return
|
|
|
|
log("npm missing; attempting Node.js/npm install via system package manager.")
|
|
from utils.install.system_pkg import install_command_via_pkg
|
|
|
|
install_command_via_pkg("npm")
|
|
|
|
if shutil.which("npm") is None:
|
|
raise RuntimeError("npm not found and could not be installed")
|
|
|
|
|
|
def npm_install_global(package: str) -> None:
|
|
ensure_npm_present()
|
|
log(f"Installing '{package}' via npm (global)")
|
|
try:
|
|
install_with_optional_sudo(["npm", "install", "-g", package])
|
|
except subprocess.CalledProcessError:
|
|
pass
|
|
else:
|
|
return
|
|
|
|
prefix = Path("~/.npm-global").expanduser()
|
|
prefix.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
subprocess.run(
|
|
["npm", "install", "-g", "--prefix", str(prefix), package],
|
|
check=True,
|
|
)
|
|
except subprocess.CalledProcessError as exc:
|
|
raise RuntimeError(
|
|
f"npm install -g {package} failed (both global and --prefix)"
|
|
) from exc
|
|
ensure_dir_on_path(str(prefix / "bin"))
|
|
|
|
|
|
def npm_install_local_in_repo(repo_root: str) -> None:
|
|
ensure_npm_present()
|
|
has_lock = (Path(repo_root) / "package-lock.json").is_file()
|
|
argv = (
|
|
["npm", "ci", "--no-fund", "--no-audit"]
|
|
if has_lock
|
|
else ["npm", "install", "--no-fund", "--no-audit"]
|
|
)
|
|
try:
|
|
subprocess.run(argv, check=True, cwd=repo_root)
|
|
except subprocess.CalledProcessError as exc:
|
|
raise RuntimeError(f"npm install in {repo_root} failed") from exc
|
|
|
|
|
|
__all__ = ["ensure_npm_present", "npm_install_global", "npm_install_local_in_repo"]
|