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 05a6128  Use more consistent API names
05a6128 is described below

commit 05a61284681c69ffd88ae62ab30bb070e0d3adcf
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Jul 29 14:58:17 2025 +0100

    Use more consistent API names
---
 pyproject.toml              |   9 +-
 src/atrclient/client.py     | 166 ++++++++++++------------
 src/atrclient/models/api.py | 308 ++++++++++++++++++++------------------------
 tests/cli_ssh.t             |   2 +-
 tests/test_all.py           |  17 ++-
 uv.lock                     |  15 ++-
 6 files changed, 253 insertions(+), 264 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index b240c74..6831840 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,7 +11,7 @@ build-backend = "hatchling.build"
 
 [project]
 name            = "apache-trusted-releases"
-version         = "0.20250729.1318"
+version         = "0.20250729.1358"
 description     = "ATR CLI and Python API"
 readme          = "README.md"
 requires-python = ">=3.13"
@@ -73,5 +73,10 @@ select = [
   "W"
 ]
 
+[tool.pytest.ini_options]
+filterwarnings = [
+    "ignore:imghdr was removed in Python 
3.13:DeprecationWarning:pgpy.constants",
+]
+
 [tool.uv]
-exclude-newer = "2025-07-29T13:18:00Z"
+exclude-newer = "2025-07-29T13:58:00Z"
diff --git a/src/atrclient/client.py b/src/atrclient/client.py
index 55cb8dd..eb30934 100755
--- a/src/atrclient/client.py
+++ b/src/atrclient/client.py
@@ -170,28 +170,22 @@ def api_checks_ongoing(
     return models.api.validate_checks_ongoing(response)
 
 
-@api_post("/draft/delete")
-def api_draft_delete(api: ApiPost, args: models.api.DraftDeleteArgs) -> 
models.api.DraftDeleteResults:
+@api_post("/key/add")
+def api_key_add(api: ApiPost, args: models.api.KeyAddArgs) -> 
models.api.KeyAddResults:
     response = api.post(args)
-    return models.api.validate_draft_delete(response)
+    return models.api.validate_key_add(response)
 
 
-@api_post("/keys/add")
-def api_keys_add(api: ApiPost, args: models.api.KeysAddArgs) -> 
models.api.KeysAddResults:
+@api_post("/key/delete")
+def api_key_delete(api: ApiPost, args: models.api.KeyDeleteArgs) -> 
models.api.KeyDeleteResults:
     response = api.post(args)
-    return models.api.validate_keys_add(response)
+    return models.api.validate_key_delete(response)
 
 
-@api_post("/keys/delete")
-def api_keys_delete(api: ApiPost, args: models.api.KeysDeleteArgs) -> 
models.api.KeysDeleteResults:
-    response = api.post(args)
-    return models.api.validate_keys_delete(response)
-
-
-@api_get("/keys/get")
-def api_keys_get(api: ApiGet, fingerprint: str) -> models.api.KeysGetResults:
+@api_get("/key/get")
+def api_key_get(api: ApiGet, fingerprint: str) -> models.api.KeyGetResults:
     response = api.get(fingerprint)
-    return models.api.validate_keys_get(response)
+    return models.api.validate_key_get(response)
 
 
 @api_post("/keys/upload")
@@ -206,78 +200,88 @@ def api_keys_user(api: ApiGet, asf_uid: str) -> 
models.api.KeysUserResults:
     return models.api.validate_keys_user(response)
 
 
+@api_get("/project/releases")
+def api_project_releases(api: ApiGet, project: str) -> 
models.api.ProjectReleasesResults:
+    response = api.get(project)
+    return models.api.validate_project_releases(response)
+
+
 @api_post("/release/announce")
 def api_release_announce(api: ApiPost, args: models.api.ReleaseAnnounceArgs) 
-> models.api.ReleaseAnnounceResults:
     response = api.post(args)
     return models.api.validate_release_announce(response)
 
 
-@api_post("/releases/create")
-def api_releases_create(api: ApiPost, args: models.api.ReleasesCreateArgs) -> 
models.api.ReleasesCreateResults:
+@api_post("/release/create")
+def api_release_create(api: ApiPost, args: models.api.ReleaseCreateArgs) -> 
models.api.ReleaseCreateResults:
     response = api.post(args)
-    return models.api.validate_releases_create(response)
+    return models.api.validate_release_create(response)
 
 
-@api_post("/releases/delete")
-def api_releases_delete(api: ApiPost, args: models.api.ReleasesDeleteArgs) -> 
models.api.ReleasesDeleteResults:
+@api_post("/release/delete")
+def api_release_delete(api: ApiPost, args: models.api.ReleaseDeleteArgs) -> 
models.api.ReleaseDeleteResults:
     response = api.post(args)
-    return models.api.validate_releases_delete(response)
+    return models.api.validate_release_delete(response)
 
 
-@api_get("/releases/paths")
-def api_releases_paths(
-    api: ApiGet, project: str, version: str, revision: str | None = None
-) -> models.api.ReleasesPathsResults:
-    response = api.get(project, version, revision=revision)
-    return models.api.validate_releases_paths(response)
+@api_post("/release/draft/delete")
+def api_release_draft_delete(
+    api: ApiPost, args: models.api.ReleaseDraftDeleteArgs
+) -> models.api.ReleaseDraftDeleteResults:
+    response = api.post(args)
+    return models.api.validate_release_draft_delete(response)
 
 
-@api_get("/releases/project")
-def api_releases_project(api: ApiGet, project: str) -> 
models.api.ReleasesProjectResults:
-    response = api.get(project)
-    return models.api.validate_releases_project(response)
+@api_get("/release/paths")
+def api_release_paths(
+    api: ApiGet, project: str, version: str, revision: str | None = None
+) -> models.api.ReleasePathsResults:
+    response = api.get(project, version, revision=revision)
+    return models.api.validate_release_paths(response)
 
 
-@api_get("/releases/version")
-def api_releases_version(api: ApiGet, project: str, version: str) -> 
models.api.ReleasesVersionResults:
+@api_get("/release/get")
+def api_release_get(api: ApiGet, project: str, version: str) -> 
models.api.ReleaseGetResults:
     response = api.get(project, version)
-    return models.api.validate_releases_version(response)
+    return models.api.validate_release_get(response)
 
 
-@api_get("/revisions")
-def api_revisions(api: ApiGet, project: str, version: str) -> 
models.api.RevisionsResults:
+@api_get("/release/revisions")
+def api_release_revisions(api: ApiGet, project: str, version: str) -> 
models.api.ReleaseRevisionsResults:
     response = api.get(project, version)
-    return models.api.validate_revisions(response)
+    return models.api.validate_release_revisions(response)
 
 
-@api_post("/ssh/add")
-def api_ssh_add(api: ApiPost, args: models.api.SshAddArgs) -> 
models.api.SshAddResults:
+@api_post("/release/upload")
+def api_release_upload(api: ApiPost, args: models.api.ReleaseUploadArgs) -> 
models.api.ReleaseUploadResults:
     response = api.post(args)
-    return models.api.validate_ssh_add(response)
+    return models.api.validate_release_upload(response)
 
 
-@api_post("/ssh/delete")
-def api_ssh_delete(api: ApiPost, args: models.api.SshDeleteArgs) -> 
models.api.SshDeleteResults:
+@api_post("/signature/provenance")
+def api_signature_provenance(
+    api: ApiPost, args: models.api.SignatureProvenanceArgs
+) -> models.api.SignatureProvenanceResults:
     response = api.post(args)
-    return models.api.validate_ssh_delete(response)
+    return models.api.validate_signature_provenance(response)
 
 
-@api_get("/ssh/list")
-def api_ssh_list(api: ApiGet, asf_uid: str) -> models.api.SshListResults:
-    response = api.get(asf_uid)
-    return models.api.validate_ssh_list(response)
+@api_post("/ssh-key/add")
+def api_ssh_key_add(api: ApiPost, args: models.api.SshKeyAddArgs) -> 
models.api.SshKeyAddResults:
+    response = api.post(args)
+    return models.api.validate_ssh_key_add(response)
 
 
-@api_post("/upload")
-def api_upload(api: ApiPost, args: models.api.UploadArgs) -> 
models.api.UploadResults:
+@api_post("/ssh-key/delete")
+def api_ssh_key_delete(api: ApiPost, args: models.api.SshKeyDeleteArgs) -> 
models.api.SshKeyDeleteResults:
     response = api.post(args)
-    return models.api.validate_upload(response)
+    return models.api.validate_ssh_key_delete(response)
 
 
-@api_post("/verify/provenance")
-def api_verify_provenance(api: ApiPost, args: models.api.VerifyProvenanceArgs) 
-> models.api.VerifyProvenanceResults:
-    response = api.post(args)
-    return models.api.validate_verify_provenance(response)
+@api_get("/ssh-keys/list")
+def api_ssh_keys_list(api: ApiGet, asf_uid: str) -> 
models.api.SshKeysListResults:
+    response = api.get(asf_uid)
+    return models.api.validate_ssh_keys_list(response)
 
 
 @api_post("/vote/resolve")
@@ -376,7 +380,7 @@ def app_checks_status(
     revision: str | None = None,
     verbose: Annotated[bool, cyclopts.Parameter(alias="-v", name="--verbose")] 
= False,
 ) -> None:
-    releases_version = api_releases_version(project, version)
+    releases_version = api_release_get(project, version)
     release = releases_version.release
     # TODO: Handle the not found case better
     if release.phase != "release_candidate_draft":
@@ -452,8 +456,8 @@ def app_config_path() -> None:
 
 @APP_DEV.command(name="delete", help="Delete a release.")
 def app_dev_delete(project: str, version: str, /) -> None:
-    releases_delete_args = models.api.ReleasesDeleteArgs(project=project, 
version=version)
-    release_delete = api_releases_delete(releases_delete_args)
+    releases_delete_args = models.api.ReleaseDeleteArgs(project=project, 
version=version)
+    release_delete = api_release_delete(releases_delete_args)
     print(release_delete.deleted)
 
 
@@ -608,8 +612,8 @@ def app_dev_user() -> None:
 
 @APP_DRAFT.command(name="delete", help="Delete a draft release.")
 def app_draft_delete(project: str, version: str, /) -> None:
-    draft_delete_args = models.api.DraftDeleteArgs(project=project, 
version=version)
-    draft_delete = api_draft_delete(draft_delete_args)
+    draft_delete_args = models.api.ReleaseDraftDeleteArgs(project=project, 
version=version)
+    draft_delete = api_release_draft_delete(draft_delete_args)
     print(draft_delete.success)
 
 
@@ -690,21 +694,21 @@ def app_keys_add(path: str, committees: str = "", /) -> 
None:
         asf_uid = config_get(config, ["asf", "uid"])
     if asf_uid is None:
         show_error_and_exit("Please configure asf.uid before adding a key.")
-    keys_add_args = models.api.KeysAddArgs(asfuid=asf_uid, key=key, 
committees=selected_committee_names)
-    keys_add = api_keys_add(keys_add_args)
+    keys_add_args = models.api.KeyAddArgs(asfuid=asf_uid, key=key, 
committees=selected_committee_names)
+    keys_add = api_key_add(keys_add_args)
     print(keys_add.fingerprint)
 
 
 @APP_KEYS.command(name="delete", help="Delete an OpenPGP key.")
 def app_keys_delete(fingerprint: str, /) -> None:
-    keys_delete_args = models.api.KeysDeleteArgs(fingerprint=fingerprint)
-    keys_delete = api_keys_delete(keys_delete_args)
+    keys_delete_args = models.api.KeyDeleteArgs(fingerprint=fingerprint)
+    keys_delete = api_key_delete(keys_delete_args)
     print(keys_delete.success)
 
 
 @APP_KEYS.command(name="get", help="Get an OpenPGP key.")
 def app_keys_get(fingerprint: str, /) -> None:
-    keys_get = api_keys_get(fingerprint)
+    keys_get = api_key_get(fingerprint)
     print(keys_get.key.model_dump_json(indent=None))
 
 
@@ -736,34 +740,34 @@ def app_keys_user(asf_uid: str | None = None) -> None:
 
 @APP.command(name="list", help="List all files within a release.")
 def app_list(project: str, version: str, revision: str | None = None, /) -> 
None:
-    releases_paths = api_releases_paths(project, version, revision)
+    releases_paths = api_release_paths(project, version, revision)
     for rel_path in releases_paths.rel_paths:
         print(rel_path)
 
 
 @APP_RELEASE.command(name="info", help="Show information about a release.")
 def app_release_info(project: str, version: str, /) -> None:
-    releases_version = api_releases_version(project, version)
+    releases_version = api_release_get(project, version)
     print(releases_version.release.model_dump_json(indent=None))
 
 
 @APP_RELEASE.command(name="list", help="List releases for a project.")
 def app_release_list(project: str, /) -> None:
     # TODO: Support showing all of a user's releases if no project is provided
-    releases_project = api_releases_project(project)
-    releases_display(releases_project.data)
+    releases_project = api_project_releases(project)
+    releases_display(releases_project.releases)
 
 
 @APP_RELEASE.command(name="start", help="Start a release.")
 def app_release_start(project: str, version: str, /) -> None:
-    releases_create_args = models.api.ReleasesCreateArgs(project=project, 
version=version)
-    releases_create = api_releases_create(releases_create_args)
+    releases_create_args = models.api.ReleaseCreateArgs(project=project, 
version=version)
+    releases_create = api_release_create(releases_create_args)
     print(releases_create.release.model_dump_json(indent=None))
 
 
 @APP.command(name="revisions", help="List all revisions for a release.")
 def app_revisions(project: str, version: str, /) -> None:
-    revisions = api_revisions(project, version)
+    revisions = api_release_revisions(project, version)
     for revision in revisions.revisions:
         print(revision)
 
@@ -825,15 +829,15 @@ def app_show(path: str, /) -> None:
 
 @APP_SSH.command(name="add", help="Add an SSH key.")
 def app_ssh_add(text: str, /) -> None:
-    ssh_add_args = models.api.SshAddArgs(text=text)
-    ssh_add = api_ssh_add(ssh_add_args)
+    ssh_add_args = models.api.SshKeyAddArgs(text=text)
+    ssh_add = api_ssh_key_add(ssh_add_args)
     print(ssh_add.fingerprint)
 
 
 @APP_SSH.command(name="delete", help="Delete an SSH key.")
 def app_ssh_delete(fingerprint: str, /) -> None:
-    ssh_delete_args = models.api.SshDeleteArgs(fingerprint=fingerprint)
-    ssh_delete = api_ssh_delete(ssh_delete_args)
+    ssh_delete_args = models.api.SshKeyDeleteArgs(fingerprint=fingerprint)
+    ssh_delete = api_ssh_key_delete(ssh_delete_args)
     print(ssh_delete.success)
 
 
@@ -844,7 +848,7 @@ def app_ssh_list(asf_uid: str | None = None) -> None:
             asf_uid = config_get(config, ["asf", "uid"])
     if asf_uid is None:
         show_error_and_exit("No ASF UID provided and asf.uid not configured.")
-    ssh_list = api_ssh_list(asf_uid)
+    ssh_list = api_ssh_keys_list(asf_uid)
     print(ssh_list.data)
 
 
@@ -853,14 +857,14 @@ def app_upload(project: str, version: str, path: str, 
filepath: str, /) -> None:
     with open(filepath, "rb") as fh:
         content = fh.read()
 
-    upload_args = models.api.UploadArgs(
+    upload_args = models.api.ReleaseUploadArgs(
         project=project,
         version=version,
         relpath=path,
         content=base64.b64encode(content).decode("utf-8"),
     )
 
-    upload = api_upload(upload_args)
+    upload = api_release_upload(upload_args)
     print(upload.revision.model_dump_json(indent=None))
 
 
@@ -904,7 +908,7 @@ def app_verify(url: str, /, verbose: bool = False) -> None:
     signature_file_name = signature_url.split("/")[-1]
 
     print_if_verbose("To verify the signature, we need the OpenPGP signing key 
from the ATR.\n")
-    verify_provenance_args = models.api.VerifyProvenanceArgs(
+    verify_provenance_args = models.api.SignatureProvenanceArgs(
         artifact_file_name=artifact_file_name,
         artifact_sha3_256=artifact_hash,
         signature_file_name=signature_file_name,
@@ -916,7 +920,7 @@ def app_verify(url: str, /, verbose: bool = False) -> None:
     dumped_json["signature_asc_text"] = dumped_json["signature_asc_text"][:32] 
+ "..."
     print_if_verbose(json.dumps(dumped_json, indent=2))
     print_if_verbose("")
-    verify_provenance = api_verify_provenance(verify_provenance_args)
+    verify_provenance = api_signature_provenance(verify_provenance_args)
     print_if_verbose("The ATR found a matching OpenPGP key with the following 
fingerprint:\n")
     print_if_verbose(verify_provenance.fingerprint.upper() + "\n")
     print_if_verbose("This key is associated with these committees with a 
project containing the artifact:\n")
@@ -1396,7 +1400,7 @@ def timestamp_format(ts: int | str | None) -> str | None:
 
 
 def verify_summary(
-    verify_provenance: models.api.VerifyProvenanceResults,
+    verify_provenance: models.api.SignatureProvenanceResults,
     signature_data: bytes,
     artifact_data: bytes,
     verbose: bool = False,
diff --git a/src/atrclient/models/api.py b/src/atrclient/models/api.py
index f6d0906..8a0b0a9 100644
--- a/src/atrclient/models/api.py
+++ b/src/atrclient/models/api.py
@@ -51,34 +51,24 @@ class ChecksOngoingResults(schema.Strict):
     ongoing: int = schema.Field(..., **example(10))
 
 
-class CommitteesGetResults(schema.Strict):
-    endpoint: Literal["/committees/get"] = schema.Field(alias="endpoint")
+class CommitteeGetResults(schema.Strict):
+    endpoint: Literal["/committee/get"] = schema.Field(alias="endpoint")
     committee: sql.Committee
 
 
-class CommitteesKeysResults(schema.Strict):
-    endpoint: Literal["/committees/keys"] = schema.Field(alias="endpoint")
+class CommitteeKeysResults(schema.Strict):
+    endpoint: Literal["/committee/keys"] = schema.Field(alias="endpoint")
     keys: Sequence[sql.PublicSigningKey]
 
 
-class CommitteesListResults(schema.Strict):
-    endpoint: Literal["/committees/list"] = schema.Field(alias="endpoint")
-    committees: Sequence[sql.Committee]
-
-
-class CommitteesProjectsResults(schema.Strict):
-    endpoint: Literal["/committees/projects"] = schema.Field(alias="endpoint")
+class CommitteeProjectsResults(schema.Strict):
+    endpoint: Literal["/committee/projects"] = schema.Field(alias="endpoint")
     projects: Sequence[sql.Project]
 
 
-class DraftDeleteArgs(schema.Strict):
-    project: str = schema.Field(..., **example("example"))
-    version: str = schema.Field(..., **example("0.0.1"))
-
-
-class DraftDeleteResults(schema.Strict):
-    endpoint: Literal["/draft/delete"] = schema.Field(alias="endpoint")
-    success: str = schema.Field(..., **example("Draft 'example-0.0.1' 
deleted"))
+class CommitteesListResults(schema.Strict):
+    endpoint: Literal["/committees/list"] = schema.Field(alias="endpoint")
+    committees: Sequence[sql.Committee]
 
 
 class JwtCreateArgs(schema.Strict):
@@ -92,7 +82,7 @@ class JwtCreateResults(schema.Strict):
     jwt: str = schema.Field(..., 
**example("eyJhbGciOiJIUzI1[...]mMjLiuyu5CSpyHI="))
 
 
-class KeysAddArgs(schema.Strict):
+class KeyAddArgs(schema.Strict):
     asfuid: str = schema.Field(..., **example("user"))
     key: str = schema.Field(
         ..., **example("-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n...\n-----END 
PGP PUBLIC KEY BLOCK-----\n")
@@ -100,23 +90,23 @@ class KeysAddArgs(schema.Strict):
     committees: list[str] = schema.Field(..., **example(["example"]))
 
 
-class KeysAddResults(schema.Strict):
-    endpoint: Literal["/keys/add"] = schema.Field(alias="endpoint")
+class KeyAddResults(schema.Strict):
+    endpoint: Literal["/key/add"] = schema.Field(alias="endpoint")
     success: str = schema.Field(..., **example("Key added"))
     fingerprint: str = schema.Field(..., 
**example("0123456789abcdef0123456789abcdef01234567"))
 
 
-class KeysDeleteArgs(schema.Strict):
+class KeyDeleteArgs(schema.Strict):
     fingerprint: str = schema.Field(..., 
**example("0123456789abcdef0123456789abcdef01234567"))
 
 
-class KeysDeleteResults(schema.Strict):
-    endpoint: Literal["/keys/delete"] = schema.Field(alias="endpoint")
+class KeyDeleteResults(schema.Strict):
+    endpoint: Literal["/key/delete"] = schema.Field(alias="endpoint")
     success: str = schema.Field(..., **example("Key deleted"))
 
 
-class KeysGetResults(schema.Strict):
-    endpoint: Literal["/keys/get"] = schema.Field(alias="endpoint")
+class KeyGetResults(schema.Strict):
+    endpoint: Literal["/key/get"] = schema.Field(alias="endpoint")
     key: sql.PublicSigningKey
 
 
@@ -147,13 +137,6 @@ KeysUploadOutcome = Annotated[
 KeysUploadOutcomeAdapter = pydantic.TypeAdapter(KeysUploadOutcome)
 
 
-# def validate_keys_upload_outcome(value: Any) -> KeysUploadOutcome:
-#     obj = KeysUploadOutcomeAdapter.validate_python(value)
-#     if not isinstance(obj, KeysUploadOutcome):
-#         raise ResultsTypeError(f"Invalid API response: {value}")
-#     return obj
-
-
 class KeysUploadResults(schema.Strict):
     endpoint: Literal["/keys/upload"] = schema.Field(alias="endpoint")
     results: Sequence[KeysUploadResult | KeysUploadException]
@@ -167,21 +150,21 @@ class KeysUserResults(schema.Strict):
     keys: Sequence[sql.PublicSigningKey]
 
 
-class ProjectsGetResults(schema.Strict):
-    endpoint: Literal["/projects/get"] = schema.Field(alias="endpoint")
+class ProjectGetResults(schema.Strict):
+    endpoint: Literal["/project/get"] = schema.Field(alias="endpoint")
     project: sql.Project
 
 
+class ProjectReleasesResults(schema.Strict):
+    endpoint: Literal["/project/releases"] = schema.Field(alias="endpoint")
+    releases: Sequence[sql.Release]
+
+
 class ProjectsListResults(schema.Strict):
     endpoint: Literal["/projects/list"] = schema.Field(alias="endpoint")
     projects: Sequence[sql.Project]
 
 
-class ProjectsReleasesResults(schema.Strict):
-    endpoint: Literal["/projects/releases"] = schema.Field(alias="endpoint")
-    releases: Sequence[sql.Release]
-
-
 class ReleaseAnnounceArgs(schema.Strict):
     project: str = schema.Field(..., **example("example"))
     version: str = schema.Field(..., **example("1.0.0"))
@@ -200,60 +183,38 @@ class ReleaseAnnounceResults(schema.Strict):
     success: bool = schema.Field(..., **example(True))
 
 
[email protected]
-class ReleasesQuery:
-    offset: int = 0
-    limit: int = 20
-    phase: str | None = None
+class ReleaseDraftDeleteArgs(schema.Strict):
+    project: str = schema.Field(..., **example("example"))
+    version: str = schema.Field(..., **example("0.0.1"))
 
 
-class ReleasesResults(schema.Strict):
-    endpoint: Literal["/releases"] = schema.Field(alias="endpoint")
-    data: Sequence[sql.Release]
-    count: int
+class ReleaseDraftDeleteResults(schema.Strict):
+    endpoint: Literal["/release/draft/delete"] = schema.Field(alias="endpoint")
+    success: str = schema.Field(..., **example("Draft 'example-0.0.1' 
deleted"))
 
 
-class ReleasesCreateArgs(schema.Strict):
+class ReleaseCreateArgs(schema.Strict):
     project: str
     version: str
 
 
-class ReleasesCreateResults(schema.Strict):
-    endpoint: Literal["/releases/create"] = schema.Field(alias="endpoint")
+class ReleaseCreateResults(schema.Strict):
+    endpoint: Literal["/release/create"] = schema.Field(alias="endpoint")
     release: sql.Release
 
 
-class ReleasesDeleteArgs(schema.Strict):
+class ReleaseDeleteArgs(schema.Strict):
     project: str
     version: str
 
 
-class ReleasesDeleteResults(schema.Strict):
-    endpoint: Literal["/releases/delete"] = schema.Field(alias="endpoint")
+class ReleaseDeleteResults(schema.Strict):
+    endpoint: Literal["/release/delete"] = schema.Field(alias="endpoint")
     deleted: str
 
 
-class ReleasesPathsResults(schema.Strict):
-    endpoint: Literal["/releases/paths"] = schema.Field(alias="endpoint")
-    rel_paths: Sequence[str]
-
-
[email protected]
-class ReleasesProjectQuery:
-    limit: int = 20
-    offset: int = 0
-    # project: str
-    # version: str
-
-
-class ReleasesProjectResults(schema.Strict):
-    endpoint: Literal["/releases/project"] = schema.Field(alias="endpoint")
-    data: Sequence[sql.Release]
-    count: int
-
-
-class ReleasesVersionResults(schema.Strict):
-    endpoint: Literal["/releases/version"] = schema.Field(alias="endpoint")
+class ReleaseGetResults(schema.Strict):
+    endpoint: Literal["/release/get"] = schema.Field(alias="endpoint")
     release: sql.Release
 
     @pydantic.field_validator("release", mode="before")
@@ -270,42 +231,88 @@ class ReleasesVersionResults(schema.Strict):
         return v
 
 
-class ReleasesRevisionsResults(schema.Strict):
-    endpoint: Literal["/releases/revisions"] = schema.Field(alias="endpoint")
-    revisions: Sequence[sql.Revision]
+class ReleasePathsResults(schema.Strict):
+    endpoint: Literal["/release/paths"] = schema.Field(alias="endpoint")
+    rel_paths: Sequence[str]
 
 
-class RevisionsResults(schema.Strict):
-    endpoint: Literal["/revisions"] = schema.Field(alias="endpoint")
+class ReleaseRevisionsResults(schema.Strict):
+    endpoint: Literal["/release/revisions"] = schema.Field(alias="endpoint")
     revisions: Sequence[sql.Revision]
 
 
-class SshAddArgs(schema.Strict):
+class ReleaseUploadArgs(schema.Strict):
+    project: str
+    version: str
+    relpath: str
+    content: str
+
+
+class ReleaseUploadResults(schema.Strict):
+    endpoint: Literal["/release/upload"] = schema.Field(alias="endpoint")
+    revision: sql.Revision
+
+
[email protected]
+class ReleasesListQuery:
+    offset: int = 0
+    limit: int = 20
+    phase: str | None = None
+
+
+class ReleasesListResults(schema.Strict):
+    endpoint: Literal["/releases/list"] = schema.Field(alias="endpoint")
+    data: Sequence[sql.Release]
+    count: int
+
+
+class SignatureProvenanceArgs(schema.Strict):
+    artifact_file_name: str
+    artifact_sha3_256: str
+    signature_file_name: str
+    signature_asc_text: str
+    signature_sha3_256: str
+
+
+class SignatureProvenanceKey(schema.Strict):
+    committee: str
+    keys_file_url: str
+    keys_file_sha3_256: str
+
+
+class SignatureProvenanceResults(schema.Strict):
+    endpoint: Literal["/signature/provenance"] = schema.Field(alias="endpoint")
+    fingerprint: str
+    key_asc_text: str
+    committees_with_artifact: list[SignatureProvenanceKey]
+
+
+class SshKeyAddArgs(schema.Strict):
     text: str
 
 
-class SshAddResults(schema.Strict):
-    endpoint: Literal["/ssh/add"] = schema.Field(alias="endpoint")
+class SshKeyAddResults(schema.Strict):
+    endpoint: Literal["/ssh-key/add"] = schema.Field(alias="endpoint")
     fingerprint: str
 
 
-class SshDeleteArgs(schema.Strict):
+class SshKeyDeleteArgs(schema.Strict):
     fingerprint: str
 
 
-class SshDeleteResults(schema.Strict):
-    endpoint: Literal["/ssh/delete"] = schema.Field(alias="endpoint")
+class SshKeyDeleteResults(schema.Strict):
+    endpoint: Literal["/ssh-key/delete"] = schema.Field(alias="endpoint")
     success: str
 
 
 @dataclasses.dataclass
-class SshListQuery:
+class SshKeysListQuery:
     offset: int = 0
     limit: int = 20
 
 
-class SshListResults(schema.Strict):
-    endpoint: Literal["/ssh/list"] = schema.Field(alias="endpoint")
+class SshKeysListResults(schema.Strict):
+    endpoint: Literal["/ssh-keys/list"] = schema.Field(alias="endpoint")
     data: Sequence[sql.SSHKey]
     count: int
 
@@ -364,77 +371,42 @@ class VoteTabulateResults(schema.Strict):
     details: tabulate.VoteDetails
 
 
-class UploadArgs(schema.Strict):
-    project: str
-    version: str
-    relpath: str
-    content: str
-
-
-class UploadResults(schema.Strict):
-    endpoint: Literal["/upload"] = schema.Field(alias="endpoint")
-    revision: sql.Revision
-
-
-class VerifyProvenanceArgs(schema.Strict):
-    artifact_file_name: str
-    artifact_sha3_256: str
-    signature_file_name: str
-    signature_asc_text: str
-    signature_sha3_256: str
-
-
-class VerifyProvenanceKey(schema.Strict):
-    committee: str
-    keys_file_url: str
-    keys_file_sha3_256: str
-
-
-class VerifyProvenanceResults(schema.Strict):
-    endpoint: Literal["/verify/provenance"] = schema.Field(alias="endpoint")
-    fingerprint: str
-    key_asc_text: str
-    committees_with_artifact: list[VerifyProvenanceKey]
-
-
 # This is for *Results classes only
 # We do NOT put *Args classes here
 Results = Annotated[
     ChecksListResults
     | ChecksOngoingResults
-    | CommitteesGetResults
-    | CommitteesKeysResults
+    | CommitteeGetResults
+    | CommitteeKeysResults
+    | CommitteeProjectsResults
     | CommitteesListResults
-    | CommitteesProjectsResults
-    | DraftDeleteResults
     | JwtCreateResults
-    | KeysAddResults
-    | KeysDeleteResults
-    | KeysGetResults
+    | KeyAddResults
+    | KeyDeleteResults
+    | KeyGetResults
     | KeysUploadResults
     | KeysUserResults
-    | ProjectsGetResults
+    | ProjectGetResults
+    | ProjectReleasesResults
     | ProjectsListResults
-    | ProjectsReleasesResults
     | ReleaseAnnounceResults
-    | ReleasesResults
-    | ReleasesCreateResults
-    | ReleasesDeleteResults
-    | ReleasesPathsResults
-    | ReleasesProjectResults
-    | ReleasesVersionResults
-    | ReleasesRevisionsResults
-    | RevisionsResults
-    | SshAddResults
-    | SshDeleteResults
-    | SshListResults
+    | ReleaseCreateResults
+    | ReleaseDeleteResults
+    | ReleaseDraftDeleteResults
+    | ReleaseGetResults
+    | ReleasePathsResults
+    | ReleaseRevisionsResults
+    | ReleaseUploadResults
+    | ReleasesListResults
+    | SignatureProvenanceResults
+    | SshKeyAddResults
+    | SshKeyDeleteResults
+    | SshKeysListResults
     | TasksResults
     | UsersListResults
-    | VerifyProvenanceResults
     | VoteResolveResults
     | VoteStartResults
-    | VoteTabulateResults
-    | UploadResults,
+    | VoteTabulateResults,
     schema.Field(discriminator="endpoint"),
 ]
 
@@ -453,36 +425,34 @@ def validator[T](t: type[T]) -> Callable[[Any], T]:
 
 validate_checks_list = validator(ChecksListResults)
 validate_checks_ongoing = validator(ChecksOngoingResults)
-validate_committees_get = validator(CommitteesGetResults)
-validate_committees_keys = validator(CommitteesKeysResults)
+validate_committee_get = validator(CommitteeGetResults)
+validate_committee_keys = validator(CommitteeKeysResults)
+validate_committee_projects = validator(CommitteeProjectsResults)
 validate_committees_list = validator(CommitteesListResults)
-validate_committees_projects = validator(CommitteesProjectsResults)
-validate_draft_delete = validator(DraftDeleteResults)
 validate_jwt_create = validator(JwtCreateResults)
-validate_keys_add = validator(KeysAddResults)
-validate_keys_delete = validator(KeysDeleteResults)
-validate_keys_get = validator(KeysGetResults)
+validate_key_add = validator(KeyAddResults)
+validate_key_delete = validator(KeyDeleteResults)
+validate_key_get = validator(KeyGetResults)
 validate_keys_upload = validator(KeysUploadResults)
 validate_keys_user = validator(KeysUserResults)
-validate_projects_get = validator(ProjectsGetResults)
+validate_project_get = validator(ProjectGetResults)
+validate_project_releases = validator(ProjectReleasesResults)
 validate_projects_list = validator(ProjectsListResults)
-validate_projects_releases = validator(ProjectsReleasesResults)
 validate_release_announce = validator(ReleaseAnnounceResults)
-validate_releases = validator(ReleasesResults)
-validate_releases_create = validator(ReleasesCreateResults)
-validate_releases_delete = validator(ReleasesDeleteResults)
-validate_releases_paths = validator(ReleasesPathsResults)
-validate_releases_project = validator(ReleasesProjectResults)
-validate_releases_version = validator(ReleasesVersionResults)
-validate_releases_revisions = validator(ReleasesRevisionsResults)
-validate_revisions = validator(RevisionsResults)
-validate_ssh_add = validator(SshAddResults)
-validate_ssh_delete = validator(SshDeleteResults)
-validate_ssh_list = validator(SshListResults)
+validate_release_create = validator(ReleaseCreateResults)
+validate_release_delete = validator(ReleaseDeleteResults)
+validate_release_draft_delete = validator(ReleaseDraftDeleteResults)
+validate_release_get = validator(ReleaseGetResults)
+validate_release_paths = validator(ReleasePathsResults)
+validate_release_revisions = validator(ReleaseRevisionsResults)
+validate_release_upload = validator(ReleaseUploadResults)
+validate_releases_list = validator(ReleasesListResults)
+validate_signature_provenance = validator(SignatureProvenanceResults)
+validate_ssh_key_add = validator(SshKeyAddResults)
+validate_ssh_key_delete = validator(SshKeyDeleteResults)
+validate_ssh_keys_list = validator(SshKeysListResults)
 validate_tasks = validator(TasksResults)
 validate_users_list = validator(UsersListResults)
-validate_verify_provenance = validator(VerifyProvenanceResults)
 validate_vote_resolve = validator(VoteResolveResults)
 validate_vote_start = validator(VoteStartResults)
 validate_vote_tabulate = validator(VoteTabulateResults)
-validate_upload = validator(UploadResults)
diff --git a/tests/cli_ssh.t b/tests/cli_ssh.t
index aec05a1..b68aa9a 100644
--- a/tests/cli_ssh.t
+++ b/tests/cli_ssh.t
@@ -19,7 +19,7 @@ $ atr ssh list
 ! atr ssh add invalid-key
 <.stderr.>
 atr: error: Error message from the API:
-500 https://localhost.apache.org:8080/api/ssh/add
+500 https://localhost.apache.org:8080/api/ssh-key/add
 {
   "error": "Invalid SSH key format"
 }
diff --git a/tests/test_all.py b/tests/test_all.py
index 9af300d..a42bca4 100755
--- a/tests/test_all.py
+++ b/tests/test_all.py
@@ -52,14 +52,14 @@ def test_app_checks_status_non_draft_phase(
     client.app_set("atr.host", "example.invalid")
     client.app_set("tokens.jwt", "dummy_jwt_token")
 
-    releases_url = 
"https://example.invalid/api/releases/version/test-project/2.3.0";
+    releases_url = "https://example.invalid/api/release/get/test-project/2.3.0";
 
     with aioresponses.aioresponses() as mock:
         mock.get(
             releases_url,
             status=200,
             payload={
-                "endpoint": "/releases/version",
+                "endpoint": "/release/get",
                 "release": {
                     "name": "test-project-2.3.0",
                     "project_name": "test-project",
@@ -86,11 +86,11 @@ def test_app_checks_status_verbose(capsys: 
pytest.CaptureFixture[str], fixture_c
     client.app_set("atr.host", "example.invalid")
     client.app_set("tokens.jwt", "dummy_jwt_token")
 
-    release_url = 
"https://example.invalid/api/releases/version/test-project/2.3.1";
+    release_url = "https://example.invalid/api/release/get/test-project/2.3.1";
     checks_url = 
"https://example.invalid/api/checks/list/test-project/2.3.1/00003";
 
     release_payload = {
-        "endpoint": "/releases/version",
+        "endpoint": "/release/get",
         "release": {
             "name": "test-project-2.3.1",
             "project_name": "test-project",
@@ -161,7 +161,7 @@ def test_app_checks_status_verbose(capsys: 
pytest.CaptureFixture[str], fixture_c
 def test_app_release_list_not_found(capsys: pytest.CaptureFixture[str], 
fixture_config_env: pathlib.Path) -> None:
     client.app_set("atr.host", "example.invalid")
 
-    releases_url = 
"https://example.invalid/api/releases/project/nonexistent-project";
+    releases_url = 
"https://example.invalid/api/project/releases/nonexistent-project";
 
     with aioresponses.aioresponses() as mock:
         mock.get(releases_url, status=404, body="Not Found")
@@ -173,11 +173,11 @@ def test_app_release_list_not_found(capsys: 
pytest.CaptureFixture[str], fixture_
 def test_app_release_list_success(capsys: pytest.CaptureFixture[str], 
fixture_config_env: pathlib.Path) -> None:
     client.app_set("atr.host", "example.invalid")
 
-    releases_url = "https://example.invalid/api/releases/project/test-project";
+    releases_url = "https://example.invalid/api/project/releases/test-project";
 
     payload = {
-        "endpoint": "/releases/project",
-        "data": [
+        "endpoint": "/project/releases",
+        "releases": [
             {
                 "name": "test-project-2.3.1",
                 "project_name": "test-project",
@@ -203,7 +203,6 @@ def test_app_release_list_success(capsys: 
pytest.CaptureFixture[str], fixture_co
                 "vote_manual": False,
             },
         ],
-        "count": 2,
     }
 
     with aioresponses.aioresponses() as mock:
diff --git a/uv.lock b/uv.lock
index 47e769f..8ecf92a 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2,7 +2,7 @@ version = 1
 requires-python = ">=3.13"
 
 [options]
-exclude-newer = "2025-07-29T13:18:00Z"
+exclude-newer = "2025-07-29T13:58:00Z"
 
 [[package]]
 name = "aiohappyeyeballs"
@@ -83,7 +83,7 @@ wheels = [
 
 [[package]]
 name = "apache-trusted-releases"
-version = "0.20250729.1318"
+version = "0.20250729.1358"
 source = { editable = "." }
 dependencies = [
     { name = "aiohttp" },
@@ -722,6 +722,17 @@ dependencies = [
     { name = "typing-extensions" },
 ]
 sdist = { url = 
"https://files.pythonhosted.org/packages/5a/03/a0af991e3a43174d6b83fca4fb399745abceddd1171bdabae48ce877ff47/sqlalchemy-2.0.42.tar.gz";,
 hash = 
"sha256:160bedd8a5c28765bd5be4dec2d881e109e33b34922e50a3b881a7681773ac5f", size 
= 9749972 }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/e9/7e/25d8c28b86730c9fb0e09156f601d7a96d1c634043bf8ba36513eb78887b/sqlalchemy-2.0.42-cp313-cp313-macosx_10_13_x86_64.whl";,
 hash = 
"sha256:941804f55c7d507334da38133268e3f6e5b0340d584ba0f277dd884197f4ae8c", size 
= 2127905 },
+    { url = 
"https://files.pythonhosted.org/packages/e5/a1/9d8c93434d1d983880d976400fcb7895a79576bd94dca61c3b7b90b1ed0d/sqlalchemy-2.0.42-cp313-cp313-macosx_11_0_arm64.whl";,
 hash = 
"sha256:95d3d06a968a760ce2aa6a5889fefcbdd53ca935735e0768e1db046ec08cbf01", size 
= 2115726 },
+    { url = 
"https://files.pythonhosted.org/packages/a2/cc/d33646fcc24c87cc4e30a03556b611a4e7bcfa69a4c935bffb923e3c89f4/sqlalchemy-2.0.42-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl";,
 hash = 
"sha256:4cf10396a8a700a0f38ccd220d940be529c8f64435c5d5b29375acab9267a6c9", size 
= 3246007 },
+    { url = 
"https://files.pythonhosted.org/packages/67/08/4e6c533d4c7f5e7c4cbb6fe8a2c4e813202a40f05700d4009a44ec6e236d/sqlalchemy-2.0.42-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";,
 hash = 
"sha256:9cae6c2b05326d7c2c7c0519f323f90e0fb9e8afa783c6a05bb9ee92a90d0f04", size 
= 3250919 },
+    { url = 
"https://files.pythonhosted.org/packages/5c/82/f680e9a636d217aece1b9a8030d18ad2b59b5e216e0c94e03ad86b344af3/sqlalchemy-2.0.42-cp313-cp313-musllinux_1_2_aarch64.whl";,
 hash = 
"sha256:f50f7b20677b23cfb35b6afcd8372b2feb348a38e3033f6447ee0704540be894", size 
= 3180546 },
+    { url = 
"https://files.pythonhosted.org/packages/7d/a2/8c8f6325f153894afa3775584c429cc936353fb1db26eddb60a549d0ff4b/sqlalchemy-2.0.42-cp313-cp313-musllinux_1_2_x86_64.whl";,
 hash = 
"sha256:9d88a1c0d66d24e229e3938e1ef16ebdbd2bf4ced93af6eff55225f7465cf350", size 
= 3216683 },
+    { url = 
"https://files.pythonhosted.org/packages/39/44/3a451d7fa4482a8ffdf364e803ddc2cfcafc1c4635fb366f169ecc2c3b11/sqlalchemy-2.0.42-cp313-cp313-win32.whl";,
 hash = 
"sha256:45c842c94c9ad546c72225a0c0d1ae8ef3f7c212484be3d429715a062970e87f", size 
= 2093990 },
+    { url = 
"https://files.pythonhosted.org/packages/4b/9e/9bce34f67aea0251c8ac104f7bdb2229d58fb2e86a4ad8807999c4bee34b/sqlalchemy-2.0.42-cp313-cp313-win_amd64.whl";,
 hash = 
"sha256:eb9905f7f1e49fd57a7ed6269bc567fcbbdac9feadff20ad6bd7707266a91577", size 
= 2120473 },
+    { url = 
"https://files.pythonhosted.org/packages/ee/55/ba2546ab09a6adebc521bf3974440dc1d8c06ed342cceb30ed62a8858835/sqlalchemy-2.0.42-py3-none-any.whl";,
 hash = 
"sha256:defcdff7e661f0043daa381832af65d616e060ddb54d3fe4476f51df7eaa1835", size 
= 1922072 },
+]
 
 [[package]]
 name = "sqlmodel"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to