From 0b328ef092cd410bc344cdb86b6b268bc17b08e3 Mon Sep 17 00:00:00 2001 From: Gerrod Date: Tue, 14 Jul 2026 15:55:08 -0400 Subject: [PATCH 1/2] Fix provenance sync for Pulp User publishers (#1273) * Fix provenance sync to use Pulp's extended Provenance model Sync was validating provenance with pypi_attestations.Provenance, which rejects Pulp User publishers created during upload. Use the extended model from pulp_python.app.provenance instead. (cherry picked from commit 4acf7775bac47df59cf0a64e64912e5799e2c7a0) --- CHANGES/+pulp-attestation-sync.bugfix | 1 + pulp_python/app/tasks/sync.py | 2 +- .../tests/functional/api/test_attestations.py | 48 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 CHANGES/+pulp-attestation-sync.bugfix 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 e1fa7b224..25a4955d5 100644 --- a/pulp_python/app/tasks/sync.py +++ b/pulp_python/app/tasks/sync.py @@ -9,7 +9,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 pulpcore.plugin.download import HttpDownloader @@ -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, From af2d3c69e1dfe35d88dee5375ccf60646479ac77 Mon Sep 17 00:00:00 2001 From: Gerrod Ubben Date: Tue, 14 Jul 2026 18:12:49 -0400 Subject: [PATCH 2/2] Run package substitution tests outside the parallel suite Tests that upload a different artifact under the shelf-reader tar.gz filename leave incorrect global content that can break parallel PyPI JSON checks. Legit shelf-reader uploads and syncs remain parallel. Assisted-by: Cursor Co-authored-by: Cursor --- pulp_python/tests/functional/api/test_crud_content_unit.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pulp_python/tests/functional/api/test_crud_content_unit.py b/pulp_python/tests/functional/api/test_crud_content_unit.py index 5bf799563..658cc4332 100644 --- a/pulp_python/tests/functional/api/test_crud_content_unit.py +++ b/pulp_python/tests/functional/api/test_crud_content_unit.py @@ -203,7 +203,6 @@ def test_package_creation_with_metadata( ) -@pytest.mark.parallel def test_disallow_package_substitution( monitor_task, python_bindings, @@ -272,7 +271,6 @@ def test_disallow_package_substitution( assert repo.latest_version_href.endswith("/2/") -@pytest.mark.parallel def test_package_substitution_allowed_by_default( monitor_task, python_bindings,