Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pypi-attestations for
openSUSE:Factory checked in at 2026-03-05 17:29:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pypi-attestations (Old)
and /work/SRC/openSUSE:Factory/.python-pypi-attestations.new.561 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pypi-attestations"
Thu Mar 5 17:29:56 2026 rev:2 rq:1336681 version:0.0.29
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-pypi-attestations/python-pypi-attestations.changes
2025-11-09 21:09:44.735771919 +0100
+++
/work/SRC/openSUSE:Factory/.python-pypi-attestations.new.561/python-pypi-attestations.changes
2026-03-05 17:32:09.440957920 +0100
@@ -1,0 +2,8 @@
+Thu Mar 5 08:21:22 UTC 2026 - Dirk Mรผller <[email protected]>
+
+- update to 0.0.29:
+ * Support for verifying Google Cloud attestations has been
+ added to the CLI.
+ * The minimum Python version required is now `3.10`
+
+-------------------------------------------------------------------
Old:
----
pypi_attestations-0.0.28.tar.gz
New:
----
pypi_attestations-0.0.29.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pypi-attestations.spec ++++++
--- /var/tmp/diff_new_pack.7ffdMS/_old 2026-03-05 17:32:13.805139431 +0100
+++ /var/tmp/diff_new_pack.7ffdMS/_new 2026-03-05 17:32:13.805139431 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-pypi-attestations
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%define upname pypi_attestations
Name: python-pypi-attestations
-Version: 0.0.28
+Version: 0.0.29
Release: 0
Summary: A library to convert between Sigstore Bundles and PEP-740
Attestation objects
License: Apache-2.0
++++++ pypi_attestations-0.0.28.tar.gz -> pypi_attestations-0.0.29.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/.github/dependabot.yml
new/pypi_attestations-0.0.29/.github/dependabot.yml
--- old/pypi_attestations-0.0.28/.github/dependabot.yml 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/.github/dependabot.yml 2025-12-11
14:22:58.000000000 +0100
@@ -2,6 +2,8 @@
updates:
- package-ecosystem: pip
+ cooldown:
+ default-days: 7
directory: /
groups:
python:
@@ -11,6 +13,8 @@
interval: daily
- package-ecosystem: github-actions
+ cooldown:
+ default-days: 7
directory: /
groups:
actions:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/.github/workflows/docs.yml
new/pypi_attestations-0.0.29/.github/workflows/docs.yml
--- old/pypi_attestations-0.0.28/.github/workflows/docs.yml 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/.github/workflows/docs.yml 2025-12-11
14:22:58.000000000 +0100
@@ -14,11 +14,11 @@
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #
v5.0.0
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
with:
persist-credentials: false
- - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #
v6.0.0
+ - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #
v6.1.0
with:
python-version-file: pyproject.toml
cache: "pip"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/.github/workflows/lint.yml
new/pypi_attestations-0.0.29/.github/workflows/lint.yml
--- old/pypi_attestations-0.0.28/.github/workflows/lint.yml 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/.github/workflows/lint.yml 2025-12-11
14:22:58.000000000 +0100
@@ -15,11 +15,11 @@
lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #
v5.0.0
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
with:
persist-credentials: false
- - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #
v6.0.0
+ - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #
v6.1.0
with:
python-version-file: pyproject.toml
cache: "pip"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/.github/workflows/release.yml
new/pypi_attestations-0.0.29/.github/workflows/release.yml
--- old/pypi_attestations-0.0.28/.github/workflows/release.yml 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/.github/workflows/release.yml 2025-12-11
14:22:58.000000000 +0100
@@ -15,11 +15,11 @@
name: Build distributions ๐ฆ
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #
v5.0.0
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
with:
persist-credentials: false
- - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #
v6.0.0
+ - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #
v6.1.0
with:
python-version-file: pyproject.toml
cache: "pip"
@@ -32,7 +32,7 @@
run: python -m build
- name: Upload distributions
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
# v4.6.2
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
# v5.0.0
with:
name: distributions
path: dist/
@@ -46,7 +46,7 @@
attestations: write # to persist the attestation files
steps:
- name: Download distributions
- uses:
actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses:
actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: distributions
path: dist/
@@ -67,7 +67,7 @@
steps:
- name: Download distributions
- uses:
actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses:
actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: distributions
path: dist/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/.github/workflows/tests.yml
new/pypi_attestations-0.0.29/.github/workflows/tests.yml
--- old/pypi_attestations-0.0.28/.github/workflows/tests.yml 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/.github/workflows/tests.yml 2025-12-11
14:22:58.000000000 +0100
@@ -18,18 +18,18 @@
strategy:
matrix:
python:
- - "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
+ - "3.14"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #
v5.0.0
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
with:
persist-credentials: false
- - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #
v6.0.0
+ - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #
v6.1.0
with:
python-version: ${{ matrix.python }}
cache: "pip"
@@ -46,13 +46,13 @@
test-offline:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #
v5.0.0
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
with:
persist-credentials: false
- - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #
v6.0.0
+ - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #
v6.1.0
with:
- python-version: 3.13
+ python-version: 3.14
cache: "pip"
cache-dependency-path: pyproject.toml
allow-prereleases: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/.github/workflows/zizmor.yml
new/pypi_attestations-0.0.29/.github/workflows/zizmor.yml
--- old/pypi_attestations-0.0.28/.github/workflows/zizmor.yml 1970-01-01
01:00:00.000000000 +0100
+++ new/pypi_attestations-0.0.29/.github/workflows/zizmor.yml 2025-12-11
14:22:58.000000000 +0100
@@ -0,0 +1,24 @@
+name: GitHub Actions Security Analysis with zizmor ๐
+
+on:
+ push:
+ branches: ["main"]
+ pull_request:
+ branches: ["**"]
+
+permissions: {}
+
+jobs:
+ zizmor:
+ name: Run zizmor ๐
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 #
v6.0.0
+ with:
+ persist-credentials: false
+
+ - name: Run zizmor ๐
+ uses:
zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/.gitignore
new/pypi_attestations-0.0.29/.gitignore
--- old/pypi_attestations-0.0.28/.gitignore 2025-10-16 18:52:55.000000000
+0200
+++ new/pypi_attestations-0.0.29/.gitignore 2025-12-11 14:22:58.000000000
+0100
@@ -1,4 +1,5 @@
env/
+.venv/
pip-wheel-metadata/
*.egg-info/
__pycache__/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/CHANGELOG.md
new/pypi_attestations-0.0.29/CHANGELOG.md
--- old/pypi_attestations-0.0.28/CHANGELOG.md 2025-10-16 18:52:55.000000000
+0200
+++ new/pypi_attestations-0.0.29/CHANGELOG.md 2025-12-11 14:22:58.000000000
+0100
@@ -7,6 +7,16 @@
## [Unreleased]
+## [0.0.29]
+
+### Added
+
+- Support for verifying Google Cloud attestations has been added to the CLI.
+
+### Changed
+
+- The minimum Python version required is now `3.10`
+
## [0.0.28]
### Changed
@@ -327,7 +337,8 @@
- Initial implementation
-[Unreleased]: https://github.com/pypi/pypi-attestations/compare/v0.0.28...HEAD
+[Unreleased]: https://github.com/pypi/pypi-attestations/compare/v0.0.29...HEAD
+[0.0.29]: https://github.com/pypi/pypi-attestations/compare/v0.0.28...v0.0.29
[0.0.28]: https://github.com/pypi/pypi-attestations/compare/v0.0.27...v0.0.28
[0.0.27]: https://github.com/pypi/pypi-attestations/compare/v0.0.26...v0.0.27
[0.0.26]: https://github.com/pypi/pypi-attestations/compare/v0.0.25...v0.0.26
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/Makefile
new/pypi_attestations-0.0.29/Makefile
--- old/pypi_attestations-0.0.28/Makefile 2025-10-16 18:52:55.000000000
+0200
+++ new/pypi_attestations-0.0.29/Makefile 2025-12-11 14:22:58.000000000
+0100
@@ -6,7 +6,7 @@
$(shell find test -name '*.py')
# Optionally overriden by the user, if they're using a virtual environment
manager.
-VENV ?= env
+VENV ?= .venv
# On Windows, venv scripts/shims are under `Scripts` instead of `bin`.
VENV_BIN := $(VENV)/bin
@@ -45,7 +45,7 @@
$(VENV)/pyvenv.cfg: pyproject.toml
# Create our Python 3 virtual environment
- python3 -m venv env
+ python3 -m venv $(VENV)
$(VENV_BIN)/python -m pip install --upgrade pip
$(VENV_BIN)/python -m pip install -e .[$(INSTALL_EXTRA)]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/PKG-INFO
new/pypi_attestations-0.0.29/PKG-INFO
--- old/pypi_attestations-0.0.28/PKG-INFO 2025-10-16 18:53:04.782124000
+0200
+++ new/pypi_attestations-0.0.29/PKG-INFO 2025-12-11 14:23:05.831881500
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: pypi-attestations
-Version: 0.0.28
+Version: 0.0.29
Summary: A library to convert between Sigstore Bundles and PEP-740 Attestation
objects
Author-email: Trail of Bits <[email protected]>
Maintainer-email: PyPI Admins <[email protected]>
@@ -10,7 +10,7 @@
Project-URL: Issues, https://github.com/pypi/pypi-attestations/issues
Project-URL: Source, https://github.com/pypi/pypi-attestations
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=3.9
+Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography
@@ -187,10 +187,6 @@
### Verifying a PyPI package
-> [!IMPORTANT]
-> This subcommand supports publish attestations from GitHub and GitLab.
-> It **does not currently support** Google Cloud-based publish attestations.
-
> [!NOTE]
> The package to verify can be passed either as a path to a local file, a
> `pypi:` prefixed filename (e.g: 'pypi:sampleproject-1.0.0-py3-none-any.whl'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/README.md
new/pypi_attestations-0.0.29/README.md
--- old/pypi_attestations-0.0.28/README.md 2025-10-16 18:52:55.000000000
+0200
+++ new/pypi_attestations-0.0.29/README.md 2025-12-11 14:22:58.000000000
+0100
@@ -144,10 +144,6 @@
### Verifying a PyPI package
-> [!IMPORTANT]
-> This subcommand supports publish attestations from GitHub and GitLab.
-> It **does not currently support** Google Cloud-based publish attestations.
-
> [!NOTE]
> The package to verify can be passed either as a path to a local file, a
> `pypi:` prefixed filename (e.g: 'pypi:sampleproject-1.0.0-py3-none-any.whl'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/pyproject.toml
new/pypi_attestations-0.0.29/pyproject.toml
--- old/pypi_attestations-0.0.28/pyproject.toml 2025-10-16 18:52:55.000000000
+0200
+++ new/pypi_attestations-0.0.29/pyproject.toml 2025-12-11 14:22:58.000000000
+0100
@@ -22,7 +22,7 @@
"sigstore >= 4.0, < 5.0",
"sigstore-models",
]
-requires-python = ">=3.9"
+requires-python = ">=3.10"
[tool.setuptools.dynamic]
version = { attr = "pypi_attestations.__version__" }
@@ -64,7 +64,7 @@
mypy_path = "src"
packages = "pypi_attestations"
plugins = ["pydantic.mypy"]
-python_version = "3.9"
+python_version = "3.10"
allow_redefinition = true
check_untyped_defs = true
disallow_incomplete_defs = true
@@ -83,7 +83,7 @@
[tool.ruff]
line-length = 100
-target-version = "py39"
+target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "ANN", "D", "COM", "ISC", "TCH", "SLF"]
@@ -91,9 +91,6 @@
# COM812 and ISC001 can cause conflicts when using ruff as a formatter.
# See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules.
ignore = ["D203", "D213", "COM812", "ISC001"]
-# Needed since Pydantic relies on runtime type annotations, and we target
Python versions
-# < 3.10. See
https://docs.astral.sh/ruff/rules/non-pep604-annotation/#why-is-this-bad
-pyupgrade.keep-runtime-typing = true
[tool.ruff.lint.per-file-ignores]
@@ -107,6 +104,6 @@
[tool.interrogate]
# don't enforce documentation coverage for packaging, testing, the virtual
# environment, or the CLI (which is documented separately).
-exclude = ["env", "test", "src/pypi_attestations/__main__.py"]
+exclude = [".venv", "test", "src/pypi_attestations/__main__.py"]
ignore-semiprivate = true
fail-under = 100
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/src/pypi_attestations/__init__.py
new/pypi_attestations-0.0.29/src/pypi_attestations/__init__.py
--- old/pypi_attestations-0.0.28/src/pypi_attestations/__init__.py
2025-10-16 18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/src/pypi_attestations/__init__.py
2025-12-11 14:22:58.000000000 +0100
@@ -1,6 +1,6 @@
"""The `pypi-attestations` APIs."""
-__version__ = "0.0.28"
+__version__ = "0.0.29"
from ._impl import (
Attestation,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/src/pypi_attestations/_cli.py
new/pypi_attestations-0.0.29/src/pypi_attestations/_cli.py
--- old/pypi_attestations-0.0.28/src/pypi_attestations/_cli.py 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/src/pypi_attestations/_cli.py 2025-12-11
14:22:58.000000000 +0100
@@ -156,11 +156,16 @@
verify_pypi_command.add_argument(
"--repository",
type=str,
- required=True,
help="URL of the publishing GitHub or GitLab repository",
)
verify_pypi_command.add_argument(
+ "--gcp-service-account",
+ type=str,
+ help="Email of the Google Cloud service account",
+ )
+
+ verify_pypi_command.add_argument(
"--staging",
action="store_true",
default=False,
@@ -576,9 +581,27 @@
try:
for attestation_bundle in provenance.attestation_bundles:
publisher = attestation_bundle.publisher
- if isinstance(publisher, GooglePublisher): # pragma: no cover
- _die("This CLI doesn't support Google Cloud-based publisher
verification")
-
_check_repository_identity(expected_repository_url=args.repository,
publisher=publisher)
+ if isinstance(publisher, GooglePublisher):
+ if not args.gcp_service_account:
+ _die(
+ "Provenance signed by a Google Cloud account, but no
service account "
+ "provided; use '--gcp-service-account'"
+ )
+ if publisher.email != args.gcp_service_account:
+ _die(
+ f"Verification failed: provenance was signed by
service account "
+ f'"{publisher.email}", expected
"{args.gcp_service_account}"'
+ )
+ else:
+ if not args.repository:
+ _die(
+ "Provenance signed by a repository, but no repository
URL provided; "
+ "use '--repository'"
+ )
+ _check_repository_identity(
+ expected_repository_url=args.repository,
publisher=publisher
+ )
+
policy = publisher._as_policy() # noqa: SLF001
for attestation in attestation_bundle.attestations:
attestation.verify(policy, dist, staging=args.staging,
offline=args.offline)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/src/pypi_attestations/_impl.py
new/pypi_attestations-0.0.29/src/pypi_attestations/_impl.py
--- old/pypi_attestations-0.0.28/src/pypi_attestations/_impl.py 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/src/pypi_attestations/_impl.py 2025-12-11
14:22:58.000000000 +0100
@@ -8,7 +8,7 @@
import base64
import json
from enum import Enum
-from typing import TYPE_CHECKING, Annotated, Any, Literal, NewType, Optional,
Union, get_args
+from typing import TYPE_CHECKING, Annotated, Any, Literal, NewType
import packaging
import packaging.tags
@@ -238,7 +238,7 @@
*,
staging: bool = False,
offline: bool = False,
- ) -> tuple[str, Optional[dict[str, Any]]]:
+ ) -> tuple[str, dict[str, Any] | None]:
"""Verify against an existing Python distribution.
The `identity` can be an object confirming to
@@ -257,8 +257,7 @@
# NOTE: Can't do `isinstance` with `Publisher` since it's
# a `_GenericAlias`; instead we punch through to the inner
# `_Publisher` union.
- # Use of typing.get_args is needed for Python < 3.10
- if isinstance(identity, get_args(_Publisher)):
+ if isinstance(identity, _Publisher):
policy = identity._as_policy() # noqa: SLF001
else:
policy = identity
@@ -412,7 +411,7 @@
packaging.utils.BuildTag,
frozenset[packaging.tags.Tag],
]
-_DistName = Union[_SdistName, _BdistName]
+_DistName = _SdistName | _BdistName
def _check_dist_filename(dist: str) -> _DistName:
@@ -544,7 +543,7 @@
action.
"""
- environment: Optional[str] = None
+ environment: str | None = None
"""
The optional name GitHub Actions environment that the publishing
action was performed from.
@@ -637,7 +636,7 @@
but can be customized.
"""
- environment: Optional[str] = None
+ environment: str | None = None
"""
The optional environment that the publishing action was performed from.
"""
@@ -661,7 +660,7 @@
return policy.Identity(identity=self.email,
issuer="https://accounts.google.com")
-_Publisher = Union[GitHubPublisher, GitLabPublisher, GooglePublisher]
+_Publisher = GitHubPublisher | GitLabPublisher | GooglePublisher
Publisher = Annotated[_Publisher, Field(discriminator="kind")]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/src/pypi_attestations.egg-info/PKG-INFO
new/pypi_attestations-0.0.29/src/pypi_attestations.egg-info/PKG-INFO
--- old/pypi_attestations-0.0.28/src/pypi_attestations.egg-info/PKG-INFO
2025-10-16 18:53:04.000000000 +0200
+++ new/pypi_attestations-0.0.29/src/pypi_attestations.egg-info/PKG-INFO
2025-12-11 14:23:05.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: pypi-attestations
-Version: 0.0.28
+Version: 0.0.29
Summary: A library to convert between Sigstore Bundles and PEP-740 Attestation
objects
Author-email: Trail of Bits <[email protected]>
Maintainer-email: PyPI Admins <[email protected]>
@@ -10,7 +10,7 @@
Project-URL: Issues, https://github.com/pypi/pypi-attestations/issues
Project-URL: Source, https://github.com/pypi/pypi-attestations
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=3.9
+Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography
@@ -187,10 +187,6 @@
### Verifying a PyPI package
-> [!IMPORTANT]
-> This subcommand supports publish attestations from GitHub and GitLab.
-> It **does not currently support** Google Cloud-based publish attestations.
-
> [!NOTE]
> The package to verify can be passed either as a path to a local file, a
> `pypi:` prefixed filename (e.g: 'pypi:sampleproject-1.0.0-py3-none-any.whl'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/src/pypi_attestations.egg-info/SOURCES.txt
new/pypi_attestations-0.0.29/src/pypi_attestations.egg-info/SOURCES.txt
--- old/pypi_attestations-0.0.28/src/pypi_attestations.egg-info/SOURCES.txt
2025-10-16 18:53:04.000000000 +0200
+++ new/pypi_attestations-0.0.29/src/pypi_attestations.egg-info/SOURCES.txt
2025-12-11 14:23:05.000000000 +0100
@@ -11,6 +11,7 @@
.github/workflows/lint.yml
.github/workflows/release.yml
.github/workflows/tests.yml
+.github/workflows/zizmor.yml
src/pypi_attestations/__init__.py
src/pypi_attestations/__main__.py
src/pypi_attestations/_cli.py
@@ -28,6 +29,8 @@
test/test_impl.py
test/test_init.py
test/assets/200170367.pem
+test/assets/gcb_attestation_test-0.0.0.tar.gz
+test/assets/gcb_attestation_test-0.0.0.tar.gz.provenance
test/assets/gitlab_oidc_project-0.0.3.tar.gz
test/assets/gitlab_oidc_project-0.0.3.tar.gz.publish.attestation
test/assets/no-source-repository-ref-extension.pem
Binary files
old/pypi_attestations-0.0.28/test/assets/gcb_attestation_test-0.0.0.tar.gz and
new/pypi_attestations-0.0.29/test/assets/gcb_attestation_test-0.0.0.tar.gz
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pypi_attestations-0.0.28/test/assets/gcb_attestation_test-0.0.0.tar.gz.provenance
new/pypi_attestations-0.0.29/test/assets/gcb_attestation_test-0.0.0.tar.gz.provenance
---
old/pypi_attestations-0.0.28/test/assets/gcb_attestation_test-0.0.0.tar.gz.provenance
1970-01-01 01:00:00.000000000 +0100
+++
new/pypi_attestations-0.0.29/test/assets/gcb_attestation_test-0.0.0.tar.gz.provenance
2025-12-11 14:22:58.000000000 +0100
@@ -0,0 +1 @@
+{"attestation_bundles":[{"attestations":[{"envelope":{"signature":"MEUCIQDvmv1O9ioY84yUE79+ckD+AF3uHi+twKtJRfUlyYgCyQIgRDcFfWof5Gf4lvJzkTKGtNaJ9NlCHF+BA3yUwRKNaTw=","statement":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2NiX2F0dGVzdGF0aW9uX3Rlc3QtMC4wLjAudGFyLmd6IiwiZGlnZXN0Ijp7InNoYTI1NiI6IjQ2MTMxNzM2MjQxOTEyNGI2MDEyZTg1NTQyM2E5MDc4ZDZkZThhZWQzZTc0ZmE3OGNjNzRkNjY5YjIzZGM2Y2YifX1dLCJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9kb2NzLnB5cGkub3JnL2F0dGVzdGF0aW9ucy9wdWJsaXNoL3YxIiwicHJlZGljYXRlIjpudWxsfQ=="},"verification_material":{"certificate":"MIIC7DCCAnKgAwIBAgIUVJkn21utBSU3vjewVuPQb3e8Jz0wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNDIxMTUwMjI3WhcNMjUwNDIxMTUxMjI3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE16HRcqztt38BoUOwhhagqdU43mBPeR9sctF0jTQ00NUpjWqvPc8CMmKR85kpwFxS2WfPe7D0wIByY8ZfdgT/66OCAZEwggGNMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUi5A/s39XjLixRjkQs8mHtSEpTFMwHwYDVR0j
BBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wQAYDVR0RAQH/BDYwNIEyOTE5NDM2MTU4MjM2LWNvbXB1dGVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20wKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGWWN88EgAABAMASDBGAiEA9EUW3yTYEtEeZ0SMaYlHPZ2+LHrae1hb+9bCRmdMjgwCIQDSMxXrTejGcgOZqJT8jxCZT77yieMU16PO92ZrpQ5wrjAKBggqhkjOPQQDAwNoADBlAjEAxl/X0fmqgftikX/Lq+c++syGCCNf1zHB35VYPSqN+vZvLEzbASrJjx6fFMID8pF4AjBXeTTem553VCEM3Y9bMuM9eSen6by5XyGTWL0j7ro/YjmSC+xs9IHoSHQ6vYRQH00=","transparency_entries":[{"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMGMxNzdhODg2MDZmNWE0Yjg1ZmZjZjE3YjAwOTI3NzI1MjNhOGZkZjBjZjdmNTFlY2UxN2VhMTk1ZjU2NzQxOCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6Ijc2ODhiNDgzNGYwM2U4YTRlNmNhZWVmOTc2NWQyYzQzNzk5ZGNhN2U1ODNjZTJhODc1MDkwNDBjODgzZjhlYmUifSwic
2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRRHZtdjFPOWlvWTg0eVVFNzkrY2tEK0FGM3VIaSt0d0t0SlJmVWx5WWdDeVFJZ1JEY0ZmV29mNUdmNGx2SnprVEtHdE5hSjlObENIRitCQTN5VXdSS05hVHc9IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNM1JFTkRRVzVMWjBGM1NVSkJaMGxWVmtwcmJqSXhkWFJDVTFVemRtcGxkMVoxVUZGaU0yVTRTbm93ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwVmQwNUVTWGhOVkZWM1RXcEpNMWRvWTA1TmFsVjNUa1JKZUUxVVZYaE5ha2t6VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVXhOa2hTWTNGNmRIUXpPRUp2VlU5M2FHaGhaM0ZrVlRRemJVSlFaVkk1YzJOMFJqQUthbFJSTURCT1ZYQnFWM0YyVUdNNFEwMXRTMUk0Tld0d2QwWjRVekpYWmxCbE4wUXdkMGxDZVZrNFdtWmtaMVF2TmpaUFEwRmFSWGRuWjBkT1RVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVnBOVUV2Q25Nek9WaHFUR2w0VW1wclVYTTRiVWgwVTBWd1ZFWk5kMGgzV1VSV1VqQnFRa0puZDBadlFWVX
pPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMUZCV1VSV1VqQlNRVkZJTDBKRVdYZE9TVVY1VDFSRk5VNUVUVEpOVkZVMFRXcE5Na3hYVG5aaVdFSXhaRWRXUVZwSFZqSmFWM2gyWTBkV2VRcE1iV1I2V2xoS01tRlhUbXhaVjA1cVlqTldkV1JETldwaU1qQjNTMUZaUzB0M1dVSkNRVWRFZG5wQlFrRlJVV0poU0ZJd1kwaE5Oa3g1T1doWk1rNTJDbVJYTlRCamVUVnVZakk1Ym1KSFZYVlpNamwwVFVOelIwTnBjMGRCVVZGQ1p6YzRkMEZSWjBWSVVYZGlZVWhTTUdOSVRUWk1lVGxvV1RKT2RtUlhOVEFLWTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFKUjB4Q1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtnd1JXVjNRalZCU0dOQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWXdwdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhWMWRPT0RoRlowRkJRa0ZOUVZORVFrZEJhVVZCT1VWVlZ6TjVWRmxGZEVWbENsb3dVMDFoV1d4SVVGb3lLMHhJY21GbE1XaGlLemxpUTFKdFpFMXFaM2REU1ZGRVUwMTRXSEpVWldwSFkyZFBXbkZLVkRocWVFTmFWRGMzZVdsbFRWVUtNVFpRVHpreVduSndVVFYzY21wQlMwSm5aM0ZvYTJwUFVGRlJSRUYzVG05QlJFSnNRV3BGUVhoc0wxZ3dabTF4WjJaMGFXdFlMMHh4SzJNckszTjVSd3BEUTA1bU1YcElRak0xVmxsUVUzRk9LM1phZGt4RmVtSkJVM0pLYW5nMlprWk5TVVE0Y0VZMFFXcENXR1ZVVkdWdE5UVXpWa05GVFROWk9XSk5kVTA1Q21WVFpXNDJZbmsxV0hsSFZGZE1NR28
zY204dldXcHRVME1yZUhNNVNVaHZVMGhSTm5aWlVsRklNREE5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=","inclusionPromise":{"signedEntryTimestamp":"MEUCIQD7SQTmBd5ANk107TOcsarnbywnOM8GvPiFjsnd3XE8iQIgGSUUbpi8Qi4Oh/+hmOT9JQhsWpLFJd7l6DKwZeSWwMs="},"inclusionProof":{"checkpoint":{"envelope":"rekor.sigstore.dev
-
1193050959916656506\n78266107\nyTVQR2Socsw5nyWnvnYLqwOxBQRXiOZORK1cgJ6y7qI=\n\nโ
rekor.sigstore.dev
wNI9ajBFAiEA7otHsrHxWEz4Cl6rzVxxwPutBhHfcvOuRrUTM5srjUYCIF10wsHxGBfjflyR5QTTYl6Wcj9GpAMsN2uHkANKgRGu\n"},"hashes":["KVLYepdrDI5/LfHdWT1V7PN/MotHRCtfRperGwlO9JA=","lMH6ki0y7U5Of2brFmlIYF03HuN88eFy6T8eWlyB/dk=","4O+gSq+awPls7N18oHMxdt/NTTOAOViQy6fQWvYR4FU=","UdNOa+z8HXYyMRlPTrMP6f0u2SuYAycU9osH2XmfOFE=","fKLixv2KO9tAJNy/Y7Jx9jp4xsOmz/iWHfGo6h/tQfY=","iiMUkc1juY2OYURyjaGDN4yeGwK+vlahZgBqGwaoxxk=","Qs31aP8k4DnnQFAbKPqTVyMQnPITYnouZgayf6eVuH0=","Gj0wra8n0qTMwIaUpL2oKbJ5hRKqfEBMeGWQ0znz+Uc=","12yDkzWUtOuQpFBmsOEepvEQN6k8pfUARwzfDsqE5o4=","H6YN+nyPx48sKudbYXqp6lrbloDk93pvBQXfe01rp
aQ=","CkAnGwVEwNrYv/Y6Lbm9Q/XPKBSzJh4VT7/YI1PU48Y=","nDWqvIPJey+hFoi5jU9xvVb382cPBtUxKz54B39oXfE=","3NymYFL9lsZznt4xbSrh8IjEcFrJYRYBn1Elo+ASpeA=","2c0Z0VGteoqr0adlQJB2QTdZT3Cn912kMtCAGoE/xW0=","PHJDSL8Ui2OWQsJZ4vZa/V48UosV5lnRgMOoVTBsbDw=","gGNvqHSiyarbPiEG0lmBLLIhU2F6djF/wmlcFeaQdP8=","7v8qPHNDLerpduaMx06eb/MwgoQwczTn/cYGKX/9wZ4="],"logIndex":"78266105","rootHash":"yTVQR2Socsw5nyWnvnYLqwOxBQRXiOZORK1cgJ6y7qI=","treeSize":"78266107"},"integratedTime":"1745247747","kindVersion":{"kind":"dsse","version":"0.0.1"},"logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"logIndex":"200170367"}]},"version":1}],"publisher":{"email":"[email protected]","kind":"Google"}}],"version":1}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/test/test_cli.py
new/pypi_attestations-0.0.29/test/test_cli.py
--- old/pypi_attestations-0.0.28/test/test_cli.py 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/test/test_cli.py 2025-12-11
14:22:58.000000000 +0100
@@ -50,6 +50,12 @@
converted_sigstore_bundle_path = _ASSETS /
"pypi_attestation_models-0.0.4a2.tar.gz.attestation"
+# gcb assets
+gcb_artifact_path = _ASSETS / "gcb_attestation_test-0.0.0.tar.gz"
+gcb_provenance_path = _ASSETS / "gcb_attestation_test-0.0.0.tar.gz.provenance"
+gcb_service_account = "[email protected]"
+
+
def run_main_with_command(cmd: list[str]) -> None:
"""Helper method to run the main function with a given command."""
sys.argv[1:] = cmd
@@ -939,3 +945,70 @@
)
assert "Output file already exists" in caplog.text
+
+
+def test_verify_pypi_command_gcb_ok(caplog: pytest.LogCaptureFixture) -> None:
+ run_main_with_command(
+ [
+ "verify",
+ "pypi",
+ "--offline",
+ "--gcp-service-account",
+ gcb_service_account,
+ "--provenance-file",
+ gcb_provenance_path.as_posix(),
+ gcb_artifact_path.as_posix(),
+ ]
+ )
+ assert "OK: gcb_attestation_test-0.0.0.tar.gz" in caplog.text
+
+
+def test_verify_pypi_command_gcb_no_service_account(caplog:
pytest.LogCaptureFixture) -> None:
+ with pytest.raises(SystemExit):
+ run_main_with_command(
+ [
+ "verify",
+ "pypi",
+ "--offline",
+ "--provenance-file",
+ gcb_provenance_path.as_posix(),
+ gcb_artifact_path.as_posix(),
+ ]
+ )
+ assert (
+ "Provenance signed by a Google Cloud account, but no service account
provided"
+ in caplog.text
+ )
+
+
+def test_verify_pypi_command_gcb_wrong_service_account(caplog:
pytest.LogCaptureFixture) -> None:
+ with pytest.raises(SystemExit):
+ run_main_with_command(
+ [
+ "verify",
+ "pypi",
+ "--offline",
+ "--gcp-service-account",
+ "[email protected]",
+ "--provenance-file",
+ gcb_provenance_path.as_posix(),
+ gcb_artifact_path.as_posix(),
+ ]
+ )
+ assert "Verification failed: provenance was signed by service account" in
caplog.text
+
+
+def test_verify_pypi_command_github_no_repository(caplog:
pytest.LogCaptureFixture) -> None:
+ # Use a github-signed attestation to check the other path
+ with pytest.raises(SystemExit):
+ run_main_with_command(
+ [
+ "verify",
+ "pypi",
+ "--offline",
+ "--provenance-file",
+ pypi_sdist_provenance_path.as_posix(),
+ pypi_sdist_path.as_posix(),
+ ]
+ )
+ assert "Provenance signed by a repository, but no repository URL provided"
in caplog.text
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pypi_attestations-0.0.28/test/test_impl.py
new/pypi_attestations-0.0.29/test/test_impl.py
--- old/pypi_attestations-0.0.28/test/test_impl.py 2025-10-16
18:52:55.000000000 +0200
+++ new/pypi_attestations-0.0.29/test/test_impl.py 2025-12-11
14:22:58.000000000 +0100
@@ -46,6 +46,10 @@
gl_signed_dist = impl.Distribution.from_file(gl_signed_dist_path)
gl_attestation_path = _ASSETS /
"gitlab_oidc_project-0.0.3.tar.gz.publish.attestation"
+gcb_signed_dist_path = _ASSETS / "gcb_attestation_test-0.0.0.tar.gz"
+gcb_signed_dist = impl.Distribution.from_file(gcb_signed_dist_path)
+gcb_attestation_path = _ASSETS / "gcb_attestation_test-0.0.0.tar.gz.provenance"
+
class TestDistribution:
def test_from_file_nonexistent(self, tmp_path: Path) -> None:
@@ -199,6 +203,17 @@
with pytest.raises(impl.VerificationError, match=r"Build Config URI .+
does not match"):
attestation.verify(publisher, gl_signed_dist, offline=True)
+ def test_verify_from_google_publisher(self) -> None:
+ publisher = impl.GooglePublisher(
+ email="[email protected]",
+ )
+
+ provenance =
impl.Provenance.model_validate_json(gcb_attestation_path.read_bytes())
+ attestation = provenance.attestation_bundles[0].attestations[0]
+ predicate_type, predicate = attestation.verify(publisher,
gcb_signed_dist, offline=True)
+ assert predicate_type ==
"https://docs.pypi.org/attestations/publish/v1"
+ assert predicate is None
+
def test_verify(self) -> None:
# Our checked-in asset has this identity.
pol = policy.Identity(