This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 6526bc7 Add a page listing manually recorded distributions
6526bc7 is described below
commit 6526bc76086900524a02a5a403b2f79f54517588
Author: Sean B. Palmer <[email protected]>
AuthorDate: Thu Aug 7 17:07:49 2025 +0100
Add a page listing manually recorded distributions
---
atr/blueprints/api/api.py | 1 +
atr/db/__init__.py | 21 ++++++++++++
atr/models/sql.py | 13 +++----
atr/routes/distribution.py | 28 ++++++++++++++-
atr/storage/writers/distributions.py | 2 +-
migrations/versions/0020_2025.08.07_23999f25.py | 45 +++++++++++++++++++++++++
6 files changed, 102 insertions(+), 8 deletions(-)
diff --git a/atr/blueprints/api/api.py b/atr/blueprints/api/api.py
index a252770..b8c2564 100644
--- a/atr/blueprints/api/api.py
+++ b/atr/blueprints/api/api.py
@@ -311,6 +311,7 @@ async def ignore_delete(data: models.api.IgnoreDeleteArgs)
-> DictResponse:
).model_dump(), 200
+# TODO: Rename to ignores
@api.BLUEPRINT.route("/ignore/list/<committee_name>")
@quart_schema.validate_response(models.api.IgnoreListResults, 200)
async def ignore_list(committee_name: str) -> DictResponse:
diff --git a/atr/db/__init__.py b/atr/db/__init__.py
index 530a7ce..65dc3af 100644
--- a/atr/db/__init__.py
+++ b/atr/db/__init__.py
@@ -283,6 +283,27 @@ class Session(sqlalchemy.ext.asyncio.AsyncSession):
return Query(self, query)
+ def distribution(
+ self,
+ release_name: Opt[str] = NOT_SET,
+ platform: Opt[sql.DistributionPlatform] = NOT_SET,
+ owner_namespace: Opt[str] = NOT_SET,
+ package: Opt[str] = NOT_SET,
+ version: Opt[str] = NOT_SET,
+ ) -> Query[sql.Distribution]:
+ query = sqlmodel.select(sql.Distribution)
+ if is_defined(release_name):
+ query = query.where(sql.Distribution.release_name == release_name)
+ if is_defined(platform):
+ query = query.where(sql.Distribution.platform == platform)
+ if is_defined(owner_namespace):
+ query = query.where(sql.Distribution.owner_namespace ==
owner_namespace)
+ if is_defined(package):
+ query = query.where(sql.Distribution.package == package)
+ if is_defined(version):
+ query = query.where(sql.Distribution.version == version)
+ return Query(self, query)
+
async def execute_query(self, query: sqlalchemy.sql.expression.Executable)
-> sqlalchemy.engine.Result:
if (self.log_queries or global_log_query) and isinstance(query,
sqlalchemy.sql.expression.Select):
try:
diff --git a/atr/models/sql.py b/atr/models/sql.py
index ed1750a..291ee67 100644
--- a/atr/models/sql.py
+++ b/atr/models/sql.py
@@ -819,13 +819,14 @@ class CheckResultIgnore(sqlmodel.SQLModel, table=True):
# Distribution: Release
class Distribution(sqlmodel.SQLModel, table=True):
- id: int = sqlmodel.Field(default=None, primary_key=True)
- release_name: str = sqlmodel.Field(foreign_key="release.name",
ondelete="CASCADE")
+ release_name: str = sqlmodel.Field(primary_key=True, index=True,
foreign_key="release.name", ondelete="CASCADE")
release: Release = sqlmodel.Relationship(back_populates="distributions")
- platform: DistributionPlatform =
sqlmodel.Field(default=DistributionPlatform.ARTIFACTHUB)
- owner_namespace: str | None = sqlmodel.Field(default=None)
- package: str
- version: str
+ platform: DistributionPlatform = sqlmodel.Field(
+ primary_key=True, index=True, default=DistributionPlatform.ARTIFACTHUB
+ )
+ owner_namespace: str = sqlmodel.Field(primary_key=True, index=True,
default="")
+ package: str = sqlmodel.Field(primary_key=True, index=True)
+ version: str = sqlmodel.Field(primary_key=True, index=True)
staging: bool = sqlmodel.Field(default=False)
upload_date: datetime.datetime | None = sqlmodel.Field(default=None)
api_url: str
diff --git a/atr/routes/distribution.py b/atr/routes/distribution.py
index aa7e92d..da19529 100644
--- a/atr/routes/distribution.py
+++ b/atr/routes/distribution.py
@@ -133,6 +133,32 @@ class DistributeData(schema.Lax):
return None if v is None or (isinstance(v, str) and v.strip() == "")
else v
[email protected]("/distributions/list/<project>/<version>", methods=["GET"])
+async def list_get(session: routes.CommitterSession, project: str, version:
str) -> str:
+ async with db.session() as data:
+ distributions = await data.distribution(
+ release_name=sql.release_name(project, version),
+ ).all()
+
+ block = htm.Block()
+ block.h1["Distribution list"]
+ for distribution in distributions:
+ block.h2[f"{distribution.platform.name} {distribution.package}
{distribution.version}"]
+ tbody = htpy.tbody[
+ _tr("Release name", distribution.release_name),
+ _tr("Platform", distribution.platform.value.name),
+ _tr("Owner or Namespace", distribution.owner_namespace or "-"),
+ _tr("Package", distribution.package),
+ _tr("Version", distribution.version),
+ _tr("Staging", "Yes" if distribution.staging else "No"),
+ _tr("Upload date", str(distribution.upload_date)),
+ _tr("API URL", distribution.api_url),
+ ]
+ block.table(".table.table-striped.table-bordered")[tbody]
+ title = f"Distribution list for {project} {version}"
+ return await template.blank(title, content=block.collect())
+
+
@routes.committer("/distribution/record/<project>/<version>", methods=["GET"])
async def record(session: routes.CommitterSession, project: str, version: str)
-> str:
form = await DistributeForm.create_form(data={"package": project,
"version": version})
@@ -259,7 +285,7 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /) -> str:
_tr("Owner or Namespace", distribution.owner_namespace or "-"),
_tr("Package", distribution.package),
_tr("Version", distribution.version),
- _tr("Staging", "No" if distribution.staging else "Yes"),
+ _tr("Staging", "Yes" if distribution.staging else "No"),
_tr("Upload date", str(distribution.upload_date)),
_tr("API URL", distribution.api_url),
]
diff --git a/atr/storage/writers/distributions.py
b/atr/storage/writers/distributions.py
index 8786f6f..de1a301 100644
--- a/atr/storage/writers/distributions.py
+++ b/atr/storage/writers/distributions.py
@@ -101,7 +101,7 @@ class CommitteeMember(CommitteeParticipant):
distribution = sql.Distribution(
platform=platform,
release_name=release_name,
- owner_namespace=owner_namespace,
+ owner_namespace=owner_namespace or "",
package=package,
version=version,
staging=staging,
diff --git a/migrations/versions/0020_2025.08.07_23999f25.py
b/migrations/versions/0020_2025.08.07_23999f25.py
new file mode 100644
index 0000000..9767358
--- /dev/null
+++ b/migrations/versions/0020_2025.08.07_23999f25.py
@@ -0,0 +1,45 @@
+"""Add a compound primary key to Distribution
+
+Revision ID: 0020_2025.08.07_23999f25
+Revises: 0019_2025.08.07_279ca4a9
+Create Date: 2025-08-07 15:53:27.260830+00:00
+"""
+
+from collections.abc import Sequence
+
+import sqlalchemy as sa
+from alembic import op
+
+# Revision identifiers, used by Alembic
+revision: str = "0020_2025.08.07_23999f25"
+down_revision: str | None = "0019_2025.08.07_279ca4a9"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+ op.execute("UPDATE distribution SET owner_namespace = '' WHERE
owner_namespace IS NULL")
+ with op.batch_alter_table("distribution", schema=None) as batch_op:
+ batch_op.drop_column("id")
+ batch_op.alter_column("owner_namespace", existing_type=sa.VARCHAR(),
nullable=False)
+ batch_op.create_primary_key(
+ "pk_distribution", ["release_name", "platform", "owner_namespace",
"package", "version"]
+ )
+ batch_op.create_index(batch_op.f("ix_distribution_owner_namespace"),
["owner_namespace"], unique=False)
+ batch_op.create_index(batch_op.f("ix_distribution_package"),
["package"], unique=False)
+ batch_op.create_index(batch_op.f("ix_distribution_platform"),
["platform"], unique=False)
+ batch_op.create_index(batch_op.f("ix_distribution_release_name"),
["release_name"], unique=False)
+ batch_op.create_index(batch_op.f("ix_distribution_version"),
["version"], unique=False)
+
+
+def downgrade() -> None:
+ with op.batch_alter_table("distribution", schema=None) as batch_op:
+ batch_op.drop_index(batch_op.f("ix_distribution_version"))
+ batch_op.drop_index(batch_op.f("ix_distribution_release_name"))
+ batch_op.drop_index(batch_op.f("ix_distribution_platform"))
+ batch_op.drop_index(batch_op.f("ix_distribution_package"))
+ batch_op.drop_index(batch_op.f("ix_distribution_owner_namespace"))
+ batch_op.drop_constraint("pk_distribution", type_="primary")
+ batch_op.alter_column("owner_namespace", existing_type=sa.VARCHAR(),
nullable=True)
+ batch_op.add_column(sa.Column("id", sa.INTEGER(), nullable=False))
+ batch_op.create_primary_key("pk_distribution_id", ["id"])
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]