Skip to content

Commit e80f39b

Browse files
committed
feat(scripts): bump npm deps to absolute latest with peer-dep conflict resolution
1 parent 2fa949a commit e80f39b

1 file changed

Lines changed: 70 additions & 5 deletions

File tree

scripts/update.py

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,25 @@
33
Steps
44
-----
55
1. Repin the conda environment (scripts/repin.sh)
6-
2. Update root npm dependencies
7-
3. Update Extension npm dependencies
6+
2. Update root npm dependencies to latest, resolving peer dep conflicts
7+
3. Update Extension npm dependencies to latest, resolving peer dep conflicts
88
4. Rebuild the extension
99
5. Check hg.mozilla.org for a newer Firefox and update install-firefox.sh if found
1010
1111
Run from the project root:
1212
python scripts/update.py
1313
"""
1414

15+
import re
1516
import subprocess
1617
import sys
1718
from pathlib import Path
1819

1920
ROOT = Path(__file__).resolve().parent.parent
2021
SCRIPTS = ROOT / "scripts"
2122

23+
_MAX_RESOLVE_ATTEMPTS = 10
24+
2225

2326
def run(*cmd: str, cwd: Path = ROOT) -> None:
2427
print(f"+ {' '.join(cmd)}")
@@ -29,13 +32,75 @@ def conda_run(*cmd: str, cwd: Path = ROOT) -> None:
2932
run("conda", "run", "-n", "openwpm", *cmd, cwd=cwd)
3033

3134

35+
def _npm_install(cwd: Path) -> str:
36+
"""Run npm install and return combined stdout+stderr."""
37+
result = subprocess.run(
38+
["conda", "run", "-n", "openwpm", "npm", "install", "--include=dev"],
39+
cwd=cwd,
40+
capture_output=True,
41+
text=True,
42+
)
43+
print(result.stdout, end="")
44+
if result.returncode != 0:
45+
print(result.stderr, end="", file=sys.stderr)
46+
raise subprocess.CalledProcessError(result.returncode, result.args)
47+
return result.stdout + result.stderr
48+
49+
50+
def _peer_dep_conflicts(output: str) -> list[tuple[str, str]]:
51+
"""Return unique (package, required_range) pairs from npm peer dep warnings.
52+
53+
npm emits lines like:
54+
npm warn peer some-pkg@"^2.0.0" from other-pkg@3.0.0
55+
The package named after 'peer' is the one that is too new; downgrade it
56+
to the stated range to satisfy the peer requirement.
57+
"""
58+
seen: set[tuple[str, str]] = set()
59+
conflicts = []
60+
for m in re.finditer(r'peer (\S+)@"([^"]+)" from', output):
61+
pkg, rng = m.group(1), m.group(2)
62+
if (pkg, rng) not in seen:
63+
seen.add((pkg, rng))
64+
conflicts.append((pkg, rng))
65+
return conflicts
66+
67+
68+
def npm_bump_and_resolve(cwd: Path) -> None:
69+
"""Bump all npm deps to absolute latest, then resolve peer dep conflicts.
70+
71+
Strategy: use npm-check-updates to rewrite package.json to the newest
72+
published version of every dependency, then iteratively downgrade any
73+
package that triggers a peer dep warning until npm install is clean.
74+
"""
75+
print(f"\n=== npm bump-to-latest: {cwd.relative_to(ROOT)} ===")
76+
77+
# Rewrite package.json with the latest published versions of all deps.
78+
conda_run("npx", "--yes", "npm-check-updates", "--upgrade", cwd=cwd)
79+
80+
for attempt in range(1, _MAX_RESOLVE_ATTEMPTS + 1):
81+
output = _npm_install(cwd)
82+
conflicts = _peer_dep_conflicts(output)
83+
if not conflicts:
84+
print(f" Clean install on attempt {attempt}.")
85+
return
86+
print(f" Attempt {attempt}: {len(conflicts)} peer dep conflict(s)")
87+
for pkg, rng in conflicts:
88+
print(f" Downgrading {pkg} → '{rng}'")
89+
conda_run("npm", "install", f"{pkg}@{rng}", "--include=dev", cwd=cwd)
90+
91+
print(
92+
f"WARNING: peer dep conflicts not fully resolved after {_MAX_RESOLVE_ATTEMPTS} attempts.",
93+
file=sys.stderr,
94+
)
95+
96+
3297
def main() -> None:
3398
# Repin the conda environment from unpinned sources
3499
run("./repin.sh", cwd=SCRIPTS)
35100

36-
# Update npm dependencies (root package.json and Extension/package.json)
37-
conda_run("npm", "update", "--include=dev")
38-
conda_run("npm", "update", "--include=dev", cwd=ROOT / "Extension")
101+
# Bump npm deps to latest and resolve peer dep conflicts
102+
npm_bump_and_resolve(ROOT)
103+
npm_bump_and_resolve(ROOT / "Extension")
39104

40105
# Rebuild the extension XPI after dependency changes
41106
run("./build-extension.sh", cwd=SCRIPTS)

0 commit comments

Comments
 (0)