From a4cd2fe6c68cc3b50358761619ff7f626e67e52b Mon Sep 17 00:00:00 2001 From: Dave Page Date: Fri, 8 Feb 2019 14:48:41 +0000 Subject: [PATCH] Add a script for auto-generating the library.txt file. --- tools/dependency_inventory.py | 213 ++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 tools/dependency_inventory.py diff --git a/tools/dependency_inventory.py b/tools/dependency_inventory.py new file mode 100644 index 000000000..4fc539572 --- /dev/null +++ b/tools/dependency_inventory.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- + +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2019, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +# This utility will generate a iist of all dependencies, their upstream +# repos and licence information. + +import os +import json +import pkg_resources +import sys +import textwrap + +from subprocess import Popen, PIPE + +# Column sizes +name_size = 64 +version_size = 16 +licence_size = 32 + + +def print_title(title): + print(title) + print("=" * len(title), "\n") + + +def print_row(name, version, licence, url): + print("{} {} {} {}".format(name.ljust(name_size), + version.ljust(version_size), + licence.ljust(licence_size), url)) + + +def print_table_header(): + print_row("Name", "Version", "Licence", "URL") + print_row("----", "-------", "-------", "---") + + +def print_summary(count): + print("\n{} dependencies listed.\n".format(count)) + + +def get_python_deps(): + # Get the path to the requirements.txt file + req_file = os.path.realpath(os.path.dirname(__file__) + + "/../requirements.txt") + + with open(req_file, 'r') as req_file_p: + if sys.version_info[0] >= 3: + required = req_file_p.read().splitlines() + else: + required = req_file_p.read().decode("utf-8").splitlines() + + # Get the package info from the requirements file + requirements = pkg_resources.parse_requirements(required) + + have_unknowns = False + count = 0 + + # Iterate the packages and get the distribution info for each + for pkg in requirements: + metadata = [] + + try: + distribution = pkg_resources.get_distribution(pkg) + except IndexError as e: + # The package probably isn't required on this version of Python, + # thus we have no info about it. + have_unknowns = True + + name = pkg.name + version = "Unknown" + + for spec in pkg.specs: + if spec[0] == "==": + version = spec[1] + break + + licence = "Unknown" + url = "Unknown" + if pkg.url is not None: + url = pkg.url + + print_row(name, version, licence, url) + count = count + 1 + + # Next one.... + continue + + try: + metadata = distribution.get_metadata_lines('METADATA') + except FileNotFoundError: + metadata = distribution.get_metadata_lines('PKG-INFO') + + # Somewhere to store the info we need... + name = "" + version = "Unknown" + url = "Unknown" + licence = "Unknown" + + # Loop over each line in the metadata and grab the info we need + for line in metadata: + if line.startswith("Name: "): + name = line[6:] + + if line.startswith("Version: "): + version = line[9:] + + if line.startswith("License: "): + licence = line[9:] + + if line.startswith("Home-page: "): + url = line[11:] + + if name != "": + print_row(name, version, licence, url) + count = count + 1 + + if have_unknowns: + major = sys.version_info.major + minor = sys.version_info.minor + + print("") + print(textwrap.fill("NOTE: This report was generated using " + "Python {}.{}. Full information may not be shown " + "for Python modules that are not required with " + "this version.".format( + major, minor), width=79)) + + print_summary(count) + + +def get_js_deps(): + # Get the path to package.json file + web_dir = os.path.realpath(os.path.dirname(__file__) + "/../web/") + + # Build the Yarn command + cmd = ["yarn", "--cwd", web_dir, "licenses", "list", "--json"] + + # Run the command + process = Popen(cmd, stdout=PIPE) + (output, err) = process.communicate() + process.wait() + + # Cleanup the output + raw_data = json.loads(output.splitlines()[-1]) + modules = raw_data['data']['body'] + + # Loop through the modules, and output the data. + for module in modules: + name = module[0] + + version = "Unknown" + if module[1] != "": + version = module[1] + + licence = "Unknown" + if module[2] != "": + licence = module[2] + + url = "Unknown" + if module[3] != "": + url = module[3] + + print_row(name, version, licence, url) + + print_summary(len(modules)) + + +def dump_header(): + print_title("pgAdmin 4 Dependency Inventory") + + print(textwrap.fill( + "pgAdmin 4 is built on C++, Python and Javascript, and is " + "dependent on various third party libraries. These are " + "automatically compiled from the system, requirements.txt." + "and packages.json and list below.", + width=79), "\n") + + +def dump_cplusplus(): + print_title("C++ Dependencies") + print_table_header() + print_row("QT", "4.6.2+", "LGPL v2.1/3", "http://www.qt.io/") + print_row("Python", "2.7/3.4+", "PSF", "https://www.python.org/") + print_summary(2) + + +def dump_python(): + print_title("Python Dependencies") + print_table_header() + + get_python_deps() + + +def dump_js(): + print_title("Direct Javascript Dependencies") + print_table_header() + + get_js_deps() + + +# Let's do this thing! +dump_header() +dump_cplusplus() +dump_python() +dump_js()