scripts: Add consistency check script

Add `package-consistency-check.py` script to prevent #6809 and #6815
from happening. This script takes a string present in the repository
field of packages to identify pacakges from a specific source.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
This commit is contained in:
Ruoqing He 2024-10-27 16:49:44 +08:00 committed by Wei Liu
parent 675140f627
commit 337cbf3d33

View File

@ -0,0 +1,83 @@
#!/bin/env python3
#
# Copyright © 2024 Institute of Software, CAS. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
import subprocess
import json
from argparse import ArgumentParser
from collections import defaultdict
def get_cargo_metadata():
result = subprocess.run(
['cargo', 'metadata', '--format-version=1'],
capture_output=True,
text=True
)
if result.returncode != 0:
exit(1)
metadata = json.loads(result.stdout)
return metadata
def find_dependents_of_package(metadata, package_source):
"""Find dependencies based on the provided source identifier and return related package info."""
packages = defaultdict(list)
direct_dependents = defaultdict(list)
# Identify packages from the given package source and record version
for pkg in metadata['packages']:
repository = pkg['repository'] or ''
if package_source in repository:
packages[pkg['name']].append(pkg['version'])
# Find packages that immediately depend on the identified source packages
for node in metadata['resolve']['nodes']:
current_pkg = next(pkg for pkg in metadata['packages'] if pkg['id'] == node['id'])
current_pkg_name = current_pkg['name']
current_pkg_version = current_pkg['version']
for dep_id in node['dependencies']:
dep_pkg = next(pkg for pkg in metadata['packages'] if pkg['id'] == dep_id)
dep_name = dep_pkg['name']
dep_version = dep_pkg['version']
if dep_name in packages:
direct_dependents[(dep_name, dep_version)].append((current_pkg_name, current_pkg_version))
return packages, direct_dependents
def check_for_version_conflicts(packages, direct_dependents):
"""Check if there are multiple versions of dependencies, and return True if conflicts are found."""
has_conflicts = False
for pkg_name, versions in packages.items():
if len(set(versions)) > 1:
has_conflicts = True
print(f"Error: Multiple versions detected for {pkg_name}: {set(versions)}")
for version in set(versions):
print(f" Version {version} used by:")
for dependent, dep_version in direct_dependents[(pkg_name, version)]:
print(f" - {dependent} v{dep_version}")
return has_conflicts
if __name__ == '__main__':
parser = ArgumentParser(description='Cargo dependency conflict checker.')
parser.add_argument('package_source', type=str, help='A keyword used to match the repository URL field')
args = parser.parse_args()
metadata = get_cargo_metadata()
if metadata is None:
print("Error: Metadata is empty")
exit(1)
packages, direct_dependents = find_dependents_of_package(metadata, args.package_source)
has_conflicts = check_for_version_conflicts(packages, direct_dependents)
if has_conflicts:
exit(1)