"""Install the pinned embedded fx runtime; no user-level fx configuration is written.""" import hashlib import io import os from pathlib import Path import platform import shutil import subprocess import tarfile import urllib.request ROOT = Path(__file__).resolve().parents[1] NODE_VERSION = 'v24.21.0' NODE_SHA256 = 'fd8e59d5a511510f6a298afb548f18c7d2b1be404d8b4a27d94fbe49f56cb2d6' def prepare(): runtime = ROOT / '.runtime' runtime.mkdir(exist_ok=True) if platform.system() == 'Linux' and platform.machine() == 'x86_64': node_root = runtime / f'node-{NODE_VERSION}-linux-x64' node = node_root / 'bin/node' if not node.is_file(): url = f'https://nodejs.org/dist/{NODE_VERSION}/node-{NODE_VERSION}-linux-x64.tar.xz' with urllib.request.urlopen(url, timeout=120) as response: data = response.read() if hashlib.sha256(data).hexdigest() != NODE_SHA256: raise RuntimeError('Node runtime checksum mismatch') with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as archive: archive.extractall(runtime, filter='data') os.environ['PATH'] = str(node_root / 'bin') + os.pathsep + os.environ.get('PATH', '') node = shutil.which('node') npm = shutil.which('npm') if not node or not npm: raise RuntimeError('Node 20+ and npm are required for isolated fx refinement') major = int(subprocess.check_output([node, '--version'], text=True).strip().lstrip('v').split('.')[0]) if major < 20: raise RuntimeError('Node 20+ is required for isolated fx refinement') folder = ROOT / 'studio/fx_runtime' lock_hash = hashlib.sha256((folder / 'package-lock.json').read_bytes()).hexdigest() marker = folder / 'node_modules/.studio-lock' if not marker.is_file() or marker.read_text() != lock_hash: subprocess.run([npm, 'ci', '--ignore-scripts', '--no-audit', '--no-fund'], cwd=folder, check=True, timeout=300) marker.write_text(lock_hash)