33Steps
44-----
551. 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
884. Rebuild the extension
995. Check hg.mozilla.org for a newer Firefox and update install-firefox.sh if found
1010
1111Run from the project root:
1212 python scripts/update.py
1313"""
1414
15+ import re
1516import subprocess
1617import sys
1718from pathlib import Path
1819
1920ROOT = Path (__file__ ).resolve ().parent .parent
2021SCRIPTS = ROOT / "scripts"
2122
23+ _MAX_RESOLVE_ATTEMPTS = 10
24+
2225
2326def 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+
3297def 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