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