Update npm_install.py
This commit is contained in:
+144
-31
@@ -255,66 +255,163 @@ def apply_interactive_choices(args, choices):
|
||||
|
||||
|
||||
def _memory_gb():
|
||||
"""Return total and available RAM in GiB using /proc when possible."""
|
||||
"""Return RAM and swap information in GiB."""
|
||||
try:
|
||||
values = {}
|
||||
for line in Path("/proc/meminfo").read_text().splitlines():
|
||||
key, value = line.split(":", 1)
|
||||
values[key] = int(value.split()[0])
|
||||
total = values.get("MemTotal", 0) / (1024**2)
|
||||
available = values.get("MemAvailable", values.get("MemFree", 0)) / (1024**2)
|
||||
return total, available
|
||||
return {
|
||||
"ram_total": values.get("MemTotal", 0) / (1024**2),
|
||||
"ram_available": values.get("MemAvailable", values.get("MemFree", 0)) / (1024**2),
|
||||
"swap_total": values.get("SwapTotal", 0) / (1024**2),
|
||||
"swap_free": values.get("SwapFree", 0) / (1024**2),
|
||||
}
|
||||
except Exception:
|
||||
try:
|
||||
page_size = os.sysconf("SC_PAGE_SIZE")
|
||||
total = page_size * os.sysconf("SC_PHYS_PAGES") / (1024**3)
|
||||
available = page_size * os.sysconf("SC_AVPHYS_PAGES") / (1024**3)
|
||||
return total, available
|
||||
return {
|
||||
"ram_total": page_size * os.sysconf("SC_PHYS_PAGES") / (1024**3),
|
||||
"ram_available": page_size * os.sysconf("SC_AVPHYS_PAGES") / (1024**3),
|
||||
"swap_total": 0.0,
|
||||
"swap_free": 0.0,
|
||||
}
|
||||
except Exception:
|
||||
return 0.0, 0.0
|
||||
return {
|
||||
"ram_total": 0.0,
|
||||
"ram_available": 0.0,
|
||||
"swap_total": 0.0,
|
||||
"swap_free": 0.0,
|
||||
}
|
||||
|
||||
|
||||
def _container_type() -> str | None:
|
||||
"""Return container type when running inside one (for example: lxc, docker)."""
|
||||
if shutil.which("systemd-detect-virt"):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["systemd-detect-virt", "--container"],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
value = (result.stdout or "").strip().lower()
|
||||
if value and value != "none":
|
||||
return value
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
marker = Path("/run/systemd/container")
|
||||
try:
|
||||
if marker.exists():
|
||||
value = marker.read_text(encoding="utf-8", errors="ignore").strip().lower()
|
||||
if value:
|
||||
return value
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
cgroup = Path("/proc/1/cgroup").read_text(encoding="utf-8", errors="ignore").lower()
|
||||
for name in ("lxc", "docker", "podman", "containerd"):
|
||||
if name in cgroup:
|
||||
return name
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _print_manual_memory_help(container_type: str | None, available_gb: float):
|
||||
print("\n" + "=" * 70)
|
||||
print("INSUFFICIENT MEMORY FOR INSTALLATION / UPDATE")
|
||||
print("=" * 70)
|
||||
if container_type:
|
||||
print(f"Container detected: {container_type}")
|
||||
print("Automatic swapon inside a container is usually not permitted.")
|
||||
else:
|
||||
print("Automatic temporary swap could not be enabled.")
|
||||
print(f"Available RAM + free swap: {available_gb:.1f} GB")
|
||||
print(f"Required: {MIN_MEMORY_GB:.1f} GB")
|
||||
print("")
|
||||
print("Temporarily increase RAM or swap before running the installer again.")
|
||||
if container_type:
|
||||
print("For LXC, configure RAM/swap on the host or hypervisor, not inside the container.")
|
||||
print("After the installation/update completes, you can restore the previous limits.")
|
||||
print("=" * 70 + "\n")
|
||||
|
||||
|
||||
def check_memory_and_create_swap():
|
||||
"""Create temporary swap only when needed and remember what we changed."""
|
||||
total_memory_gb, available_memory_gb = _memory_gb()
|
||||
state = {"created": False, "activated_existing": False}
|
||||
"""Ensure enough RAM+swap for builds; create temporary swap only outside containers."""
|
||||
memory = _memory_gb()
|
||||
state = {"created": False, "activated_existing": False, "ready": True}
|
||||
|
||||
ram_total = memory["ram_total"]
|
||||
ram_available = memory["ram_available"]
|
||||
swap_total = memory["swap_total"]
|
||||
swap_free = memory["swap_free"]
|
||||
available_total = ram_available + swap_free
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print("MEMORY CHECK")
|
||||
print(f"{'='*70}")
|
||||
if total_memory_gb:
|
||||
print(f"Total RAM: {total_memory_gb:.1f} GB")
|
||||
print(f"Available: {available_memory_gb:.1f} GB")
|
||||
else:
|
||||
if not ram_total:
|
||||
print("RAM detection: unavailable")
|
||||
print("Continuing without automatic swap changes.")
|
||||
print(f"{'='*70}\n")
|
||||
return state
|
||||
print(f"Threshold: {MIN_MEMORY_GB} GB")
|
||||
|
||||
if available_memory_gb >= MIN_MEMORY_GB:
|
||||
print("✓ Memory sufficient")
|
||||
print(f"Total RAM: {ram_total:.1f} GB")
|
||||
print(f"Available RAM: {ram_available:.1f} GB")
|
||||
print(f"Swap: {swap_free:.1f} GB free / {swap_total:.1f} GB total")
|
||||
print(f"Usable now: {available_total:.1f} GB (available RAM + free swap)")
|
||||
print(f"Threshold: {MIN_MEMORY_GB:.1f} GB")
|
||||
|
||||
if available_total >= MIN_MEMORY_GB:
|
||||
print("\u2713 Memory/swap sufficient")
|
||||
print(f"{'='*70}\n")
|
||||
return state
|
||||
|
||||
print(f"\u26a0 Low working memory ({available_total:.1f} GB < {MIN_MEMORY_GB:.1f} GB)")
|
||||
|
||||
container_type = _container_type()
|
||||
if container_type:
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(container_type, available_total)
|
||||
return state
|
||||
|
||||
swap_file = Path("/swapfile")
|
||||
print(f"⚠ Low available memory ({available_memory_gb:.1f} GB)")
|
||||
|
||||
try:
|
||||
active_swap = run_out(["swapon", "--show=NAME", "--noheadings"], check=False)
|
||||
if str(swap_file) in active_swap.split():
|
||||
print("✓ /swapfile is already active; leaving it untouched")
|
||||
print(f"{'='*70}\n")
|
||||
# It was already counted in SwapFree above. If that was still not enough,
|
||||
# do not alter a pre-existing swap file owned by the administrator.
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(None, available_total)
|
||||
return state
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if swap_file.exists():
|
||||
with step("Activating existing /swapfile"):
|
||||
run(["swapon", str(swap_file)])
|
||||
state["activated_existing"] = True
|
||||
print("✓ Existing /swapfile activated temporarily")
|
||||
print(f"{'='*70}\n")
|
||||
try:
|
||||
with step("Activating existing /swapfile"):
|
||||
run(["swapon", str(swap_file)])
|
||||
state["activated_existing"] = True
|
||||
except subprocess.CalledProcessError:
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(None, available_total)
|
||||
return state
|
||||
|
||||
memory = _memory_gb()
|
||||
available_after = memory["ram_available"] + memory["swap_free"]
|
||||
if available_after < MIN_MEMORY_GB:
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(None, available_after)
|
||||
else:
|
||||
print(f"\u2713 Existing /swapfile activated; usable memory: {available_after:.1f} GB")
|
||||
print(f"{'='*70}\n")
|
||||
return state
|
||||
|
||||
try:
|
||||
@@ -330,16 +427,29 @@ def check_memory_and_create_swap():
|
||||
run(["chmod", "600", str(swap_file)])
|
||||
run(["mkswap", str(swap_file)])
|
||||
run(["swapon", str(swap_file)])
|
||||
except BaseException:
|
||||
# The caller cannot receive `state` if this function fails before return,
|
||||
# so clean up a partially created swap file here.
|
||||
except subprocess.CalledProcessError:
|
||||
if state["created"]:
|
||||
run(["swapoff", str(swap_file)], check=False)
|
||||
swap_file.unlink(missing_ok=True)
|
||||
state["created"] = False
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(None, available_total)
|
||||
return state
|
||||
except BaseException:
|
||||
if state["created"]:
|
||||
run(["swapoff", str(swap_file)], check=False)
|
||||
swap_file.unlink(missing_ok=True)
|
||||
state["created"] = False
|
||||
raise
|
||||
|
||||
print("✓ Temporary swap created and activated")
|
||||
print(f"{'='*70}\n")
|
||||
memory = _memory_gb()
|
||||
available_after = memory["ram_available"] + memory["swap_free"]
|
||||
if available_after < MIN_MEMORY_GB:
|
||||
state["ready"] = False
|
||||
_print_manual_memory_help(None, available_after)
|
||||
else:
|
||||
print(f"\u2713 Temporary swap created; usable memory: {available_after:.1f} GB")
|
||||
print(f"{'='*70}\n")
|
||||
return state
|
||||
|
||||
def cleanup_build_artifacts():
|
||||
@@ -3141,6 +3251,9 @@ def main() -> int:
|
||||
swap_state = {}
|
||||
try:
|
||||
swap_state = check_memory_and_create_swap()
|
||||
if not swap_state.get("ready", True):
|
||||
return 1
|
||||
|
||||
print("\n================== NPM + ANGIE installer ==================")
|
||||
print(f"Repository: {GITHUB_REPO_URL}")
|
||||
print(f"OS: {OSREL['PRETTY']}")
|
||||
|
||||
Reference in New Issue
Block a user