2020-04-22 16:14:45 +00:00
|
|
|
#!/usr/bin/env python3
|
2021-06-07 15:24:19 +00:00
|
|
|
#
|
|
|
|
# generates Provides: bundled(npm(...)) = ... lines for each declared dependency and devDependency of package.json
|
|
|
|
#
|
2022-04-05 17:39:40 +00:00
|
|
|
import os
|
2020-04-22 16:14:45 +00:00
|
|
|
import sys
|
|
|
|
import json
|
2022-06-22 17:13:29 +00:00
|
|
|
import yaml
|
2020-04-22 16:14:45 +00:00
|
|
|
from packaging import version
|
|
|
|
|
|
|
|
|
2022-04-05 17:39:40 +00:00
|
|
|
def scan_package_json(package_dir):
|
|
|
|
for root, dirs, files in os.walk(package_dir, topdown=True):
|
|
|
|
dirs[:] = [d for d in dirs if d not in ["node_modules", "vendor"]]
|
|
|
|
if "package.json" in files:
|
|
|
|
yield os.path.join(root, "package.json")
|
|
|
|
|
|
|
|
|
2020-04-22 16:14:45 +00:00
|
|
|
def read_declared_pkgs(package_json_path):
|
|
|
|
with open(package_json_path) as f:
|
|
|
|
package_json = json.load(f)
|
2022-04-05 17:39:40 +00:00
|
|
|
return list(package_json.get("dependencies", {}).keys()) + list(
|
|
|
|
package_json.get("devDependencies", {}).keys()
|
|
|
|
)
|
2020-04-22 16:14:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
def read_installed_pkgs(yarn_lock_path):
|
2024-05-07 19:16:50 +00:00
|
|
|
bad_version_strings = ['0.0.0-use.local', '7.0.1-patch.1']
|
2020-04-22 16:14:45 +00:00
|
|
|
with open(yarn_lock_path) as f:
|
2022-06-22 17:13:29 +00:00
|
|
|
lockfile = yaml.safe_load(f)
|
|
|
|
for pkg_decl, meta in lockfile.items():
|
|
|
|
for pkg in pkg_decl.split(", "):
|
|
|
|
if ":" not in pkg:
|
|
|
|
continue
|
|
|
|
pkg_name = pkg[: pkg.index("@", 1)]
|
|
|
|
pkg_version = meta["version"]
|
2024-05-07 19:16:50 +00:00
|
|
|
if pkg_version not in bad_version_strings:
|
|
|
|
yield (pkg_name, pkg_version)
|
2020-04-22 16:14:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
def list_provides(declared_pkgs, installed_pkgs):
|
|
|
|
for declared_pkg in declared_pkgs:
|
|
|
|
# there can be multiple versions installed of one package (transitive dependencies)
|
|
|
|
# but rpm doesn't support Provides: with a single package and multiple versions
|
|
|
|
# so let's declare the oldest version here
|
2022-04-05 17:39:40 +00:00
|
|
|
versions = [
|
|
|
|
version.parse(pkg_version)
|
|
|
|
for pkg_name, pkg_version in installed_pkgs
|
|
|
|
if pkg_name == declared_pkg
|
|
|
|
]
|
|
|
|
|
|
|
|
if not versions:
|
|
|
|
print(f"warning: {declared_pkg} missing in yarn.lock", file=sys.stderr)
|
|
|
|
continue
|
|
|
|
|
2020-04-22 16:14:45 +00:00
|
|
|
oldest_version = sorted(versions)[0]
|
2020-11-04 16:44:15 +00:00
|
|
|
yield f"Provides: bundled(npm({declared_pkg})) = {oldest_version}"
|
2020-04-22 16:14:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if len(sys.argv) != 2:
|
2020-11-25 17:55:36 +00:00
|
|
|
print(f"usage: {sys.argv[0]} package-X.Y.Z/", file=sys.stdout)
|
2020-04-22 16:14:45 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2020-11-25 17:55:36 +00:00
|
|
|
package_dir = sys.argv[1]
|
2022-06-22 17:13:29 +00:00
|
|
|
declared_pkgs = set()
|
2022-04-05 17:39:40 +00:00
|
|
|
for package_json_path in scan_package_json(package_dir):
|
2022-06-22 17:13:29 +00:00
|
|
|
declared_pkgs.update(read_declared_pkgs(package_json_path))
|
|
|
|
installed_pkgs = list(read_installed_pkgs(f"{package_dir}/yarn.lock"))
|
2020-04-22 16:14:45 +00:00
|
|
|
provides = list_provides(declared_pkgs, installed_pkgs)
|
|
|
|
for provide in sorted(provides):
|
|
|
|
print(provide)
|