Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use trusted publishing to adapt UI #14657

Merged
merged 3 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions tests/unit/packaging/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
)
from ...common.db.packaging import (
DependencyFactory as DBDependencyFactory,
FileEventFactory as DBFileEventFactory,
FileFactory as DBFileFactory,
ProjectFactory as DBProjectFactory,
ReleaseFactory as DBReleaseFactory,
Expand Down Expand Up @@ -515,6 +516,60 @@ def test_github_open_issue_info_url(self, db_session, home_page, expected):
release = DBReleaseFactory.create(home_page=home_page)
assert release.github_open_issue_info_url == expected

def test_trusted_published_none(self, db_session):
release = DBReleaseFactory.create()

assert not release.trusted_published

def test_trusted_published_all(self, db_session):
release = DBReleaseFactory.create()
release_file = DBFileFactory.create(
release=release,
filename=f"{release.project.name}-{release.version}.tar.gz",
python_version="source",
)
DBFileEventFactory.create(
source=release_file,
tag="fake:event",
additional={},
)

# Without the `publisher_url` key, not considered trusted published
assert not release.trusted_published

DBFileEventFactory.create(
source=release_file,
tag="fake:event",
additional={"publisher_url": "https://fake/url"},
)

assert release.trusted_published

def test_trusted_published_mixed(self, db_session):
release = DBReleaseFactory.create()
rfile_1 = DBFileFactory.create(
release=release,
filename=f"{release.project.name}-{release.version}.tar.gz",
python_version="source",
)
rfile_2 = DBFileFactory.create(
release=release,
filename=f"{release.project.name}-{release.version}.whl",
python_version="bdist_wheel",
)
DBFileEventFactory.create(
source=rfile_1,
tag="fake:event",
additional={},
)
DBFileEventFactory.create(
source=rfile_2,
tag="fake:event",
additional={"publisher_url": "https://fake/url"},
)

assert not release.trusted_published


class TestFile:
def test_requires_python(self, db_session):
Expand Down Expand Up @@ -579,3 +634,28 @@ def test_query_paths(self, db_session):
)

assert results == (expected, expected + ".metadata")

def test_published_via_trusted_publisher(self, db_session):
project = DBProjectFactory.create()
release = DBReleaseFactory.create(project=project)
rfile = DBFileFactory.create(
release=release,
filename=f"{project.name}-{release.version}.tar.gz",
python_version="source",
)
DBFileEventFactory.create(
source=rfile,
tag="fake:event",
additional={},
)

# Without the `publisher_url` key, not considered trusted published
assert not rfile.uploaded_via_trusted_publisher

DBFileEventFactory.create(
source=rfile,
tag="fake:event",
additional={"publisher_url": "https://fake/url"},
)

assert rfile.uploaded_via_trusted_publisher
20 changes: 20 additions & 0 deletions warehouse/admin/templates/admin/projects/release_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ <h3>Details</h3>
<td>Created via</td>
<td><code>{{ release.uploaded_via }}</code></td>
</tr>
<tr>
<td>Trusted Published?</td>
<td>
{% if release.trusted_published %}
<i class="fa fa-fw fa-check text-green"></i>
{% else %}
<i class="fa fa-fw fa-times text-red"></i>
<span>
<i class="fa fa-info-circle" data-bs-toggle="tooltip" title="All Files of a Release must be TP for this to be True"></i>
</span>
{% endif %}
</tr>
<tr>
<td>Yanked</td>
<td>{{ release.yanked }}</td>
Expand All @@ -70,6 +82,7 @@ <h3>Files</h3>
<th>Package Type</th>
<th>Python Version</th>
<th>Uploaded via</th>
<th>Trusted Publisher?</th>
</tr>
</thead>
<tbody>
Expand All @@ -79,6 +92,13 @@ <h3>Files</h3>
<td>{{ file.packagetype }}</td>
<td>{{ file.python_version }}</td>
<td><code>{{ file.uploaded_via }}</code></td>
<td>
{% if file.uploaded_via_trusted_publisher %}
<i class="fa fa-fw fa-check text-green"></i>
{% else %}
<i class="fa fa-fw fa-times text-red"></i>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
Expand Down
21 changes: 21 additions & 0 deletions warehouse/packaging/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,17 @@ def has_meta(self):
]
)

@property
def trusted_published(self) -> bool:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought: If this is too cumbersome a query pattern, we could explore creating a database trigger to update the Release's value on upload.

"""
A Release can be considered published via a trusted publisher if
**all** the Files in the release are published via a trusted publisher.
"""
files = self.files.all() # type: ignore[attr-defined]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since self.files returns an AppenderQuery, it returns True until we resolve the query by fetching the rows. If there's a better way to do this, I'm all ears!

if not files:
return False
return all(file.uploaded_via_trusted_publisher for file in files)


class PackageType(str, enum.Enum):
bdist_dmg = "bdist_dmg"
Expand Down Expand Up @@ -670,6 +681,16 @@ def __table_args__(cls): # noqa
comment="If True, the object has been archived to our archival bucket.",
)

@property
def uploaded_via_trusted_publisher(self) -> bool:
"""Return True if the file was uploaded via a trusted publisher."""
return (
self.events.where(
self.Event.additional.has_key("publisher_url") # type: ignore[attr-defined] # noqa E501
).count()
> 0
)

@hybrid_property
def metadata_path(self):
return self.path + ".metadata"
Expand Down