From e2e3db650978a5b3f3df2f580e56994b88f24466 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 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, From 48aa27696ed1a4b4ed65b1833cdd524a541fa8c1 Mon Sep 17 00:00:00 2001 From: Gerrod Ubben Date: Tue, 14 Jul 2026 18:24:43 -0400 Subject: [PATCH 2/2] Isolate shelf-reader test pollution on 3.27 branch. Run the brittle pypi JSON assertions outside the parallel suite with delete_orphans_pre so orphaned shelf-reader content cannot cause flaky failures. Assisted-by: Cursor Co-authored-by: Cursor --- pulp_python/tests/functional/api/test_pypi_apis.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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,