From 086b10d904bad971eb071f891bda61cc91890c2f Mon Sep 17 00:00:00 2001 From: John Andersen Date: Sat, 22 Jun 2024 14:17:24 -0700 Subject: [PATCH] scripts: oss scorecard pindeps: Use pip-tools compile Signed-off-by: John Andersen --- scripts/ossf_scorecard_pindeps.py | 100 ++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 19 deletions(-) diff --git a/scripts/ossf_scorecard_pindeps.py b/scripts/ossf_scorecard_pindeps.py index a90561e2ca..0e2b958489 100644 --- a/scripts/ossf_scorecard_pindeps.py +++ b/scripts/ossf_scorecard_pindeps.py @@ -4,11 +4,15 @@ import copy import shlex import pathlib +import textwrap +import tempfile import functools +import subprocess import dataclasses import urllib.request from typing import List, Optional +import tomli_w from pydantic import BaseModel @@ -18,6 +22,7 @@ @functools.cache +@snoop def pypi_package_json(package: str) -> dict: with urllib.request.urlopen( f"https://pypi.org/pypi/{package}/json" @@ -25,6 +30,13 @@ def pypi_package_json(package: str) -> dict: return json.load(resp) +def find_package_name_from_zip(url): + # - Download the package + # - Run sys.executable -m build . on the package + # - Read `Name: return this-value` from `dist/*.tar.gz/*/PKG-INFO + pass + + def pin_packages(cmd): cmd = copy.copy(cmd) i_install = cmd.index("install") @@ -34,17 +46,24 @@ def pin_packages(cmd): if cmd[i - 1] == "-e" and not arg.startswith("-") ] packages = [ - arg + ( + arg + if not ".zip" and not "://" in arg + else f"{find_package_name_from_zip(arg)} @ {arg}" + ) for i, arg in enumerate(cmd[i_install + 1 :], start=i_install + 1) if cmd[i - 1] != "-e" and not arg.startswith("-") ] + if "-U" in cmd: + cmd.remove("-U") + if "--upgrade" in cmd: + cmd.remove("--upgrade") for i, package_name in enumerate(packages): if ( not package_name.strip() - or package_name.startswith("http://") - or package_name.startswith("https://") - or package_name.startswith("git+") + or "http://" in package_name + or "https://" in package_name or "==" in package_name ): continue @@ -53,18 +72,47 @@ def pin_packages(cmd): pypi_latest_package_version = package_json["info"]["version"] - for release_dict in package_json["releases"][ - pypi_latest_package_version - ]: - cmd.insert( - cmd.index(package_name, i_install + 1) + 2, - f'--hash=sha256:{release_dict["digests"]["sha256"]}', + packages[ + packages.index(package_name, i_install + 1) + ] = f"{package_name}=={pypi_latest_package_version}" + + with tempfile.TemporaryDirectory() as tempdir: + if not packages: + return None + + pyproject_toml_path = pathlib.Path(tempdir, "pyproject.toml") + pyproject_toml_path.write_text( + tomli_w.dumps( + { + "build-system": { + "requires": ["setuptools >= 61.0"], + "build-backend": "setuptools.build_meta", + }, + "project": { + "name": "pip-tools-compile-pin-deps", + "version": "1.0.0", + "dependencies": [ + *editable_packages, + *packages, + ], + }, + }, ) + ) - cmd[ - cmd.index(package_name, i_install + 1) - ] = f"{package_name}=={pypi_latest_package_version}" - return cmd + print(pyproject_toml_path.read_text()) + + cmd = [ + sys.executable, + "-m", + "piptools", + "compile", + "--generate-hashes", + str(pyproject_toml_path.resolve()), + ] + return subprocess.check_output( + cmd, + ) class Snippet(BaseModel): @@ -135,6 +183,10 @@ def main(): location.physicalLocation.region.snippet.text ) pinned_pip_install_command = pin_packages(pip_install_command) + if pinned_pip_install_command is None: + snoop.pp("Nothing we can do here", pip_install_command) + continue + continue if ( location.physicalLocation.artifactLocation.uriBaseId != "%SRCROOT%" @@ -164,14 +216,24 @@ def main(): pip_install_command[-1], i_line_end ) + len(pip_install_command[-1]) line_end = line[i_line_end:] - line = ( - line_start - + shlex.join(pinned_pip_install_command) - + line_end + shlex.join(pinned_pip_install_command) + new_lines.append( + shlex.join( + [ + "echo", + "-e", + "a", + "|", + "tee", + "requirements.txt", + ] + ) ) + line = line_start + "-r requirements.txt" + line_end + snoop.pp(new_lines[-1], line) snoop.pp(location.physicalLocation, line_number, line) new_lines.append(line) - path.write_text("\n".join(new_lines)) + # path.write_text("\n".join(new_lines)) if __name__ == "__main__":