python-gitea 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitea/__init__.py +7 -0
- gitea/__main__.py +8 -0
- gitea/cli/__init__.py +1 -0
- gitea/cli/main.py +128 -0
- gitea/cli/user/__init__.py +1 -0
- gitea/cli/user/delete_user_level_runner.py +40 -0
- gitea/cli/user/get_registration_token.py +31 -0
- gitea/cli/user/get_user.py +42 -0
- gitea/cli/user/get_user_level_runners.py +34 -0
- gitea/cli/user/get_workflow_jobs.py +50 -0
- gitea/cli/user/get_workflow_runs.py +67 -0
- gitea/cli/user/main.py +34 -0
- gitea/cli/utils.py +46 -0
- gitea/client/__init__.py +8 -0
- gitea/client/async_gitea.py +106 -0
- gitea/client/base.py +45 -0
- gitea/client/gitea.py +93 -0
- gitea/issue/__init__.py +8 -0
- gitea/issue/async_issue.py +277 -0
- gitea/issue/base.py +201 -0
- gitea/issue/issue.py +277 -0
- gitea/resource/__init__.py +1 -0
- gitea/resource/async_resource.py +87 -0
- gitea/resource/resource.py +88 -0
- gitea/user/__init__.py +1 -0
- gitea/user/async_user.py +129 -0
- gitea/user/base.py +113 -0
- gitea/user/user.py +112 -0
- gitea/utils/__init__.py +7 -0
- gitea/utils/log.py +70 -0
- gitea/utils/response.py +59 -0
- gitea/version.py +11 -0
- python_gitea-0.3.0.dist-info/METADATA +185 -0
- python_gitea-0.3.0.dist-info/RECORD +37 -0
- python_gitea-0.3.0.dist-info/WHEEL +4 -0
- python_gitea-0.3.0.dist-info/entry_points.txt +2 -0
- python_gitea-0.3.0.dist-info/licenses/LICENSE +21 -0
gitea/__init__.py
ADDED
gitea/__main__.py
ADDED
gitea/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command line interface for Gitea."""
|
gitea/cli/main.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Main entry point for the python-gitea CLI application."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import enum
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LoggingLevel(str, enum.Enum):
|
|
13
|
+
"""Logging levels for the CLI."""
|
|
14
|
+
|
|
15
|
+
NOTSET = "NOTSET"
|
|
16
|
+
DEBUG = "DEBUG"
|
|
17
|
+
INFO = "INFO"
|
|
18
|
+
WARNING = "WARNING"
|
|
19
|
+
ERROR = "ERROR"
|
|
20
|
+
CRITICAL = "CRITICAL"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Create the main Typer app
|
|
24
|
+
app = typer.Typer(
|
|
25
|
+
name="python-gitea",
|
|
26
|
+
help="Main CLI for python-gitea.",
|
|
27
|
+
rich_markup_mode="rich",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def setup_logging(level: LoggingLevel = LoggingLevel.INFO) -> None:
|
|
32
|
+
"""Set up logging with Rich handler.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
level: Logging level.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
import logging # noqa: PLC0415
|
|
39
|
+
|
|
40
|
+
from rich.console import Console # noqa: PLC0415
|
|
41
|
+
from rich.logging import RichHandler # noqa: PLC0415
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger("python-gitea")
|
|
44
|
+
|
|
45
|
+
logger.setLevel(level.value)
|
|
46
|
+
|
|
47
|
+
console = Console()
|
|
48
|
+
|
|
49
|
+
# Remove any existing handlers to ensure RichHandler is used
|
|
50
|
+
for h in logger.handlers[:]: # Use slice copy to avoid modification during iteration
|
|
51
|
+
logger.removeHandler(h)
|
|
52
|
+
# Add the RichHandler
|
|
53
|
+
|
|
54
|
+
handler = RichHandler(
|
|
55
|
+
console=console,
|
|
56
|
+
rich_tracebacks=True,
|
|
57
|
+
show_time=True,
|
|
58
|
+
show_level=True, # Keep level (e.g., DEBUG, INFO) for clarity
|
|
59
|
+
markup=True, # Enable Rich markup in messages for styling
|
|
60
|
+
level=level.value, # Ensure handler respects the level
|
|
61
|
+
omit_repeated_times=False,
|
|
62
|
+
log_time_format="%H:%M",
|
|
63
|
+
)
|
|
64
|
+
handler.setLevel(level.value)
|
|
65
|
+
logger.addHandler(handler)
|
|
66
|
+
|
|
67
|
+
# Prevent propagation to root logger to avoid duplicate output
|
|
68
|
+
logger.propagate = False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.callback()
|
|
72
|
+
def main( # noqa: PLR0913
|
|
73
|
+
ctx: typer.Context,
|
|
74
|
+
output: Annotated[Path | None, typer.Option("--output", "-o", help="Output file name.")] = None,
|
|
75
|
+
token: Annotated[
|
|
76
|
+
str | None, typer.Option("--token", "-t", help="Gitea API token.", envvar="GITEA_API_TOKEN")
|
|
77
|
+
] = None,
|
|
78
|
+
base_url: Annotated[
|
|
79
|
+
str,
|
|
80
|
+
typer.Option(
|
|
81
|
+
"--base-url",
|
|
82
|
+
"-b",
|
|
83
|
+
help="Base URL of the Gitea instance.",
|
|
84
|
+
envvar="GITEA_BASE_URL",
|
|
85
|
+
show_default=True,
|
|
86
|
+
),
|
|
87
|
+
] = "https://gitea.com",
|
|
88
|
+
timeout: Annotated[
|
|
89
|
+
int,
|
|
90
|
+
typer.Option(
|
|
91
|
+
"--timeout",
|
|
92
|
+
help="Timeout for API requests in seconds.",
|
|
93
|
+
show_default=True,
|
|
94
|
+
),
|
|
95
|
+
] = 30,
|
|
96
|
+
verbose: Annotated[
|
|
97
|
+
LoggingLevel,
|
|
98
|
+
typer.Option("--verbose", "-v", help="Set verbosity level."),
|
|
99
|
+
] = LoggingLevel.INFO,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Enter the CLI application.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
ctx: Typer context.
|
|
105
|
+
output: Output file name.
|
|
106
|
+
token: Gitea API token.
|
|
107
|
+
base_url: Base URL of the Gitea instance.
|
|
108
|
+
timeout: Timeout for API requests in seconds.
|
|
109
|
+
verbose: Verbosity level for logging.
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
setup_logging(verbose)
|
|
113
|
+
ctx.obj = {
|
|
114
|
+
"output": output,
|
|
115
|
+
"token": token,
|
|
116
|
+
"base_url": base_url,
|
|
117
|
+
"timeout": timeout,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def register_commands() -> None:
|
|
122
|
+
"""Register CLI commands."""
|
|
123
|
+
from gitea.cli.user.main import user_app # noqa: PLC0415
|
|
124
|
+
|
|
125
|
+
app.add_typer(user_app, name="user", help="Commands for managing Gitea users.")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
register_commands()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""User module for CLI commands."""
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Delete user-level runner command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def delete_user_level_runner_command(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
runner_id: Annotated[str, typer.Option("--runner-id", help="The ID of the runner to delete.")],
|
|
13
|
+
) -> None:
|
|
14
|
+
"""Delete a user-level runner for the authenticated user.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
ctx: The Typer context.
|
|
18
|
+
runner_id: The ID of the runner to delete.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
from typing import Any # noqa: PLC0415
|
|
22
|
+
|
|
23
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
24
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
25
|
+
|
|
26
|
+
token: str | None = ctx.obj.get("token")
|
|
27
|
+
base_url: str = ctx.obj.get("base_url")
|
|
28
|
+
timeout: int = ctx.obj.get("timeout")
|
|
29
|
+
|
|
30
|
+
def api_call() -> dict[str, Any] | None:
|
|
31
|
+
"""Delete a user-level runner.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
A dictionary containing the result of the deletion.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
38
|
+
return client.user.delete_user_level_runner(runner_id=runner_id, timeout=timeout)
|
|
39
|
+
|
|
40
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="delete-user-level-runner")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Get registration token command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_registration_token_command(
|
|
9
|
+
ctx: typer.Context,
|
|
10
|
+
) -> None:
|
|
11
|
+
"""Get registration token for the authenticated user."""
|
|
12
|
+
from typing import Any # noqa: PLC0415
|
|
13
|
+
|
|
14
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
15
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
16
|
+
|
|
17
|
+
token: str | None = ctx.obj.get("token")
|
|
18
|
+
base_url: str = ctx.obj.get("base_url")
|
|
19
|
+
timeout: int = ctx.obj.get("timeout")
|
|
20
|
+
|
|
21
|
+
def api_call() -> dict[str, Any] | None:
|
|
22
|
+
"""Get registration token.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A dictionary containing the registration token.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
29
|
+
return client.user.get_registration_token(timeout=timeout)
|
|
30
|
+
|
|
31
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="get-registration-token")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Get user information command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_user_command(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
username: Annotated[
|
|
13
|
+
str | None, "The username of the user to retrieve. If None, retrieves the authenticated user."
|
|
14
|
+
] = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Get user information.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
ctx: The Typer context.
|
|
20
|
+
username: The username of the user to retrieve. If None, retrieves the authenticated user.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
from typing import Any # noqa: PLC0415
|
|
24
|
+
|
|
25
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
26
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
27
|
+
|
|
28
|
+
token: str | None = ctx.obj.get("token")
|
|
29
|
+
base_url: str = ctx.obj.get("base_url")
|
|
30
|
+
timeout: int = ctx.obj.get("timeout")
|
|
31
|
+
|
|
32
|
+
def api_call() -> dict[str, Any] | None:
|
|
33
|
+
"""Get user information.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The user information as a dictionary.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
40
|
+
return client.user.get_user(username=username, timeout=timeout)
|
|
41
|
+
|
|
42
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="get-user")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Get user-level runners command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_user_level_runners_command(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
runner_id: Annotated[str | None, typer.Option("--runner-id", help="The ID of the runner to retrieve.")] = None,
|
|
13
|
+
) -> None:
|
|
14
|
+
"""Get user-level runners for the authenticated user."""
|
|
15
|
+
from typing import Any # noqa: PLC0415
|
|
16
|
+
|
|
17
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
18
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
19
|
+
|
|
20
|
+
token: str | None = ctx.obj.get("token")
|
|
21
|
+
base_url: str = ctx.obj.get("base_url")
|
|
22
|
+
timeout: int = ctx.obj.get("timeout")
|
|
23
|
+
|
|
24
|
+
def api_call() -> dict[str, Any] | None:
|
|
25
|
+
"""Get user-level runners.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A dictionary containing the user-level runners.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
32
|
+
return client.user.get_user_level_runners(runner_id=runner_id, timeout=timeout)
|
|
33
|
+
|
|
34
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="get-user-level-runners")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Get workflow jobs command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated, Literal
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_workflow_jobs_command(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
status: Annotated[
|
|
13
|
+
Literal["pending", "queued", "in_progress", "failure", "success", "skipped"],
|
|
14
|
+
typer.Option(
|
|
15
|
+
"--status",
|
|
16
|
+
help="The status to filter workflow jobs by. Options: pending, queued, in_progress, failure, success, skipped.",
|
|
17
|
+
),
|
|
18
|
+
],
|
|
19
|
+
page: Annotated[int | None, typer.Option("--page", help="The page number for pagination.")] = None,
|
|
20
|
+
limit: Annotated[int | None, typer.Option("--limit", help="The number of items per page for pagination.")] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Get workflow jobs for the authenticated user filtered by status.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
ctx: The Typer context.
|
|
26
|
+
status: The status to filter workflow jobs by. Options: pending, queued, in_progress, failure, success, skipped.
|
|
27
|
+
page: The page number for pagination.
|
|
28
|
+
limit: The number of items per page for pagination.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
from typing import Any # noqa: PLC0415
|
|
32
|
+
|
|
33
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
34
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
35
|
+
|
|
36
|
+
token: str | None = ctx.obj.get("token")
|
|
37
|
+
base_url: str = ctx.obj.get("base_url")
|
|
38
|
+
timeout: int = ctx.obj.get("timeout")
|
|
39
|
+
|
|
40
|
+
def api_call() -> dict[str, Any] | None:
|
|
41
|
+
"""Get workflow jobs.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A dictionary containing the workflow jobs with the specified status.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
48
|
+
return client.user.get_workflow_jobs(status=status, page=page, limit=limit, timeout=timeout)
|
|
49
|
+
|
|
50
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="get-workflow-jobs")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Get workflow runs command for Gitea CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated, Literal
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_workflow_runs_command( # noqa: PLR0913
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
event: Annotated[str | None, typer.Option("--event", help="Workflow event name.")] = None,
|
|
13
|
+
branch: Annotated[str | None, typer.Option("--branch", help="Workflow branch.")] = None,
|
|
14
|
+
status: Annotated[
|
|
15
|
+
Literal["pending", "queued", "in_progress", "failure", "success", "skipped"] | None,
|
|
16
|
+
typer.Option(
|
|
17
|
+
"--status",
|
|
18
|
+
help="Workflow status.",
|
|
19
|
+
),
|
|
20
|
+
] = None,
|
|
21
|
+
actor: Annotated[str | None, typer.Option("--actor", help="Triggered by user.")] = None,
|
|
22
|
+
head_sha: Annotated[str | None, typer.Option("--head-sha", help="Triggering sha of the workflow run.")] = None,
|
|
23
|
+
page: Annotated[int | None, typer.Option("--page", help="Page number of results to return.")] = None,
|
|
24
|
+
limit: Annotated[int | None, typer.Option("--limit", help="Page size of results.")] = None,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Get workflow runs for the authenticated user filtered by various parameters.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
ctx: The Typer context.
|
|
30
|
+
event: The event that triggered the workflow run.
|
|
31
|
+
branch: The branch name to filter workflow runs.
|
|
32
|
+
status: The status to filter workflow jobs by. Options: pending, queued, in_progress, failure, success, skipped.
|
|
33
|
+
actor: The actor who triggered the workflow run.
|
|
34
|
+
head_sha: The head SHA to filter workflow runs.
|
|
35
|
+
page: The page number for pagination.
|
|
36
|
+
limit: The number of items per page for pagination.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
from typing import Any # noqa: PLC0415
|
|
40
|
+
|
|
41
|
+
import gitea.cli.utils # noqa: PLC0415
|
|
42
|
+
import gitea.client.gitea # noqa: PLC0415
|
|
43
|
+
|
|
44
|
+
token: str | None = ctx.obj.get("token")
|
|
45
|
+
base_url: str = ctx.obj.get("base_url")
|
|
46
|
+
timeout: int = ctx.obj.get("timeout")
|
|
47
|
+
|
|
48
|
+
def api_call() -> dict[str, Any] | None:
|
|
49
|
+
"""Get workflow runs.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
A dictionary containing the workflow runs.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
with gitea.client.gitea.Gitea(token=token, base_url=base_url) as client:
|
|
56
|
+
return client.user.get_workflow_runs(
|
|
57
|
+
event=event,
|
|
58
|
+
branch=branch,
|
|
59
|
+
status=status,
|
|
60
|
+
actor=actor,
|
|
61
|
+
head_sha=head_sha,
|
|
62
|
+
page=page,
|
|
63
|
+
limit=limit,
|
|
64
|
+
timeout=timeout,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
gitea.cli.utils.execute_api_command(ctx=ctx, api_call=api_call, command_name="get-workflow-runs")
|
gitea/cli/user/main.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ruff: noqa PLC0415
|
|
2
|
+
|
|
3
|
+
"""CLI commands for managing Gitea users."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
user_app = typer.Typer(
|
|
10
|
+
name="user",
|
|
11
|
+
help="Commands for managing Gitea users.",
|
|
12
|
+
rich_markup_mode="rich",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_commands() -> None:
|
|
17
|
+
"""Register user-related commands to the user_app."""
|
|
18
|
+
|
|
19
|
+
from gitea.cli.user.get_user import get_user_command
|
|
20
|
+
from gitea.cli.user.get_workflow_jobs import get_workflow_jobs_command
|
|
21
|
+
from gitea.cli.user.get_user_level_runners import get_user_level_runners_command
|
|
22
|
+
from gitea.cli.user.get_registration_token import get_registration_token_command
|
|
23
|
+
from gitea.cli.user.delete_user_level_runner import delete_user_level_runner_command
|
|
24
|
+
from gitea.cli.user.get_workflow_runs import get_workflow_runs_command
|
|
25
|
+
|
|
26
|
+
user_app.command("get-user")(get_user_command)
|
|
27
|
+
user_app.command("get-workflow-jobs")(get_workflow_jobs_command)
|
|
28
|
+
user_app.command("get-user-level-runners")(get_user_level_runners_command)
|
|
29
|
+
user_app.command("get-registration-token")(get_registration_token_command)
|
|
30
|
+
user_app.command("delete-user-level-runner")(delete_user_level_runner_command)
|
|
31
|
+
user_app.command("get-workflow-runs")(get_workflow_runs_command)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
register_commands()
|
gitea/cli/utils.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""CLI utility functions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def execute_api_command(
|
|
15
|
+
ctx: typer.Context,
|
|
16
|
+
api_call: Callable[[], dict[str, Any] | None],
|
|
17
|
+
command_name: str = "Command",
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Execute an API command and output results.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
ctx: Typer context containing token, base_url, and output.
|
|
23
|
+
api_call: Callable that executes the API call and returns the result.
|
|
24
|
+
command_name: Name of the command for error messages.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
output: Path | None = ctx.obj.get("output")
|
|
28
|
+
console = Console()
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
result = api_call()
|
|
32
|
+
|
|
33
|
+
if result is None:
|
|
34
|
+
console.print(f"{command_name} executed successfully. No content returned.")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
json_output = json.dumps(result, indent=4)
|
|
38
|
+
|
|
39
|
+
if output:
|
|
40
|
+
Path(output).write_text(json_output)
|
|
41
|
+
console.print(f"Output saved to {output}")
|
|
42
|
+
else:
|
|
43
|
+
console.print_json(json_output)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
console.print(f"Error executing {command_name}: {e}", style="red")
|
|
46
|
+
raise typer.Exit(1) from e
|
gitea/client/__init__.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Asynchronous Gitea API client implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from aiohttp import ClientResponse, ClientSession, ClientTimeout
|
|
8
|
+
|
|
9
|
+
from gitea.client.base import Client
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncGitea(Client): # pylint: disable=too-few-public-methods
|
|
13
|
+
"""Asynchronous Gitea API client."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, token: str | None = None, base_url: str = "https://gitea.com") -> None:
|
|
16
|
+
"""Initialize the asynchronous Gitea client.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
token: The API token for authentication.
|
|
20
|
+
base_url: The base URL of the Gitea instance.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
super().__init__(token=token, base_url=base_url)
|
|
24
|
+
self.session: ClientSession | None = None
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
"""Return a string representation of the AsyncGitea client.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A string representing the AsyncGitea client.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
return f"AsyncGitea Client(base_url={self.base_url})"
|
|
34
|
+
|
|
35
|
+
async def __aenter__(self) -> AsyncGitea:
|
|
36
|
+
"""Enter the asynchronous context manager.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The AsyncGitea client instance.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
if self.session is not None and not self.session.closed:
|
|
43
|
+
raise RuntimeError("AsyncGitea session already open; do not re-enter context manager.")
|
|
44
|
+
self.session = ClientSession(headers=self.headers)
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
48
|
+
"""Exit the asynchronous context manager.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
exc_type: The exception type.
|
|
52
|
+
exc_val: The exception value.
|
|
53
|
+
exc_tb: The traceback.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
if self.session:
|
|
57
|
+
await self.session.close()
|
|
58
|
+
self.session = None
|
|
59
|
+
|
|
60
|
+
def _get_session(self, headers: dict | None = None, **kwargs: Any) -> ClientSession:
|
|
61
|
+
"""Get or create the aiohttp ClientSession.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
headers: Optional headers to include in the session.
|
|
65
|
+
**kwargs: Additional arguments for ClientSession.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The aiohttp ClientSession instance.
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
return ClientSession(headers=headers, **kwargs)
|
|
72
|
+
|
|
73
|
+
async def _request(
|
|
74
|
+
self, method: str, endpoint: str, headers: dict | None = None, timeout: int = 30, **kwargs: Any
|
|
75
|
+
) -> ClientResponse:
|
|
76
|
+
"""Make an asynchronous HTTP request to the Gitea API.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
method: The HTTP method (GET, POST, etc.).
|
|
80
|
+
endpoint: The API endpoint.
|
|
81
|
+
headers: Optional headers to include in the request.
|
|
82
|
+
timeout: Request timeout in seconds.
|
|
83
|
+
**kwargs: Additional arguments for the request.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
The aiohttp ClientResponse object.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
if self.session is None:
|
|
90
|
+
raise RuntimeError(
|
|
91
|
+
"AsyncGitea must be used as an async context manager. "
|
|
92
|
+
+ "Use 'async with AsyncGitea(...) as client:' to ensure proper resource cleanup."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
url = self._build_url(endpoint=endpoint)
|
|
96
|
+
request_headers = {**self.headers, **(headers or {})}
|
|
97
|
+
timeout_obj = ClientTimeout(total=timeout)
|
|
98
|
+
response = await self.session.request(
|
|
99
|
+
method=method, url=url, headers=request_headers, timeout=timeout_obj, **kwargs
|
|
100
|
+
)
|
|
101
|
+
try:
|
|
102
|
+
response.raise_for_status()
|
|
103
|
+
except Exception:
|
|
104
|
+
response.release()
|
|
105
|
+
raise
|
|
106
|
+
return response
|
gitea/client/base.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Base client class for Gitea API interactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Client: # pylint: disable=too-few-public-methods
|
|
9
|
+
"""Abstract base class for Gitea clients."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, token: str | None, base_url: str) -> None:
|
|
12
|
+
"""Construct the base client.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
token: The API token for authentication.
|
|
16
|
+
base_url: The base URL of the Gitea instance.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
self.token = token
|
|
20
|
+
self.base_url = base_url.rstrip("/")
|
|
21
|
+
self.headers: dict[str, Any] = {}
|
|
22
|
+
if self.token:
|
|
23
|
+
self.headers["Authorization"] = f"token {self.token}"
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def api_url(self) -> str:
|
|
27
|
+
"""Return the base API URL.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
str: The base API URL.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
return f"{self.base_url}/api/v1"
|
|
34
|
+
|
|
35
|
+
def _build_url(self, endpoint: str) -> str:
|
|
36
|
+
"""Construct the full URL for a given endpoint.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
endpoint (str): The API endpoint.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str: The full URL.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
return f"{self.api_url}/{endpoint.lstrip('/')}"
|