You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by sb...@apache.org on 2025/09/08 17:39:09 UTC
(tooling-releases-client) branch main updated: Add a command to record distributions, and an associated test
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-releases-client.git
The following commit(s) were added to refs/heads/main by this push:
new 4c0ba69 Add a command to record distributions, and an associated test
4c0ba69 is described below
commit 4c0ba698ebf4ec05bcec543e2cae99e8d0692e6a
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Mon Sep 8 18:38:47 2025 +0100
Add a command to record distributions, and an associated test
---
pyproject.toml | 4 ++--
src/atrclient/client.py | 46 ++++++++++++++++++++++++++++++++++++++++-
src/atrclient/models/api.py | 50 +++++++++++++++++++++++++++++++++++++++++++++
tests/cli_workflow.t | 3 +++
uv.lock | 10 ++++-----
5 files changed, 105 insertions(+), 8 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 6053cd3..7d24dd9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,7 +11,7 @@ build-backend = "hatchling.build"
[project]
name = "apache-trusted-releases"
-version = "0.20250908.1538"
+version = "0.20250908.1738"
description = "ATR CLI and Python API"
readme = "README.md"
requires-python = ">=3.13"
@@ -79,4 +79,4 @@ filterwarnings = [
]
[tool.uv]
-exclude-newer = "2025-09-08T15:38:00Z"
+exclude-newer = "2025-09-08T17:38:00Z"
diff --git a/src/atrclient/client.py b/src/atrclient/client.py
index e3af369..ea419eb 100755
--- a/src/atrclient/client.py
+++ b/src/atrclient/client.py
@@ -57,6 +57,7 @@ APP: cyclopts.App = cyclopts.App()
APP_CHECK: cyclopts.App = cyclopts.App(name="check", help="Check result operations.")
APP_CONFIG: cyclopts.App = cyclopts.App(name="config", help="Configuration operations.")
APP_DEV: cyclopts.App = cyclopts.App(name="dev", help="Developer operations.")
+APP_DISTRIBUTION: cyclopts.App = cyclopts.App(name="distribution", help="Distribution operations.")
APP_DRAFT: cyclopts.App = cyclopts.App(name="draft", help="Draft operations.")
APP_IGNORE: cyclopts.App = cyclopts.App(name="ignore", help="Ignore operations.")
APP_JWT: cyclopts.App = cyclopts.App(name="jwt", help="JWT operations.")
@@ -171,6 +172,14 @@ def api_checks_ongoing(
return models.api.validate_checks_ongoing(response)
+@api_post("/distribution/record")
+def api_distribution_record(
+ api: ApiPost, args: models.api.DistributionRecordArgs
+) -> models.api.DistributionRecordResults:
+ response = api.post(args)
+ return models.api.validate_distribution_record(response)
+
+
@api_post("/ignore/add")
def api_ignore_add(api: ApiPost, args: models.api.IgnoreAddArgs) -> models.api.IgnoreAddResults:
response = api.post(args)
@@ -636,6 +645,40 @@ def app_draft_delete(project: str, version: str, /) -> None:
print(draft_delete.success)
+@APP_DISTRIBUTION.command(name="record", help="Record a distribution.")
+def app_distribution_record(
+ project: str,
+ version: str,
+ platform: str,
+ distribution_owner_namespace: str | None,
+ distribution_package: str,
+ distribution_version: str,
+ staging: bool,
+ details: bool,
+) -> None:
+ # if not distribution_owner_namespace:
+ # distribution_owner_namespace = None
+ if platform not in models.sql.DistributionPlatform.__members__:
+ show_error_and_exit(f"Invalid platform: {platform}")
+ platform_member = models.sql.DistributionPlatform[platform]
+ distribution_record_args = models.api.DistributionRecordArgs(
+ project=project,
+ version=version,
+ platform=platform_member,
+ distribution_owner_namespace=distribution_owner_namespace,
+ distribution_package=distribution_package,
+ distribution_version=distribution_version,
+ staging=staging,
+ details=details,
+ )
+ distribution_record = api_distribution_record(distribution_record_args)
+ if not distribution_record.success:
+ show_error_and_exit("Failed to record distribution.")
+ if not distribution_record.success:
+ show_error_and_exit("Failed to record distribution.")
+ print("Distribution recorded.")
+
+
@APP.command(name="docs", help="Show comprehensive CLI documentation in Markdown.")
def app_docs() -> None:
old_help_format = APP.help_format
@@ -1456,6 +1499,7 @@ def subcommands_register(app: cyclopts.App) -> None:
app.command(APP_CHECK)
app.command(APP_CONFIG)
app.command(APP_DEV)
+ app.command(APP_DISTRIBUTION)
app.command(APP_DRAFT)
app.command(APP_IGNORE)
app.command(APP_JWT)
@@ -1535,7 +1579,7 @@ async def web_get_url(url: str, verify_ssl: bool = True) -> bytes:
async def web_post(url: str, args: models.schema.Strict, jwt_token: str | None, verify_ssl: bool = True) -> JSON:
- return await web_post_json(url, args.model_dump(), jwt_token, verify_ssl)
+ return await web_post_json(url, args.model_dump(mode="json"), jwt_token, verify_ssl)
async def web_post_json(url: str, args: JSON, jwt_token: str | None, verify_ssl: bool = True) -> JSON:
diff --git a/src/atrclient/models/api.py b/src/atrclient/models/api.py
index eba2c52..adf370b 100644
--- a/src/atrclient/models/api.py
+++ b/src/atrclient/models/api.py
@@ -81,6 +81,20 @@ class DistributionRecordArgs(schema.Strict):
staging: bool = schema.Field(..., **example(False))
details: bool = schema.Field(..., **example(False))
+ @pydantic.field_validator("platform", mode="before")
+ @classmethod
+ def platform_to_enum(cls, v):
+ if isinstance(v, str):
+ try:
+ return sql.DistributionPlatform.__members__[v]
+ except KeyError:
+ raise ValueError(f"'{v}' is not a valid DistributionPlatform")
+ return v
+
+ @pydantic.field_serializer("platform")
+ def serialise_platform(self, v):
+ return v.name if isinstance(v, sql.DistributionPlatform) else v
+
class DistributionRecordResults(schema.Strict):
endpoint: Literal["/distribution/record"] = schema.Field(alias="endpoint")
@@ -213,6 +227,38 @@ class ProjectsListResults(schema.Strict):
projects: Sequence[sql.Project]
+class PublisherDistributionRecordArgs(schema.Strict):
+ publisher: str = schema.Field(..., **example("user"))
+ jwt: str = schema.Field(..., **example("eyJhbGciOiJIUzI1[...]mMjLiuyu5CSpyHI="))
+ project: str = schema.Field(..., **example("example"))
+ version: str = schema.Field(..., **example("0.0.1"))
+ platform: sql.DistributionPlatform = schema.Field(..., **example(sql.DistributionPlatform.ARTIFACT_HUB))
+ distribution_owner_namespace: str | None = schema.Field(default=None, **example("example"))
+ distribution_package: str = schema.Field(..., **example("example"))
+ distribution_version: str = schema.Field(..., **example("0.0.1"))
+ staging: bool = schema.Field(..., **example(False))
+ details: bool = schema.Field(..., **example(False))
+
+ @pydantic.field_validator("platform", mode="before")
+ @classmethod
+ def platform_to_enum(cls, v):
+ if isinstance(v, str):
+ try:
+ return sql.DistributionPlatform.__members__[v]
+ except KeyError:
+ raise ValueError(f"'{v}' is not a valid DistributionPlatform")
+ return v
+
+ @pydantic.field_serializer("platform")
+ def serialise_platform(self, v):
+ return v.name if isinstance(v, sql.DistributionPlatform) else v
+
+
+class PublisherDistributionRecordResults(schema.Strict):
+ endpoint: Literal["/publisher/distribution/record"] = schema.Field(alias="endpoint")
+ success: Literal[True] = schema.Field(..., **example(True))
+
+
class PublisherReleaseAnnounceArgs(schema.Strict):
publisher: str = schema.Field(..., **example("user"))
jwt: str = schema.Field(..., **example("eyJhbGciOiJIUzI1[...]mMjLiuyu5CSpyHI="))
@@ -482,6 +528,7 @@ Results = Annotated[
| CommitteeKeysResults
| CommitteeProjectsResults
| CommitteesListResults
+ | DistributionRecordResults
| IgnoreAddResults
| IgnoreDeleteResults
| IgnoreListResults
@@ -494,6 +541,7 @@ Results = Annotated[
| ProjectGetResults
| ProjectReleasesResults
| ProjectsListResults
+ | PublisherDistributionRecordResults
| PublisherReleaseAnnounceResults
| PublisherSshRegisterResults
| PublisherVoteResolveResults
@@ -537,6 +585,7 @@ validate_committee_get = validator(CommitteeGetResults)
validate_committee_keys = validator(CommitteeKeysResults)
validate_committee_projects = validator(CommitteeProjectsResults)
validate_committees_list = validator(CommitteesListResults)
+validate_distribution_record = validator(DistributionRecordResults)
validate_ignore_add = validator(IgnoreAddResults)
validate_ignore_delete = validator(IgnoreDeleteResults)
validate_ignore_list = validator(IgnoreListResults)
@@ -549,6 +598,7 @@ validate_keys_user = validator(KeysUserResults)
validate_project_get = validator(ProjectGetResults)
validate_project_releases = validator(ProjectReleasesResults)
validate_projects_list = validator(ProjectsListResults)
+validate_publisher_distribution_record = validator(PublisherDistributionRecordResults)
validate_publisher_release_announce = validator(PublisherReleaseAnnounceResults)
validate_publisher_ssh_register = validator(PublisherSshRegisterResults)
validate_publisher_vote_resolve = validator(PublisherVoteResolveResults)
diff --git a/tests/cli_workflow.t b/tests/cli_workflow.t
index ce762d4..c6c4195 100644
--- a/tests/cli_workflow.t
+++ b/tests/cli_workflow.t
@@ -55,6 +55,9 @@ $ atr vote start tooling-test-example 0.3+cli 00002 -m "<!user!>@apache.org"
$ atr vote resolve tooling-test-example 0.3+cli passed
Vote marked as passed.
+$ atr distribution record tooling-test-example 0.3+cli NPM None react 18.2.0 False False
+Distribution recorded.
+
$ atr announce tooling-test-example 0.3+cli 00003 -m "<!user!>@apache.org" -s "[ANNOUNCE] Release tooling-test-example 0.3+cli" -b "Release tooling-test-example 0.3+cli has been announced."
Announcement sent.
diff --git a/uv.lock b/uv.lock
index 84348b3..bbb3011 100644
--- a/uv.lock
+++ b/uv.lock
@@ -3,7 +3,7 @@ revision = 3
requires-python = ">=3.13"
[options]
-exclude-newer = "2025-09-08T15:38:00Z"
+exclude-newer = "2025-09-08T17:38:00Z"
[[package]]
name = "aiohappyeyeballs"
@@ -84,7 +84,7 @@ wheels = [
[[package]]
name = "apache-trusted-releases"
-version = "0.20250908.1538"
+version = "0.20250908.1738"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
@@ -219,7 +219,7 @@ wheels = [
[[package]]
name = "cyclopts"
-version = "3.23.1"
+version = "3.24.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
@@ -227,9 +227,9 @@ dependencies = [
{ name = "rich" },
{ name = "rich-rst" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/ea/7a/28b63c43d4c17d6587abcfef648841d39543158bcc47b5d40a03b8831f7a/cyclopts-3.23.1.tar.gz", hash = "sha256:ca6a5e9b326caf156d79f3932e2f88b95629e59fd371c0b3a89732b7619edacb", size = 75161, upload-time = "2025-08-30T17:40:34.396Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/30/ca/7782da3b03242d5f0a16c20371dff99d4bd1fedafe26bc48ff82e42be8c9/cyclopts-3.24.0.tar.gz", hash = "sha256:de6964a041dfb3c57bf043b41e68c43548227a17de1bad246e3a0bfc5c4b7417", size = 76131, upload-time = "2025-09-08T15:40:57.75Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/63/67/ac57fbef5414ce84fe0bdeb497918ab2c781ff2cbf23c1bd91334b225669/cyclopts-3.23.1-py3-none-any.whl", hash = "sha256:8e57c6ea47d72b4b565c6a6c8a9fd56ed048ab4316627991230f4ad24ce2bc29", size = 85222, upload-time = "2025-08-30T17:40:33.005Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/8b/2c95f0645c6f40211896375e6fa51f504b8ccb29c21f6ae661fe87ab044e/cyclopts-3.24.0-py3-none-any.whl", hash = "sha256:809d04cde9108617106091140c3964ee6fceb33cecdd537f7ffa360bde13ed71", size = 86154, upload-time = "2025-09-08T15:40:56.41Z" },
]
[[package]]
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org