diff --git a/CHANGES/+pulp-attestation-sync.bugfix b/CHANGES/+pulp-attestation-sync.bugfix new file mode 100644 index 000000000..b61e1661c --- /dev/null +++ b/CHANGES/+pulp-attestation-sync.bugfix @@ -0,0 +1 @@ +Fixed provenance sync failing when syncing Pulp-created attestations that use a `Pulp User` publisher. diff --git a/pulp_python/app/tasks/sync.py b/pulp_python/app/tasks/sync.py index ee957da98..a4dc874e9 100644 --- a/pulp_python/app/tasks/sync.py +++ b/pulp_python/app/tasks/sync.py @@ -10,7 +10,6 @@ from bandersnatch.mirror import Mirror from lxml.etree import LxmlError from packaging.requirements import Requirement -from pypi_attestations import Provenance from pypi_simple import IndexPage from rest_framework import serializers @@ -28,6 +27,7 @@ PythonPackageContent, PythonRemote, ) +from pulp_python.app.provenance import Provenance from pulp_python.app.utils import PYPI_LAST_SERIAL, aget_remote_simple_page, parse_metadata logger = logging.getLogger(__name__) diff --git a/pulp_python/tests/functional/api/test_attestations.py b/pulp_python/tests/functional/api/test_attestations.py index 0737256b7..6a8ac9d95 100644 --- a/pulp_python/tests/functional/api/test_attestations.py +++ b/pulp_python/tests/functional/api/test_attestations.py @@ -154,6 +154,54 @@ def test_attestation_sync_upload(python_bindings, twine_package, download_python assert att_bundle["publisher"]["kind"] == "Pulp User" +@pytest.mark.parallel +def test_sync_pulp_created_provenance( + domain_factory, + python_bindings, + twine_package, + python_repo_factory, + python_distribution_factory, + python_remote_factory, + python_repo_with_sync, + python_content_summary, + monitor_task, +): + """Test syncing provenance across domains from a Pulp upload.""" + source_domain = domain_factory() + attestations = get_attestations(twine_package.provenance_url) + source_repo = python_repo_factory(pulp_domain=source_domain.name) + body = { + "relative_path": twine_package.filename, + "file_url": twine_package.url, + "attestations": json.dumps(attestations), + "repository": source_repo.pulp_href, + } + task = python_bindings.ContentPackagesApi.create(pulp_domain=source_domain.name, **body).task + monitor_task(task) + + source_distro = python_distribution_factory( + repository=source_repo, pulp_domain=source_domain.name + ) + dest_domain = domain_factory() + remote = python_remote_factory( + url=source_distro.base_url, + provenance=True, + includes=[f"twine=={twine_package.version}"], + pulp_domain=dest_domain.name, + ) + dest_repo = python_repo_with_sync(remote=remote, pulp_domain=dest_domain.name) + + summary = python_content_summary(repository_version=dest_repo.latest_version_href) + assert summary.present["python.provenance"]["count"] == 1 + + provs = python_bindings.ContentProvenanceApi.list( + repository_version=dest_repo.latest_version_href, + pulp_domain=dest_domain.name, + ) + assert provs.count == 1 + assert provs.results[0].provenance["attestation_bundles"][0]["publisher"]["kind"] == "Pulp User" + + def test_attestation_twine_upload( pulpcore_bindings, python_content_summary, diff --git a/pulp_python/tests/functional/api/test_pypi_apis.py b/pulp_python/tests/functional/api/test_pypi_apis.py index 48a3179d2..37c94c402 100644 --- a/pulp_python/tests/functional/api/test_pypi_apis.py +++ b/pulp_python/tests/functional/api/test_pypi_apis.py @@ -245,8 +245,9 @@ def test_simple_redirect_with_publications( assert response.url == str(urljoin(pulp_content_url, f"{distro.base_path}/simple/")) -@pytest.mark.parallel -def test_pypi_json(python_remote_factory, python_repo_with_sync, python_distribution_factory): +def test_pypi_json( + delete_orphans_pre, python_remote_factory, python_repo_with_sync, python_distribution_factory +): """Checks the data of `pypi/{package_name}/json` endpoint.""" remote = python_remote_factory(policy="immediate") repo = python_repo_with_sync(remote) @@ -260,8 +261,8 @@ def test_pypi_json(python_remote_factory, python_repo_with_sync, python_distribu assert_pypi_json(response.json()) -@pytest.mark.parallel def test_pypi_json_content_app( + delete_orphans_pre, python_remote_factory, python_repo_with_sync, python_publication_factory,