2022-10-06 20:23:43 +03:00
import sys
2022-10-06 20:41:31 +03:00
import subprocess
2022-10-06 22:16:15 +03:00
import os
2022-10-07 01:00:26 +03:00
from pathlib import Path
2022-10-06 20:23:43 +03:00
2022-10-06 21:26:57 +03:00
def main ( ) :
""" Check if the dependency updates in package-lock are also updated in yarn.locks """
2022-10-07 01:01:25 +03:00
targetBranch = sys . argv [ 1 ] # Script is called with PR Target Branch Name, Fulfilled by AzDo
2022-10-06 21:26:57 +03:00
subprocess . getoutput ( f " git fetch --all " )
2022-10-06 22:16:15 +03:00
subprocess . getoutput ( f " git pull origin { targetBranch } " )
2022-10-06 21:26:57 +03:00
VerifyDependencies ( targetBranch )
sys . exit ( 0 )
def VerifyDependencies ( targetBranch ) :
""" Enumerate through all changed files to check diffs. """
2023-07-13 21:05:56 +03:00
# origin/ requires origin/ to be up to date.
2022-10-07 01:00:26 +03:00
changedFiles = [ Path ( path ) for path in subprocess . getoutput ( f " git diff --name-only origin/ { targetBranch } .. " ) . splitlines ( ) ]
2022-10-06 21:26:57 +03:00
npmLockFile = " package-lock.json "
2023-07-13 21:05:56 +03:00
2022-10-06 21:26:57 +03:00
for file in changedFiles :
fileName = os . path . basename ( os . path . realpath ( file ) )
if fileName == npmLockFile :
NpmChangesMirrorYarnChanges ( changedFiles , file , targetBranch )
2022-10-07 01:00:26 +03:00
def GetNpmDependencyUpdates ( packageLockDiffLines ) :
2022-10-06 22:06:10 +03:00
""" Returns a dictionary of [dependency -> [] (can be changed to version in later implementations)] changes found in diff string of package-lock.json """
# Assumes dependency line starts with "node_modules/DEPENDENCYNAME". Version may or may not come after
dependencies = { }
2022-10-07 01:00:26 +03:00
for line in packageLockDiffLines :
line = line . strip ( )
line = line . lstrip ( " \t " )
if line . startswith ( ' " node_modules/ ' ) :
dependencies [ line . split ( ' " node_modules/ ' , 1 ) [ 1 ] . split ( ' " ' , 1 ) [ 0 ] ] = [ ] # will be "node_modules/dep further" content, need to cull
2022-10-06 22:06:10 +03:00
return dependencies
2022-10-07 01:00:26 +03:00
def GetYarnDependencyUpdates ( yarnLockDiffLines ) :
2022-10-06 22:06:10 +03:00
""" Returns a dictionary of [dependency -> [] (can be changed to version in later implementations)] changes found in diff string of yarn.lock """
2022-10-07 01:00:26 +03:00
# Assumes dependency line starts with DEPEDENCY@Version without whitespace
2022-10-06 22:06:10 +03:00
dependencies = { }
2022-10-07 01:00:26 +03:00
for line in yarnLockDiffLines :
if line == line . lstrip ( ) and " @ " in line :
depsAtVers = line . lstrip ( ' " ' ) . split ( " , " ) # multiple dependencies are possible with diff versions, sep by ,
for dependencyAtVers in depsAtVers :
dep = dependencyAtVers . rsplit ( " @ " , 1 ) [ 0 ]
vers = dependencyAtVers . rsplit ( " @ " , 1 ) [ 1 ]
2023-07-13 21:05:56 +03:00
dependencies [ dep ] = [ ] # Could add version here later. That will probably not happen
2022-10-06 22:06:10 +03:00
return dependencies
2022-10-07 01:00:26 +03:00
def GetUnmatchedDiffs ( yarnDiff , npmDiff ) :
2023-09-20 01:00:47 +03:00
""" Returns [] if dependency updates are reflected in both diffs, else the dependencies out of sync. """
2022-10-07 01:00:26 +03:00
# v Remove + or - from diff and additional git diff context lines
yarnDeps = GetYarnDependencyUpdates ( [ line [ 1 : ] for line in yarnDiff . splitlines ( ) if line . startswith ( " + " ) or line . startswith ( " - " ) ] )
npmDeps = GetNpmDependencyUpdates ( [ line [ 1 : ] for line in npmDiff . splitlines ( ) if line . startswith ( " + " ) or line . startswith ( " - " ) ] )
outOfSyncDependencies = [ ]
2022-10-06 22:06:10 +03:00
for dep in npmDeps :
if dep in yarnDeps and yarnDeps [ dep ] == npmDeps [ dep ] : # version changes match
continue
else :
2022-10-07 01:00:26 +03:00
outOfSyncDependencies . append ( dep )
return outOfSyncDependencies
2022-10-06 22:06:10 +03:00
2022-10-06 21:26:57 +03:00
def NpmChangesMirrorYarnChanges ( changedFiles , packageLockPath , targetBranch ) :
2023-09-20 01:00:47 +03:00
""" Returns successfully if yarn.lock matches package lock changes, if not, throws exit code """
2022-10-06 21:26:57 +03:00
yarnLockFile = " yarn.lock "
2022-10-07 01:00:26 +03:00
yarnLockPath = Path ( os . path . join ( os . path . dirname ( packageLockPath ) , yarnLockFile ) )
2022-10-06 21:30:36 +03:00
outOfDateYarnLocks = [ ]
2023-07-13 21:05:56 +03:00
2022-10-06 21:30:36 +03:00
if yarnLockPath in changedFiles :
2022-10-07 01:00:26 +03:00
yarnDiff = subprocess . getoutput ( f " git diff origin/ { targetBranch } .. -- { str ( yarnLockPath ) } " )
npmDiff = subprocess . getoutput ( f " git diff origin/ { targetBranch } .. -- { packageLockPath } " )
diffSetComplement = GetUnmatchedDiffs ( yarnDiff , npmDiff )
if diffSetComplement == [ ] :
2022-10-06 22:06:10 +03:00
pass
2022-10-06 21:39:55 +03:00
else :
2022-10-07 01:00:26 +03:00
outOfDateYarnLocks . append ( ( str ( yarnLockPath ) , diffSetComplement ) )
2022-10-06 21:30:36 +03:00
else :
2022-10-07 01:00:26 +03:00
outOfDateYarnLocks . append ( yarnLockPath )
2022-10-06 21:39:55 +03:00
if ( outOfDateYarnLocks != [ ] ) :
2023-09-20 01:00:47 +03:00
sys . exit ( f """ { outOfDateYarnLocks } may be out of sync with node.
The yarn . lock and package - lock appear to be out of sync with the changes made after { targetBranch } .
Update by first using npm to push to the registry , doing npm install package @version. Then , do yarn add package @version for each primary package . During the yarn add process , you may need to npm install specific dependencies that yarn will flag to allow yarn to proceed . You may consider ( yarn import ) . Note this tool will list dependencies of packages , but you should try adding just the main package first .
If you can confirm the new changes are in sync , then you may ignore this failure . """ )
2022-10-06 21:39:55 +03:00
else :
return 0 # OK, status here is not used
2023-07-13 21:05:56 +03:00
2022-10-06 22:16:15 +03:00
if __name__ == " __main__ " :
2022-10-07 01:00:26 +03:00
main ( )