Blob Blame History Raw
#!/usr/bin/python3

# This is a helper script that does 2 things:
# 1. It goes through the list of bundled modules and copies their licenses to a
#    specified directory. This then allows you to compare the licenses of the
#    previous pipenv version with a new one to see any changes.
# 2. It looks for all other *LICENSE* and *COPYING* files and warns you about
#    any have not been declared.

from pathlib import Path
import argparse
import os
import shutil

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Process pipenv bundled license files')
    parser.add_argument('--bundled-modules', action='store', required=True,
                        help='Path of a file listing the bundled modules and their licenses')
    parser.add_argument('--sources', action='store', required=True,
                        help='Path of the unpacked (and prepared) sources')
    parser.add_argument('--list-license-files', action='store_true',
                        help='Print a list of all license files for declared bundled modules')
    parser.add_argument('--license-diff-dir', action='store',
                        help='Path of an empty directory where to copy licenses for diffing')
    args = parser.parse_args()

    licenses_correct = True

    SOURCES_DIR = Path(args.sources)
    if args.license_diff_dir:
        LICENSE_DIFF_DIR = Path(args.license_diff_dir)
    all_declared_license_files = set()

    with open(args.bundled_modules) as f:
        # Ignore non-machine readable preface
        for line in f:
            if "BEGIN_MACHINE_READABLE_DATA" in line:
                break

        for line in f:
            # Ignore empty lines
            lineparts = line.strip().split(' ')
            if len(lineparts) < 2:
                continue

            # Find all the license files for this bundled module
            module = SOURCES_DIR / lineparts[1]
            if module.suffix:
                licenses = list(module.parent.glob(module.stem + "*LICENSE*"))
                licenses.extend(module.parent.glob(module.stem + "*COPYING*"))
            else:
                licenses = list(module.glob("LICENSE*"))
                licenses.extend(module.glob("COPYING*"))

            if len(licenses) == 0:
                print(f"WARNING: License file for `{module}` not found.")
                licenses_correct = False
                continue

            all_declared_license_files.update(licenses)
            if args.license_diff_dir:
                # Copy all the license files to the license diff dir
                module_dir = licenses[0].parent
                copy_to_dir = LICENSE_DIFF_DIR / module_dir.relative_to(SOURCES_DIR)
                os.makedirs(copy_to_dir, exist_ok=True)

                for license in licenses:
                    shutil.copyfile(license, copy_to_dir / license.name)

    if args.license_diff_dir:
        print(f"Copied {len(all_declared_license_files)} license files of listed bundled modules to directory `{LICENSE_DIFF_DIR}`.")
    else:
        print(f"Found {len(all_declared_license_files)} license files of listed bundled modules.")

    if args.list_license_files:
        print(f"List of all license files for declared bundled modules:")
        for l in sorted(all_declared_license_files):
            print(f"    {l}")

    # Look for all license files in the source dir
    all_licenses = set(SOURCES_DIR.rglob("*LICENSE*"))
    all_licenses.update(SOURCES_DIR.rglob("*COPYING*"))
    undeclared_licenses = all_licenses - all_declared_license_files

    # Exclude the main pipenv LICENSE file and license files in tests which we don't ship
    undeclared_licenses = [p for p in undeclared_licenses if p.parent != SOURCES_DIR
            and "tests" not in p.parts]

    # Print license files that are not declared
    if undeclared_licenses:
        licenses_correct = False
        print(f"WARNING:")
        print(f"  {len(undeclared_licenses)} bundled license files that were *NOT* declared in `{args.bundled_modules}` were found:")
        for l in sorted(undeclared_licenses):
            print(f"    {l}")

    if not args.list_license_files and licenses_correct:
        print(f"SUCCESS:")
        print(f"  The `{args.bundled_modules}` file declares all the bundled modules with licenses correctly.")