e24e7dff07
The previous behaviour assumed that in a bundled package path, there is always `node_modules` directory on each other spot – i.e.: npm/node_modules/<dep1>/node_modules/<subdep> ^ ^ With namespaced bundled packages, this is no longer necessary the truth: npm/node_modules/@nmcli/<dep1>/node_modules/… ^ ! – expected node_modules --- The previous implementation considered any directory not named `node_modules` as a package directory, and tried to process it as such. Among other things, it pruned the list of subdirectories to be processed to just another `node_modules` subdir, if that existed. With namespaced packages, this pruning in essence happened too soon, and so they were skipped altogether. With this patch applied, only directories that directly contain the `package.json` file are processed as package directories, meaning that the walk should correctly descend into namespaces (even nested ones, if they appear). Resolves: rhbz#2029904
122 lines
4.4 KiB
Python
Executable File
122 lines
4.4 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright 2012 T.C. Hollingsworth <tchollingsworth@gmail.com>
|
|
# Copyright 2017 Tomas Tomecek <ttomecek@redhat.com>
|
|
# Copyright 2019 Jan Staněk <jstanek@redhat.com>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to
|
|
# deal in the Software without restriction, including without limitation the
|
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
# sell copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
# IN THE SOFTWARE.
|
|
|
|
"""Automatic provides generator for Node.js libraries.
|
|
|
|
Metadata taken from package.json. See `man npm-json` for details.
|
|
"""
|
|
|
|
from __future__ import print_function, with_statement
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from itertools import chain, groupby
|
|
|
|
DEPENDENCY_TEMPLATE = "npm(%(name)s) = %(version)s"
|
|
BUNDLED_TEMPLATE = "bundled(nodejs-%(name)s) = %(version)s"
|
|
NODE_MODULES = {"node_modules", "node_modules_prod"}
|
|
|
|
|
|
class PrivatePackage(RuntimeError):
|
|
"""Private package metadata that should not be listed."""
|
|
|
|
|
|
#: Something is wrong with the ``package.json`` file
|
|
_INVALID_METADATA_FILE = (IOError, PrivatePackage, KeyError)
|
|
|
|
|
|
def format_metadata(metadata, bundled=False):
|
|
"""Format ``package.json``-like metadata into RPM dependency.
|
|
|
|
Arguments:
|
|
metadata (dict): Package metadata, presumably read from ``package.json``.
|
|
bundled (bool): Should the bundled dependency format be used?
|
|
|
|
Returns:
|
|
str: RPM dependency (i.e. ``npm(example) = 1.0.0``)
|
|
|
|
Raises:
|
|
KeyError: Expected key (i.e. ``name``, ``version``) missing in metadata.
|
|
PrivatePackage: The metadata indicate private (unlisted) package.
|
|
"""
|
|
|
|
# Skip private packages
|
|
if metadata.get("private", False):
|
|
raise PrivatePackage(metadata)
|
|
|
|
template = BUNDLED_TEMPLATE if bundled else DEPENDENCY_TEMPLATE
|
|
return template % metadata
|
|
|
|
|
|
def generate_dependencies(module_path, module_dir_set=NODE_MODULES):
|
|
"""Generate RPM dependency for a module and all it's dependencies.
|
|
|
|
Arguments:
|
|
module_path (str): Path to a module directory or it's ``package.json``
|
|
module_dir_set (set): Base names of directories to look into
|
|
for bundled dependencies.
|
|
|
|
Yields:
|
|
str: RPM dependency for the module and each of it's (public) bundled dependencies.
|
|
|
|
Raises:
|
|
ValueError: module_path is not valid module or ``package.json`` file
|
|
"""
|
|
|
|
# Determine paths to root module directory and package.json
|
|
if os.path.isdir(module_path):
|
|
root_dir = module_path
|
|
elif os.path.basename(module_path) == "package.json":
|
|
root_dir = os.path.dirname(module_path)
|
|
else: # Invalid metadata path
|
|
raise ValueError("Invalid module path '%s'" % module_path)
|
|
|
|
for dir_path, subdir_list, file_list in os.walk(root_dir):
|
|
# We are only interested in directories that contain package.json
|
|
if "package.json" not in file_list:
|
|
continue
|
|
|
|
# Read and format metadata
|
|
metadata_path = os.path.join(dir_path, "package.json")
|
|
bundled = dir_path != root_dir
|
|
try:
|
|
with open(metadata_path, mode="r") as metadata_file:
|
|
metadata = json.load(metadata_file)
|
|
yield format_metadata(metadata, bundled=bundled)
|
|
except _INVALID_METADATA_FILE:
|
|
pass # Ignore
|
|
|
|
# Only visit subdirectories in module_dir_set
|
|
subdir_list[:] = list(module_dir_set & set(subdir_list))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
module_paths = (path.strip() for path in sys.stdin)
|
|
provides = chain.from_iterable(generate_dependencies(m) for m in module_paths)
|
|
|
|
# sort|uniq
|
|
for provide, __ in groupby(sorted(provides)):
|
|
print(provide)
|