From 7b1e87230a5bbd9986321868b5ab4f3ba4387f4c Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Mon, 12 Jan 2026 15:28:32 +1000 Subject: [PATCH 1/7] feat: added simple validation script to dockerfile generation. Signed-off-by: Abhinav Pradeep --- .../common_spec/base_spec.py | 3 ++ .../common_spec/pypi_spec.py | 30 +++++++++++++++++++ .../dockerfile/pypi_dockerfile_output.py | 28 ++++++++++++++++- .../package_registry/pypi_registry.py | 8 +++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/macaron/build_spec_generator/common_spec/base_spec.py b/src/macaron/build_spec_generator/common_spec/base_spec.py index 698a0b948..771bede70 100644 --- a/src/macaron/build_spec_generator/common_spec/base_spec.py +++ b/src/macaron/build_spec_generator/common_spec/base_spec.py @@ -83,6 +83,9 @@ class BaseBuildSpecDict(TypedDict, total=False): #: Flag to indicate if the artifact includes binaries. has_binaries: NotRequired[bool] + + #: The artifacts that were analyzed in generating the build specification. + upstream_artifacts: dict[str, str] class BaseBuildSpec(ABC): diff --git a/src/macaron/build_spec_generator/common_spec/pypi_spec.py b/src/macaron/build_spec_generator/common_spec/pypi_spec.py index 328481f45..c67d32ad7 100644 --- a/src/macaron/build_spec_generator/common_spec/pypi_spec.py +++ b/src/macaron/build_spec_generator/common_spec/pypi_spec.py @@ -106,6 +106,7 @@ def resolve_fields(self, purl: PackageURL) -> None: metadata=[], ) + artifacts: dict[str, str] = {} pypi_package_json = pypi_registry.find_or_create_pypi_asset(purl.name, purl.version, registry_info) patched_build_commands: list[list[str]] = [] build_backends_set: set[str] = set() @@ -184,6 +185,8 @@ def resolve_fields(self, purl: PackageURL) -> None: try: with pypi_package_json.sourcecode(): + artifacts["sdist"] = pypi_package_json.sdist_url + logger.debug("sdist url at %s", artifacts["sdist"]) try: # Get the build time requirements from ["build-system", "requires"] pyproject_content = pypi_package_json.get_sourcecode_file_contents("pyproject.toml") @@ -268,6 +271,33 @@ def resolve_fields(self, purl: PackageURL) -> None: # We do not generate a build command for non-pure packages if not self.data["has_binaries"]: patched_build_commands = self.get_default_build_commands(self.data["build_tools"]) + self.data["upstream_artifacts"] = artifacts + + if not patched_build_commands: + # Resolve and patch build commands. + + # To ensure that selected_build_commands is never empty, we seed with the fallback + # command of python -m build --wheel -n + if self.data["build_commands"]: + selected_build_commands = self.data["build_commands"] + else: + self.data["build_commands"] = ["python -m build --wheel -n".split()] + selected_build_commands = ( + self.get_default_build_commands(self.data["build_tools"]) or self.data["build_commands"] + ) + + logger.debug(selected_build_commands) + + patched_build_commands = ( + patch_commands( + cmds_sequence=selected_build_commands, + patches=CLI_COMMAND_PATCHES, + ) + or [] + ) + if not patched_build_commands: + raise GenerateBuildSpecError(f"Failed to patch command sequences {selected_build_commands}.") + self.data["build_commands"] = patched_build_commands def add_parsed_requirement(self, build_requirements: dict[str, str], requirement: str) -> None: diff --git a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py index 87e5a1d0d..307ebc1cd 100644 --- a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py +++ b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py @@ -62,18 +62,22 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: build_tool_install = ( f"pip install {buildspec['build_tools'][0]} && if test -f \"flit.ini\"; then python -m flit.tomlify; fi && " ) + modern_build_command = build_tool_install + " ".join(x for x in buildspec["build_commands"][0]) legacy_build_command = ( 'if test -f "setup.py"; then pip install wheel && python setup.py bdist_wheel; ' "else python -m build --wheel -n; fi" ) + wheel_url = buildspec["upstream_artifacts"]["wheel"] + wheel_name = wheel_url.rsplit("/", 1)[-1] + dockerfile_content = f""" #syntax=docker/dockerfile:1.10 FROM oraclelinux:9 # Install core tools - RUN dnf -y install which wget tar git + RUN dnf -y install which wget tar unzip git # Install compiler and make RUN dnf -y install gcc make @@ -126,7 +130,29 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: EOF # Run the build + RUN source /deps/bin/activate && {modern_build_command if version in SpecifierSet(">=3.6") else legacy_build_command} + + # Validate script + RUN cat <<'EOF' >/validate + # Capture artifacts generated + ARTIFACTS=(/src/dist/*) + # Ensure we only have one artefact + [ ${{#ARTIFACTS[@]}} -eq 1 ] || {{ echo "Unexpected artifacts prodced!"; exit 1; }} + # BUILT_WHEEL is the artefact we built + BUILT_WHEEL=${{ARTIFACTS[0]}} + # Download the wheel + wget -q {wheel_url} + # Compare wheel names + [ $(basename $BUILT_WHEEL) == "{wheel_name}" ] || {{ echo "Wheel name does not match!"; exit 1; }} + # Compare file tree + (unzip -Z1 $BUILT_WHEEL | sort) > built.tree + (unzip -Z1 "{wheel_name}" | sort ) > pypi_artefact.tree + diff -u built.tree pypi_artefact.tree || {{ echo "File trees do not match!"; exit 1; }} + echo "Success!" + EOF + + ENTRYPOINT ["/bin/bash","/validate"] """ return dedent(dockerfile_content) diff --git a/src/macaron/slsa_analyzer/package_registry/pypi_registry.py b/src/macaron/slsa_analyzer/package_registry/pypi_registry.py index 935f662c7..6481130a9 100644 --- a/src/macaron/slsa_analyzer/package_registry/pypi_registry.py +++ b/src/macaron/slsa_analyzer/package_registry/pypi_registry.py @@ -704,6 +704,12 @@ class PyPIPackageJsonAsset: #: The source code temporary location name. package_sourcecode_path: str = field(init=False) + #: URL of the sdist file. + sdist_url: str = field(init=False) + + #: URL of the wheel file. + wheel_url: str = field(init=False) + #: The wheel temporary location name. wheel_path: str = field(init=False) @@ -832,6 +838,7 @@ def get_sourcecode_url(self, package_type: str = "sdist") -> str | None: fragment="", ).geturl() logger.debug("Found source URL: %s", configured_source_url) + self.sdist_url = configured_source_url return configured_source_url return None @@ -892,6 +899,7 @@ def get_wheel_url(self, tag: str = "none-any") -> str | None: fragment="", ).geturl() logger.debug("Found wheel URL: %s", configured_wheel_url) + self.wheel_url = configured_wheel_url return configured_wheel_url return None From 499a5d091be4f7fb9b3e7e26a19617f1440baeb2 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Tue, 13 Jan 2026 14:27:55 +1000 Subject: [PATCH 2/7] fix: updated integration and unit tests to reflect new feature. Signed-off-by: Abhinav Pradeep --- .../dockerfile/pypi_dockerfile_output.py | 1 - .../test_pypi_dockerfile_output.ambr | 23 ++++++++++++++++++- .../dockerfile/test_pypi_dockerfile_output.py | 6 +++++ .../expected_default.buildspec | 6 ++++- .../expected_dockerfile.buildspec | 23 ++++++++++++++++++- .../expected_default.buildspec | 6 ++++- .../expected_dockerfile.buildspec | 23 ++++++++++++++++++- .../pypi_toga/expected_default.buildspec | 6 ++++- .../pypi_toga/expected_dockerfile.buildspec | 23 ++++++++++++++++++- 9 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py index 307ebc1cd..146eb9edf 100644 --- a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py +++ b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py @@ -130,7 +130,6 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: EOF # Run the build - RUN source /deps/bin/activate && {modern_build_command if version in SpecifierSet(">=3.6") else legacy_build_command} # Validate script diff --git a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr index 655628572..d405b0cf9 100644 --- a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr +++ b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr @@ -6,7 +6,7 @@ FROM oraclelinux:9 # Install core tools - RUN dnf -y install which wget tar git + RUN dnf -y install which wget tar unzip git # Install compiler and make RUN dnf -y install gcc make @@ -69,5 +69,26 @@ # Run the build RUN source /deps/bin/activate && python -m build + # Validate script + RUN cat <<'EOF' >/validate + # Capture artifacts generated + ARTIFACTS=(/src/dist/*) + # Ensure we only have one artefact + [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } + # BUILT_WHEEL is the artefact we built + BUILT_WHEEL=${ARTIFACTS[0]} + # Download the wheel + wget -q https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl + # Compare wheel names + [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } + # Compare file tree + (unzip -Z1 $BUILT_WHEEL | sort) > built.tree + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + echo "Success!" + EOF + + ENTRYPOINT ["/bin/bash","/validate"] + ''' # --- diff --git a/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py b/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py index c8d4d8882..b778ee8f5 100644 --- a/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py +++ b/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py @@ -32,6 +32,12 @@ def fixture_base_build_spec() -> BaseBuildSpecDict: "build_commands": [["python", "-m", "build"]], "build_requires": {"setuptools": "==80.9.0", "wheel": ""}, "build_backends": ["setuptools.build_meta"], + "upstream_artifacts": { + "wheel": "https://files.pythonhosted.org/packages/96/c5/" + "1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", + "sdist": "https://files.pythonhosted.org/packages/cc/7e/" + "b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", + }, } ) diff --git a/tests/integration/cases/pypi_cachetools/expected_default.buildspec b/tests/integration/cases/pypi_cachetools/expected_default.buildspec index 2a05c0e95..5e189ae91 100644 --- a/tests/integration/cases/pypi_cachetools/expected_default.buildspec +++ b/tests/integration/cases/pypi_cachetools/expected_default.buildspec @@ -31,5 +31,9 @@ }, "build_backends": [ "setuptools.build_meta" - ] + ], + "upstream_artifacts": { + "wheel": "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", + "sdist": "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz" + } } diff --git a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec index 254f0b56e..37b2041d4 100644 --- a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec @@ -3,7 +3,7 @@ FROM oraclelinux:9 # Install core tools -RUN dnf -y install which wget tar git +RUN dnf -y install which wget tar unzip git # Install compiler and make RUN dnf -y install gcc make @@ -65,3 +65,24 @@ EOF # Run the build RUN source /deps/bin/activate && python -m build --wheel -n + +# Validate script +RUN cat <<'EOF' >/validate + # Capture artifacts generated + ARTIFACTS=(/src/dist/*) + # Ensure we only have one artefact + [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } + # BUILT_WHEEL is the artefact we built + BUILT_WHEEL=${ARTIFACTS[0]} + # Download the wheel + wget -q https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl + # Compare wheel names + [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } + # Compare file tree + (unzip -Z1 $BUILT_WHEEL | sort) > built.tree + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + echo "Success!" +EOF + +ENTRYPOINT ["/bin/bash","/validate"] diff --git a/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec b/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec index 3fbb4fcbc..bb638f9af 100644 --- a/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec +++ b/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec @@ -29,5 +29,9 @@ }, "build_backends": [ "flit_core.buildapi" - ] + ], + "upstream_artifacts": { + "wheel": "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", + "sdist": "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz" + } } diff --git a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec index e4133eb2c..00477a564 100644 --- a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec @@ -3,7 +3,7 @@ FROM oraclelinux:9 # Install core tools -RUN dnf -y install which wget tar git +RUN dnf -y install which wget tar unzip git # Install compiler and make RUN dnf -y install gcc make @@ -65,3 +65,24 @@ EOF # Run the build RUN source /deps/bin/activate && pip install flit && if test -f "flit.ini"; then python -m flit.tomlify; fi && flit build + +# Validate script +RUN cat <<'EOF' >/validate + # Capture artifacts generated + ARTIFACTS=(/src/dist/*) + # Ensure we only have one artefact + [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } + # BUILT_WHEEL is the artefact we built + BUILT_WHEEL=${ARTIFACTS[0]} + # Download the wheel + wget -q https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl + # Compare wheel names + [ $(basename $BUILT_WHEEL) == "markdown_it_py-4.0.0-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } + # Compare file tree + (unzip -Z1 $BUILT_WHEEL | sort) > built.tree + (unzip -Z1 "markdown_it_py-4.0.0-py3-none-any.whl" | sort ) > pypi_artefact.tree + diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + echo "Success!" +EOF + +ENTRYPOINT ["/bin/bash","/validate"] diff --git a/tests/integration/cases/pypi_toga/expected_default.buildspec b/tests/integration/cases/pypi_toga/expected_default.buildspec index 076503858..bab3f26e0 100644 --- a/tests/integration/cases/pypi_toga/expected_default.buildspec +++ b/tests/integration/cases/pypi_toga/expected_default.buildspec @@ -33,5 +33,9 @@ }, "build_backends": [ "setuptools.build_meta" - ] + ], + "upstream_artifacts": { + "wheel": "https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl", + "sdist": "https://files.pythonhosted.org/packages/17/e7/0924150329474d61e9f40f8bba1056d640cba22438e05355924019111b46/toga-0.5.1.tar.gz" + } } diff --git a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec index d50340d8b..e8e4948e1 100644 --- a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec @@ -3,7 +3,7 @@ FROM oraclelinux:9 # Install core tools -RUN dnf -y install which wget tar git +RUN dnf -y install which wget tar unzip git # Install compiler and make RUN dnf -y install gcc make @@ -65,3 +65,24 @@ EOF # Run the build RUN source /deps/bin/activate && python -m build --wheel -n + +# Validate script +RUN cat <<'EOF' >/validate + # Capture artifacts generated + ARTIFACTS=(/src/dist/*) + # Ensure we only have one artefact + [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } + # BUILT_WHEEL is the artefact we built + BUILT_WHEEL=${ARTIFACTS[0]} + # Download the wheel + wget -q https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl + # Compare wheel names + [ $(basename $BUILT_WHEEL) == "toga-0.5.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } + # Compare file tree + (unzip -Z1 $BUILT_WHEEL | sort) > built.tree + (unzip -Z1 "toga-0.5.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + echo "Success!" +EOF + +ENTRYPOINT ["/bin/bash","/validate"] From 1b88c1eecd66845f5af8a3f5c5a6ed43e304e273 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Wed, 21 Jan 2026 12:18:01 +1000 Subject: [PATCH 3/7] feat: file tree comparison now ignores metadata files Signed-off-by: Abhinav Pradeep --- .../build_spec_generator/dockerfile/pypi_dockerfile_output.py | 4 ++-- .../dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr | 4 ++-- .../cases/pypi_cachetools/expected_dockerfile.buildspec | 4 ++-- .../cases/pypi_markdown-it-py/expected_dockerfile.buildspec | 4 ++-- .../integration/cases/pypi_toga/expected_dockerfile.buildspec | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py index 146eb9edf..7dfc8cbf3 100644 --- a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py +++ b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py @@ -145,8 +145,8 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: # Compare wheel names [ $(basename $BUILT_WHEEL) == "{wheel_name}" ] || {{ echo "Wheel name does not match!"; exit 1; }} # Compare file tree - (unzip -Z1 $BUILT_WHEEL | sort) > built.tree - (unzip -Z1 "{wheel_name}" | sort ) > pypi_artefact.tree + (unzip -Z1 $BUILT_WHEEL | grep -v '\\.dist-info' | sort) > built.tree + (unzip -Z1 "{wheel_name}" | grep -v '\\.dist-info' | sort ) > pypi_artefact.tree diff -u built.tree pypi_artefact.tree || {{ echo "File trees do not match!"; exit 1; }} echo "Success!" EOF diff --git a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr index d405b0cf9..48769cb5e 100644 --- a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr +++ b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr @@ -82,8 +82,8 @@ # Compare wheel names [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree - (unzip -Z1 $BUILT_WHEEL | sort) > built.tree - (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec index 37b2041d4..0aa97a3e9 100644 --- a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec @@ -79,8 +79,8 @@ RUN cat <<'EOF' >/validate # Compare wheel names [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree - (unzip -Z1 $BUILT_WHEEL | sort) > built.tree - (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec index 00477a564..c8eeb9e33 100644 --- a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec @@ -79,8 +79,8 @@ RUN cat <<'EOF' >/validate # Compare wheel names [ $(basename $BUILT_WHEEL) == "markdown_it_py-4.0.0-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree - (unzip -Z1 $BUILT_WHEEL | sort) > built.tree - (unzip -Z1 "markdown_it_py-4.0.0-py3-none-any.whl" | sort ) > pypi_artefact.tree + (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree + (unzip -Z1 "markdown_it_py-4.0.0-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec index e8e4948e1..4aef15c8f 100644 --- a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec @@ -79,8 +79,8 @@ RUN cat <<'EOF' >/validate # Compare wheel names [ $(basename $BUILT_WHEEL) == "toga-0.5.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree - (unzip -Z1 $BUILT_WHEEL | sort) > built.tree - (unzip -Z1 "toga-0.5.1-py3-none-any.whl" | sort ) > pypi_artefact.tree + (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree + (unzip -Z1 "toga-0.5.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF From e13c7cd84e739df041ab2d0c343c31a3d9e3e9f7 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Mon, 9 Feb 2026 14:14:36 +1000 Subject: [PATCH 4/7] fix: addressed rebase related issues Signed-off-by: Abhinav Pradeep --- .../common_spec/pypi_spec.py | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/macaron/build_spec_generator/common_spec/pypi_spec.py b/src/macaron/build_spec_generator/common_spec/pypi_spec.py index c67d32ad7..cadff9295 100644 --- a/src/macaron/build_spec_generator/common_spec/pypi_spec.py +++ b/src/macaron/build_spec_generator/common_spec/pypi_spec.py @@ -271,34 +271,8 @@ def resolve_fields(self, purl: PackageURL) -> None: # We do not generate a build command for non-pure packages if not self.data["has_binaries"]: patched_build_commands = self.get_default_build_commands(self.data["build_tools"]) - self.data["upstream_artifacts"] = artifacts - - if not patched_build_commands: - # Resolve and patch build commands. - - # To ensure that selected_build_commands is never empty, we seed with the fallback - # command of python -m build --wheel -n - if self.data["build_commands"]: - selected_build_commands = self.data["build_commands"] - else: - self.data["build_commands"] = ["python -m build --wheel -n".split()] - selected_build_commands = ( - self.get_default_build_commands(self.data["build_tools"]) or self.data["build_commands"] - ) - - logger.debug(selected_build_commands) - - patched_build_commands = ( - patch_commands( - cmds_sequence=selected_build_commands, - patches=CLI_COMMAND_PATCHES, - ) - or [] - ) - if not patched_build_commands: - raise GenerateBuildSpecError(f"Failed to patch command sequences {selected_build_commands}.") - self.data["build_commands"] = patched_build_commands + self.data["upstream_artifacts"] = artifacts def add_parsed_requirement(self, build_requirements: dict[str, str], requirement: str) -> None: """ From 80ea410a242ba4a37d9d0d0739e6a80a56e76df8 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Mon, 9 Feb 2026 14:38:25 +1000 Subject: [PATCH 5/7] fix: formatting Signed-off-by: Abhinav Pradeep --- src/macaron/build_spec_generator/common_spec/base_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macaron/build_spec_generator/common_spec/base_spec.py b/src/macaron/build_spec_generator/common_spec/base_spec.py index 771bede70..e39444b2a 100644 --- a/src/macaron/build_spec_generator/common_spec/base_spec.py +++ b/src/macaron/build_spec_generator/common_spec/base_spec.py @@ -83,7 +83,7 @@ class BaseBuildSpecDict(TypedDict, total=False): #: Flag to indicate if the artifact includes binaries. has_binaries: NotRequired[bool] - + #: The artifacts that were analyzed in generating the build specification. upstream_artifacts: dict[str, str] From a71d6104f696cf8e580fdbb017ed37ef781d9e09 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Tue, 10 Feb 2026 11:21:22 +1000 Subject: [PATCH 6/7] fix: integration test and upstream_artifacts bug from merge conflict resolution Signed-off-by: Abhinav Pradeep --- src/macaron/build_spec_generator/common_spec/pypi_spec.py | 1 + .../cases/pypi_tree-sitter/expected_default.buildspec | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/macaron/build_spec_generator/common_spec/pypi_spec.py b/src/macaron/build_spec_generator/common_spec/pypi_spec.py index cadff9295..d12104601 100644 --- a/src/macaron/build_spec_generator/common_spec/pypi_spec.py +++ b/src/macaron/build_spec_generator/common_spec/pypi_spec.py @@ -142,6 +142,7 @@ def resolve_fields(self, purl: PackageURL) -> None: try: # The wheel function handles downloading binaries in the case that we cannot find a pure wheel. with pypi_package_json.wheel(download_binaries=self.data["has_binaries"]): + artifacts["wheel"] = pypi_package_json.wheel_url logger.debug("Wheel at %s", pypi_package_json.wheel_path) # Should only have .dist-info directory. logger.debug("It has directories %s", ",".join(os.listdir(pypi_package_json.wheel_path))) diff --git a/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec b/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec index c0612c42d..9363d1e9d 100644 --- a/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec +++ b/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec @@ -22,5 +22,8 @@ }, "build_backends": [ "setuptools.build_meta" - ] + ], + "upstream_artifacts": { + "sdist": "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz" + } } From 289d091da421c9cf178fe566abb0a89ea7e8a7d6 Mon Sep 17 00:00:00 2001 From: Abhinav Pradeep Date: Tue, 17 Feb 2026 10:25:59 +1000 Subject: [PATCH 7/7] fix: review Signed-off-by: Abhinav Pradeep --- .../common_spec/base_spec.py | 2 +- .../common_spec/pypi_spec.py | 10 +++---- .../dockerfile/pypi_dockerfile_output.py | 29 +++++++++++++------ .../package_registry/pypi_registry.py | 4 +-- .../test_pypi_dockerfile_output.ambr | 17 ++++++----- .../dockerfile/test_pypi_dockerfile_output.py | 12 +++++--- .../expected_default.buildspec | 8 +++-- .../expected_dockerfile.buildspec | 17 ++++++----- .../expected_default.buildspec | 8 +++-- .../expected_dockerfile.buildspec | 17 ++++++----- .../pypi_toga/expected_default.buildspec | 8 +++-- .../pypi_toga/expected_dockerfile.buildspec | 17 ++++++----- .../expected_default.buildspec | 2 +- .../cases/pypi_tree-sitter/test.yaml | 2 +- 14 files changed, 96 insertions(+), 57 deletions(-) diff --git a/src/macaron/build_spec_generator/common_spec/base_spec.py b/src/macaron/build_spec_generator/common_spec/base_spec.py index e39444b2a..6477801fd 100644 --- a/src/macaron/build_spec_generator/common_spec/base_spec.py +++ b/src/macaron/build_spec_generator/common_spec/base_spec.py @@ -85,7 +85,7 @@ class BaseBuildSpecDict(TypedDict, total=False): has_binaries: NotRequired[bool] #: The artifacts that were analyzed in generating the build specification. - upstream_artifacts: dict[str, str] + upstream_artifacts: dict[str, list[str]] class BaseBuildSpec(ABC): diff --git a/src/macaron/build_spec_generator/common_spec/pypi_spec.py b/src/macaron/build_spec_generator/common_spec/pypi_spec.py index d12104601..ee67578c9 100644 --- a/src/macaron/build_spec_generator/common_spec/pypi_spec.py +++ b/src/macaron/build_spec_generator/common_spec/pypi_spec.py @@ -106,7 +106,7 @@ def resolve_fields(self, purl: PackageURL) -> None: metadata=[], ) - artifacts: dict[str, str] = {} + upstream_artifacts: dict[str, list[str]] = {} pypi_package_json = pypi_registry.find_or_create_pypi_asset(purl.name, purl.version, registry_info) patched_build_commands: list[list[str]] = [] build_backends_set: set[str] = set() @@ -142,7 +142,7 @@ def resolve_fields(self, purl: PackageURL) -> None: try: # The wheel function handles downloading binaries in the case that we cannot find a pure wheel. with pypi_package_json.wheel(download_binaries=self.data["has_binaries"]): - artifacts["wheel"] = pypi_package_json.wheel_url + upstream_artifacts["wheels"] = pypi_package_json.wheel_urls logger.debug("Wheel at %s", pypi_package_json.wheel_path) # Should only have .dist-info directory. logger.debug("It has directories %s", ",".join(os.listdir(pypi_package_json.wheel_path))) @@ -186,8 +186,8 @@ def resolve_fields(self, purl: PackageURL) -> None: try: with pypi_package_json.sourcecode(): - artifacts["sdist"] = pypi_package_json.sdist_url - logger.debug("sdist url at %s", artifacts["sdist"]) + upstream_artifacts["sdist"] = [pypi_package_json.sdist_url] + logger.debug("sdist url at %s", upstream_artifacts["sdist"]) try: # Get the build time requirements from ["build-system", "requires"] pyproject_content = pypi_package_json.get_sourcecode_file_contents("pyproject.toml") @@ -273,7 +273,7 @@ def resolve_fields(self, purl: PackageURL) -> None: if not self.data["has_binaries"]: patched_build_commands = self.get_default_build_commands(self.data["build_tools"]) self.data["build_commands"] = patched_build_commands - self.data["upstream_artifacts"] = artifacts + self.data["upstream_artifacts"] = upstream_artifacts def add_parsed_requirement(self, build_requirements: dict[str, str], requirement: str) -> None: """ diff --git a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py index 7dfc8cbf3..67d1c6308 100644 --- a/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py +++ b/src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py @@ -69,8 +69,16 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: "else python -m build --wheel -n; fi" ) - wheel_url = buildspec["upstream_artifacts"]["wheel"] - wheel_name = wheel_url.rsplit("/", 1)[-1] + wheel_url: str = "" + wheel_name: str = "" + + wheel_urls = buildspec["upstream_artifacts"]["wheels"] + # We currently only look for the pure wheel, if it exists + if wheel_urls: + wheel_url = list(wheel_urls)[0] + wheel_name = wheel_url.rsplit("/", 1)[-1] + else: + logger.debug("We could not find an upstream artifact, and therefore we cannot run validation") dockerfile_content = f""" #syntax=docker/dockerfile:1.10 @@ -134,20 +142,23 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str: # Validate script RUN cat <<'EOF' >/validate + [ -n "{wheel_url}" ] || {{ echo "No upstream artifact to validate against."; exit 1; }} # Capture artifacts generated - ARTIFACTS=(/src/dist/*) - # Ensure we only have one artefact - [ ${{#ARTIFACTS[@]}} -eq 1 ] || {{ echo "Unexpected artifacts prodced!"; exit 1; }} - # BUILT_WHEEL is the artefact we built - BUILT_WHEEL=${{ARTIFACTS[0]}} + WHEELS=(/src/dist/*.whl) + # Ensure we only have one artifact + [ ${{#WHEELS[@]}} -eq 1 ] || {{ echo "Unexpected artifacts produced!"; exit 1; }} + # BUILT_WHEEL is the artifact we built + BUILT_WHEEL=${{WHEELS[0]}} + # Ensure the artifact produced is not the literal returned by the glob + [ -e $BUILT_WHEEL ] || {{ echo "No wheels found!"; exit 1; }} # Download the wheel wget -q {wheel_url} # Compare wheel names [ $(basename $BUILT_WHEEL) == "{wheel_name}" ] || {{ echo "Wheel name does not match!"; exit 1; }} # Compare file tree (unzip -Z1 $BUILT_WHEEL | grep -v '\\.dist-info' | sort) > built.tree - (unzip -Z1 "{wheel_name}" | grep -v '\\.dist-info' | sort ) > pypi_artefact.tree - diff -u built.tree pypi_artefact.tree || {{ echo "File trees do not match!"; exit 1; }} + (unzip -Z1 "{wheel_name}" | grep -v '\\.dist-info' | sort ) > pypi_artifact.tree + diff -u built.tree pypi_artifact.tree || {{ echo "File trees do not match!"; exit 1; }} echo "Success!" EOF diff --git a/src/macaron/slsa_analyzer/package_registry/pypi_registry.py b/src/macaron/slsa_analyzer/package_registry/pypi_registry.py index 6481130a9..9f988ed80 100644 --- a/src/macaron/slsa_analyzer/package_registry/pypi_registry.py +++ b/src/macaron/slsa_analyzer/package_registry/pypi_registry.py @@ -708,7 +708,7 @@ class PyPIPackageJsonAsset: sdist_url: str = field(init=False) #: URL of the wheel file. - wheel_url: str = field(init=False) + wheel_urls: list[str] = field(init=False) #: The wheel temporary location name. wheel_path: str = field(init=False) @@ -899,7 +899,7 @@ def get_wheel_url(self, tag: str = "none-any") -> str | None: fragment="", ).geturl() logger.debug("Found wheel URL: %s", configured_wheel_url) - self.wheel_url = configured_wheel_url + self.wheel_urls = [configured_wheel_url] return configured_wheel_url return None diff --git a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr index 48769cb5e..8ff65b0da 100644 --- a/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr +++ b/tests/build_spec_generator/dockerfile/__snapshots__/test_pypi_dockerfile_output.ambr @@ -71,20 +71,23 @@ # Validate script RUN cat <<'EOF' >/validate + [ -n "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl" ] || { echo "No upstream artifact to validate against."; exit 1; } # Capture artifacts generated - ARTIFACTS=(/src/dist/*) - # Ensure we only have one artefact - [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } - # BUILT_WHEEL is the artefact we built - BUILT_WHEEL=${ARTIFACTS[0]} + WHEELS=(/src/dist/*.whl) + # Ensure we only have one artifact + [ ${#WHEELS[@]} -eq 1 ] || { echo "Unexpected artifacts produced!"; exit 1; } + # BUILT_WHEEL is the artifact we built + BUILT_WHEEL=${WHEELS[0]} + # Ensure the artifact produced is not the literal returned by the glob + [ -e $BUILT_WHEEL ] || { echo "No wheels found!"; exit 1; } # Download the wheel wget -q https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl # Compare wheel names [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree - (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree - diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artifact.tree + diff -u built.tree pypi_artifact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py b/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py index b778ee8f5..4c8902325 100644 --- a/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py +++ b/tests/build_spec_generator/dockerfile/test_pypi_dockerfile_output.py @@ -33,10 +33,14 @@ def fixture_base_build_spec() -> BaseBuildSpecDict: "build_requires": {"setuptools": "==80.9.0", "wheel": ""}, "build_backends": ["setuptools.build_meta"], "upstream_artifacts": { - "wheel": "https://files.pythonhosted.org/packages/96/c5/" - "1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", - "sdist": "https://files.pythonhosted.org/packages/cc/7e/" - "b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", + "wheels": [ + "https://files.pythonhosted.org/packages/96/c5/" + "1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl" + ], + "sdist": [ + "https://files.pythonhosted.org/packages/cc/7e/" + "b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz" + ], }, } ) diff --git a/tests/integration/cases/pypi_cachetools/expected_default.buildspec b/tests/integration/cases/pypi_cachetools/expected_default.buildspec index 5e189ae91..87859fbd4 100644 --- a/tests/integration/cases/pypi_cachetools/expected_default.buildspec +++ b/tests/integration/cases/pypi_cachetools/expected_default.buildspec @@ -33,7 +33,11 @@ "setuptools.build_meta" ], "upstream_artifacts": { - "wheel": "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", - "sdist": "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz" + "wheels": [ + "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl" + ], + "sdist": [ + "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz" + ] } } diff --git a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec index 0aa97a3e9..9fbfdddd3 100644 --- a/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_cachetools/expected_dockerfile.buildspec @@ -68,20 +68,23 @@ RUN source /deps/bin/activate && python -m build --wheel -n # Validate script RUN cat <<'EOF' >/validate + [ -n "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl" ] || { echo "No upstream artifact to validate against."; exit 1; } # Capture artifacts generated - ARTIFACTS=(/src/dist/*) - # Ensure we only have one artefact - [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } - # BUILT_WHEEL is the artefact we built - BUILT_WHEEL=${ARTIFACTS[0]} + WHEELS=(/src/dist/*.whl) + # Ensure we only have one artifact + [ ${#WHEELS[@]} -eq 1 ] || { echo "Unexpected artifacts produced!"; exit 1; } + # BUILT_WHEEL is the artifact we built + BUILT_WHEEL=${WHEELS[0]} + # Ensure the artifact produced is not the literal returned by the glob + [ -e $BUILT_WHEEL ] || { echo "No wheels found!"; exit 1; } # Download the wheel wget -q https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl # Compare wheel names [ $(basename $BUILT_WHEEL) == "cachetools-6.2.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree - (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree - diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + (unzip -Z1 "cachetools-6.2.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artifact.tree + diff -u built.tree pypi_artifact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec b/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec index bb638f9af..de0634640 100644 --- a/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec +++ b/tests/integration/cases/pypi_markdown-it-py/expected_default.buildspec @@ -31,7 +31,11 @@ "flit_core.buildapi" ], "upstream_artifacts": { - "wheel": "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", - "sdist": "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz" + "wheels": [ + "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl" + ], + "sdist": [ + "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz" + ] } } diff --git a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec index c8eeb9e33..e6596fc1b 100644 --- a/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_markdown-it-py/expected_dockerfile.buildspec @@ -68,20 +68,23 @@ RUN source /deps/bin/activate && pip install flit && if test -f "flit.ini"; the # Validate script RUN cat <<'EOF' >/validate + [ -n "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl" ] || { echo "No upstream artifact to validate against."; exit 1; } # Capture artifacts generated - ARTIFACTS=(/src/dist/*) - # Ensure we only have one artefact - [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } - # BUILT_WHEEL is the artefact we built - BUILT_WHEEL=${ARTIFACTS[0]} + WHEELS=(/src/dist/*.whl) + # Ensure we only have one artifact + [ ${#WHEELS[@]} -eq 1 ] || { echo "Unexpected artifacts produced!"; exit 1; } + # BUILT_WHEEL is the artifact we built + BUILT_WHEEL=${WHEELS[0]} + # Ensure the artifact produced is not the literal returned by the glob + [ -e $BUILT_WHEEL ] || { echo "No wheels found!"; exit 1; } # Download the wheel wget -q https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl # Compare wheel names [ $(basename $BUILT_WHEEL) == "markdown_it_py-4.0.0-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree - (unzip -Z1 "markdown_it_py-4.0.0-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree - diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + (unzip -Z1 "markdown_it_py-4.0.0-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artifact.tree + diff -u built.tree pypi_artifact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_toga/expected_default.buildspec b/tests/integration/cases/pypi_toga/expected_default.buildspec index bab3f26e0..ac873e87f 100644 --- a/tests/integration/cases/pypi_toga/expected_default.buildspec +++ b/tests/integration/cases/pypi_toga/expected_default.buildspec @@ -35,7 +35,11 @@ "setuptools.build_meta" ], "upstream_artifacts": { - "wheel": "https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl", - "sdist": "https://files.pythonhosted.org/packages/17/e7/0924150329474d61e9f40f8bba1056d640cba22438e05355924019111b46/toga-0.5.1.tar.gz" + "wheels": [ + "https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl" + ], + "sdist": [ + "https://files.pythonhosted.org/packages/17/e7/0924150329474d61e9f40f8bba1056d640cba22438e05355924019111b46/toga-0.5.1.tar.gz" + ] } } diff --git a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec index 4aef15c8f..a8918d0ce 100644 --- a/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec +++ b/tests/integration/cases/pypi_toga/expected_dockerfile.buildspec @@ -68,20 +68,23 @@ RUN source /deps/bin/activate && python -m build --wheel -n # Validate script RUN cat <<'EOF' >/validate + [ -n "https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl" ] || { echo "No upstream artifact to validate against."; exit 1; } # Capture artifacts generated - ARTIFACTS=(/src/dist/*) - # Ensure we only have one artefact - [ ${#ARTIFACTS[@]} -eq 1 ] || { echo "Unexpected artifacts prodced!"; exit 1; } - # BUILT_WHEEL is the artefact we built - BUILT_WHEEL=${ARTIFACTS[0]} + WHEELS=(/src/dist/*.whl) + # Ensure we only have one artifact + [ ${#WHEELS[@]} -eq 1 ] || { echo "Unexpected artifacts produced!"; exit 1; } + # BUILT_WHEEL is the artifact we built + BUILT_WHEEL=${WHEELS[0]} + # Ensure the artifact produced is not the literal returned by the glob + [ -e $BUILT_WHEEL ] || { echo "No wheels found!"; exit 1; } # Download the wheel wget -q https://files.pythonhosted.org/packages/2b/1a/6a9c8230ad30e819f0965bbd596c736a03e16003d27b0363c632c84d4861/toga-0.5.1-py3-none-any.whl # Compare wheel names [ $(basename $BUILT_WHEEL) == "toga-0.5.1-py3-none-any.whl" ] || { echo "Wheel name does not match!"; exit 1; } # Compare file tree (unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree - (unzip -Z1 "toga-0.5.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artefact.tree - diff -u built.tree pypi_artefact.tree || { echo "File trees do not match!"; exit 1; } + (unzip -Z1 "toga-0.5.1-py3-none-any.whl" | grep -v '\.dist-info' | sort ) > pypi_artifact.tree + diff -u built.tree pypi_artifact.tree || { echo "File trees do not match!"; exit 1; } echo "Success!" EOF diff --git a/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec b/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec index 9363d1e9d..2173ac78b 100644 --- a/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec +++ b/tests/integration/cases/pypi_tree-sitter/expected_default.buildspec @@ -24,6 +24,6 @@ "setuptools.build_meta" ], "upstream_artifacts": { - "sdist": "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz" + "sdist": ["https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz"] } } diff --git a/tests/integration/cases/pypi_tree-sitter/test.yaml b/tests/integration/cases/pypi_tree-sitter/test.yaml index 13cf9d7d7..0b15a8bce 100644 --- a/tests/integration/cases/pypi_tree-sitter/test.yaml +++ b/tests/integration/cases/pypi_tree-sitter/test.yaml @@ -33,6 +33,6 @@ steps: options: command_args: - -purl - - pkg:pypi/markdown-it-py@0.25.2 + - pkg:pypi/tree-sitter@0.25.2 - --output-format - dockerfile