Prevent overwriting package-lock.json when installing deps
This commit is contained in:
1 parent
461e798708
commit
6d6c5a3d91
5 files changed
+120
-10
No files matched your search
@@ -14,10 +14,13 @@ from .util import (
|
||||
IS_MAC,
|
||||
IS_WINDOWS,
|
||||
REPO_ROOT,
|
||||
clean_env,
|
||||
download,
|
||||
extract_archive,
|
||||
file_hash,
|
||||
info,
|
||||
ok,
|
||||
run,
|
||||
warn,
|
||||
which,
|
||||
)
|
||||
@@ -27,6 +30,9 @@ NODE_DIR = os.path.join(REPO_ROOT, ".node")
|
||||
NODE_VERSION = "24.11.1"
|
||||
MIN_NODE_MAJOR = 20
|
||||
|
||||
UI_DIR = os.path.join(REPO_ROOT, "ui")
|
||||
UI_STATE_KEY = "ui_deps_hash"
|
||||
|
||||
|
||||
def node_bin_dir():
|
||||
# windows zips have node.exe/npm.cmd at the archive root; unix under bin/
|
||||
@@ -119,3 +125,93 @@ def ensure_node(detection, dry_run=False):
|
||||
return True
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- ui deps
|
||||
|
||||
|
||||
def npm_env():
|
||||
"""Scrubbed env with the local .node/ ahead of PATH."""
|
||||
env = clean_env()
|
||||
if os.path.isdir(node_bin_dir()):
|
||||
env["PATH"] = node_bin_dir() + os.pathsep + env.get("PATH", "")
|
||||
return env
|
||||
|
||||
|
||||
def find_npm(env=None):
|
||||
"""npm from the local .node/ copy when we installed one, else the system."""
|
||||
path = (env or npm_env()).get("PATH")
|
||||
return shutil.which("npm.cmd" if IS_WINDOWS else "npm", path=path)
|
||||
|
||||
|
||||
def _ui_deps_hash():
|
||||
return file_hash(
|
||||
[
|
||||
os.path.join(UI_DIR, "package-lock.json"),
|
||||
os.path.join(UI_DIR, "package.json"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def ensure_ui_deps(env=None, dry_run=False):
|
||||
"""Install ui/node_modules without ever rewriting ui/package-lock.json.
|
||||
|
||||
A plain `npm install` treats the lockfile as writable and re-derives it for
|
||||
the machine doing the install: on Windows it strips the `libc` fields off
|
||||
the Linux-only optional binaries (@next/swc-linux-*, rollup, lightningcss),
|
||||
on Linux it puts them back. Since the UI installs on every launch, everyone
|
||||
ends up with a modified lockfile they never asked for — which then blocks
|
||||
`manager update`, because a dirty tree aborts the pull.
|
||||
|
||||
`--no-save` keeps the resolution but stops the write-back, and stays an
|
||||
incremental install (seconds) rather than the full wipe-and-refetch of
|
||||
`npm ci`. The lockfile is snapshotted and restored regardless, so this
|
||||
holds even if a future npm changes what `--no-save` covers.
|
||||
|
||||
Gated on a hash of the manifests so steady-state launches skip npm.
|
||||
"""
|
||||
from . import env as env_mod
|
||||
|
||||
lock = os.path.join(UI_DIR, "package-lock.json")
|
||||
if not os.path.isfile(lock):
|
||||
warn("ui/package-lock.json is missing — skipping UI dependency install.")
|
||||
return False
|
||||
want = _ui_deps_hash()
|
||||
if env_mod.venv_exists() and env_mod.load_state().get(UI_STATE_KEY) == want:
|
||||
if os.path.isdir(os.path.join(UI_DIR, "node_modules")):
|
||||
ok("UI dependencies already installed.")
|
||||
return False
|
||||
if dry_run:
|
||||
info("[dry-run] would run: npm install --no-save (in %s)" % UI_DIR)
|
||||
return False
|
||||
env = env or npm_env()
|
||||
npm = find_npm(env)
|
||||
if npm is None:
|
||||
warn("npm not found — skipping UI dependency install.")
|
||||
return False
|
||||
|
||||
info("Installing UI dependencies...")
|
||||
with open(lock, "rb") as f:
|
||||
before = f.read()
|
||||
code, _ = run(
|
||||
[npm, "install", "--no-save", "--no-audit", "--no-fund"],
|
||||
cwd=UI_DIR,
|
||||
env=env,
|
||||
stream=True,
|
||||
check=False,
|
||||
)
|
||||
with open(lock, "rb") as f:
|
||||
after = f.read()
|
||||
if after != before:
|
||||
with open(lock, "wb") as f:
|
||||
f.write(before)
|
||||
info("Reverted npm's edit to ui/package-lock.json (managed by the repo).")
|
||||
if code != 0:
|
||||
warn("UI dependency install failed — the UI may not start.")
|
||||
return False
|
||||
if env_mod.venv_exists():
|
||||
state = env_mod.load_state()
|
||||
state[UI_STATE_KEY] = want
|
||||
env_mod.save_state(state)
|
||||
ok("UI dependencies ready.")
|
||||
return True
|
||||
Reference in new issue
Block a user