python-gitea 0.11.7__py3-none-any.whl → 0.12.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/cli/issue/label/__init__.py +1 -0
- gitea/cli/issue/label/add.py +97 -0
- gitea/cli/issue/label/clear.py +94 -0
- gitea/cli/issue/label/common.py +217 -0
- gitea/cli/issue/label/list.py +87 -0
- gitea/cli/issue/label/main.py +29 -0
- gitea/cli/issue/label/remove.py +99 -0
- gitea/cli/issue/label/set.py +98 -0
- gitea/cli/issue/main.py +2 -0
- gitea/issue/async_issue.py +248 -0
- gitea/issue/base.py +57 -0
- gitea/issue/issue.py +242 -0
- {python_gitea-0.11.7.dist-info → python_gitea-0.12.0.dist-info}/METADATA +1 -1
- {python_gitea-0.11.7.dist-info → python_gitea-0.12.0.dist-info}/RECORD +17 -9
- {python_gitea-0.11.7.dist-info → python_gitea-0.12.0.dist-info}/WHEEL +0 -0
- {python_gitea-0.11.7.dist-info → python_gitea-0.12.0.dist-info}/entry_points.txt +0 -0
- {python_gitea-0.11.7.dist-info → python_gitea-0.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI commands for managing the labels of issues."""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Add issue labels command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from gitea.cli.issue.label.common import ISSUE_IDS_HELP, LABEL_HELP
|
|
10
|
+
from gitea.cli.utils.options import REPOSITORY_REQUIRED_HELP
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def add_command(
|
|
14
|
+
ctx: typer.Context,
|
|
15
|
+
owner: Annotated[str, typer.Option("--owner", help="Owner of the repository.")],
|
|
16
|
+
repository: Annotated[str | None, typer.Option("--repository", help=REPOSITORY_REQUIRED_HELP)] = None,
|
|
17
|
+
issue_ids: Annotated[list[int] | None, typer.Option("--issue-id", help=ISSUE_IDS_HELP)] = None,
|
|
18
|
+
labels: Annotated[list[str] | None, typer.Option("--label", help=LABEL_HELP)] = None,
|
|
19
|
+
account_name: Annotated[
|
|
20
|
+
str | None,
|
|
21
|
+
typer.Option(
|
|
22
|
+
"--account-name",
|
|
23
|
+
help="Name of the account to use for authentication.",
|
|
24
|
+
),
|
|
25
|
+
] = None,
|
|
26
|
+
token: Annotated[
|
|
27
|
+
str | None,
|
|
28
|
+
typer.Option(
|
|
29
|
+
"--token",
|
|
30
|
+
help="Token for authentication. If not provided, the token from the specified account will be used.",
|
|
31
|
+
),
|
|
32
|
+
] = None,
|
|
33
|
+
base_url: Annotated[
|
|
34
|
+
str | None,
|
|
35
|
+
typer.Option(
|
|
36
|
+
"--base-url",
|
|
37
|
+
help="Base URL of the Gitea platform. If not provided, the base URL from the specified account will be used.",
|
|
38
|
+
),
|
|
39
|
+
] = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Add labels to one or more issues, keeping the labels they already have.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
ctx: The Typer context.
|
|
45
|
+
owner: The owner of the repository.
|
|
46
|
+
repository: The name of the repository, which this command requires.
|
|
47
|
+
issue_ids: The issue numbers shown in the web UI.
|
|
48
|
+
labels: The labels, each a name or a numeric ID.
|
|
49
|
+
account_name: Name of the account to use for authentication.
|
|
50
|
+
token: Token for authentication.
|
|
51
|
+
base_url: Base URL of the Gitea platform.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
from typing import Any # noqa: PLC0415
|
|
55
|
+
|
|
56
|
+
from gitea.cli.issue.label.common import ( # noqa: PLC0415
|
|
57
|
+
for_each_issue,
|
|
58
|
+
require_issue_ids,
|
|
59
|
+
require_labels,
|
|
60
|
+
resolve_label_ids,
|
|
61
|
+
)
|
|
62
|
+
from gitea.cli.utils.api import execute_api_command # noqa: PLC0415
|
|
63
|
+
from gitea.cli.utils.auth import get_auth_params # noqa: PLC0415
|
|
64
|
+
from gitea.cli.utils.options import require_repository # noqa: PLC0415
|
|
65
|
+
from gitea.client.gitea import Gitea # noqa: PLC0415
|
|
66
|
+
|
|
67
|
+
command = "gitea-cli issue label add"
|
|
68
|
+
token, base_url = get_auth_params(
|
|
69
|
+
config_path=ctx.obj.get("config_path"),
|
|
70
|
+
account_name=account_name,
|
|
71
|
+
token=token,
|
|
72
|
+
base_url=base_url,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def api_call() -> tuple[dict[str, Any] | list[dict[str, Any]], dict[str, Any]]:
|
|
76
|
+
"""Add the labels to each issue.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A tuple containing the issue's resulting labels, or one entry per issue, and metadata.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
target_repository = require_repository(repository, command=command)
|
|
83
|
+
targets = require_issue_ids(issue_ids, command=command)
|
|
84
|
+
requested = require_labels(labels, command=command)
|
|
85
|
+
|
|
86
|
+
with Gitea(token=token, base_url=base_url) as client:
|
|
87
|
+
label_ids = resolve_label_ids(client, owner, target_repository, requested, command=command)
|
|
88
|
+
# POST appends to the issue's labels; PUT would replace them.
|
|
89
|
+
return for_each_issue(
|
|
90
|
+
targets,
|
|
91
|
+
lambda issue_id: client.issue.add_issue_labels(
|
|
92
|
+
owner=owner, repository=target_repository, index=issue_id, labels=list(label_ids)
|
|
93
|
+
),
|
|
94
|
+
command=command,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
execute_api_command(api_call=api_call, base_url=base_url, command_name=command)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Clear issue labels command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from gitea.cli.issue.label.common import ISSUE_IDS_HELP
|
|
10
|
+
from gitea.cli.utils.options import REPOSITORY_REQUIRED_HELP
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def clear_command(
|
|
14
|
+
ctx: typer.Context,
|
|
15
|
+
owner: Annotated[str, typer.Option("--owner", help="Owner of the repository.")],
|
|
16
|
+
repository: Annotated[str | None, typer.Option("--repository", help=REPOSITORY_REQUIRED_HELP)] = None,
|
|
17
|
+
issue_ids: Annotated[list[int] | None, typer.Option("--issue-id", help=ISSUE_IDS_HELP)] = None,
|
|
18
|
+
account_name: Annotated[
|
|
19
|
+
str | None,
|
|
20
|
+
typer.Option(
|
|
21
|
+
"--account-name",
|
|
22
|
+
help="Name of the account to use for authentication.",
|
|
23
|
+
),
|
|
24
|
+
] = None,
|
|
25
|
+
token: Annotated[
|
|
26
|
+
str | None,
|
|
27
|
+
typer.Option(
|
|
28
|
+
"--token",
|
|
29
|
+
help="Token for authentication. If not provided, the token from the specified account will be used.",
|
|
30
|
+
),
|
|
31
|
+
] = None,
|
|
32
|
+
base_url: Annotated[
|
|
33
|
+
str | None,
|
|
34
|
+
typer.Option(
|
|
35
|
+
"--base-url",
|
|
36
|
+
help="Base URL of the Gitea platform. If not provided, the base URL from the specified account will be used.",
|
|
37
|
+
),
|
|
38
|
+
] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Remove every label from one or more issues.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
ctx: The Typer context.
|
|
44
|
+
owner: The owner of the repository.
|
|
45
|
+
repository: The name of the repository, which this command requires.
|
|
46
|
+
issue_ids: The issue numbers shown in the web UI.
|
|
47
|
+
account_name: Name of the account to use for authentication.
|
|
48
|
+
token: Token for authentication.
|
|
49
|
+
base_url: Base URL of the Gitea platform.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
from typing import Any # noqa: PLC0415
|
|
53
|
+
|
|
54
|
+
from gitea.cli.issue.label.common import for_each_issue, require_issue_ids # noqa: PLC0415
|
|
55
|
+
from gitea.cli.utils.api import execute_api_command # noqa: PLC0415
|
|
56
|
+
from gitea.cli.utils.auth import get_auth_params # noqa: PLC0415
|
|
57
|
+
from gitea.cli.utils.options import require_repository # noqa: PLC0415
|
|
58
|
+
from gitea.client.gitea import Gitea # noqa: PLC0415
|
|
59
|
+
|
|
60
|
+
command = "gitea-cli issue label clear"
|
|
61
|
+
token, base_url = get_auth_params(
|
|
62
|
+
config_path=ctx.obj.get("config_path"),
|
|
63
|
+
account_name=account_name,
|
|
64
|
+
token=token,
|
|
65
|
+
base_url=base_url,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def api_call() -> tuple[dict[str, Any] | list[dict[str, Any]], dict[str, Any]]:
|
|
69
|
+
"""Remove every label from each issue.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A tuple containing the issue's resulting labels, or one entry per issue, and metadata.
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
target_repository = require_repository(repository, command=command)
|
|
76
|
+
targets = require_issue_ids(issue_ids, command=command)
|
|
77
|
+
|
|
78
|
+
def clear(issue_id: int) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
79
|
+
"""Remove every label from one issue, and read back what it is left with.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
issue_id: The issue to clear.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
A tuple containing the issue's resulting labels and metadata.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
client.issue.clear_issue_labels(owner=owner, repository=target_repository, index=issue_id)
|
|
89
|
+
return client.issue.list_issue_labels(owner=owner, repository=target_repository, index=issue_id)
|
|
90
|
+
|
|
91
|
+
with Gitea(token=token, base_url=base_url) as client:
|
|
92
|
+
return for_each_issue(targets, clear, command=command)
|
|
93
|
+
|
|
94
|
+
execute_api_command(api_call=api_call, base_url=base_url, command_name=command)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""What the `issue label` commands share: reading labels and issues, and reporting per issue.
|
|
2
|
+
|
|
3
|
+
A label is named on the command line by its name or by its numeric ID. Names are
|
|
4
|
+
resolved to IDs here, through the repository's label listing, rather than sent to
|
|
5
|
+
the API as they are: only a recent Gitea accepts names in an issue's label list,
|
|
6
|
+
and one that does not rejects the whole request. Resolving first also means an
|
|
7
|
+
unknown name is an error before anything is written, rather than a label silently
|
|
8
|
+
not applied, and before the first of several issues has been changed.
|
|
9
|
+
|
|
10
|
+
A value made only of ASCII digits is taken to be an ID and is sent as it is,
|
|
11
|
+
without being looked up. That is what lets a label defined on the organization,
|
|
12
|
+
which the repository's listing does not include, be applied at all.
|
|
13
|
+
|
|
14
|
+
Every command takes one `--issue-id` or several. With one, `data` is that issue's
|
|
15
|
+
labels, as `gitea-cli issue label list` emits them. With several, `data` holds one
|
|
16
|
+
entry per issue, `{"issue_id": ..., "labels": [...]}`, in the order the issues were
|
|
17
|
+
given, so that which issue was left with which labels can be read back. The shape
|
|
18
|
+
follows from how the command was invoked, never from what the API answered.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import re
|
|
25
|
+
from collections.abc import Callable
|
|
26
|
+
from typing import TYPE_CHECKING, Any
|
|
27
|
+
|
|
28
|
+
from gitea.cli.utils.errors import CommandError
|
|
29
|
+
from gitea.utils.pagination import PAGE_SIZE, collect_all_pages
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from gitea.client.gitea import Gitea
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger("gitea")
|
|
35
|
+
|
|
36
|
+
ISSUE_IDS_HELP = "Issue number shown in the web UI. Repeat the option to act on several issues."
|
|
37
|
+
LABEL_HELP = "Label name or numeric ID. Repeat the option for several labels."
|
|
38
|
+
|
|
39
|
+
_NUMERIC_ID = re.compile(r"[0-9]+")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def require_issue_ids(issue_ids: list[int] | None, *, command: str) -> list[int]:
|
|
43
|
+
"""Read the issues a command acts on.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
issue_ids: The values passed as --issue-id, or None when it was omitted.
|
|
47
|
+
command: The command being run, named as the user invoked it.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The issue numbers, in the order given, each once.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
CommandError: If no issue was given.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
if not issue_ids:
|
|
57
|
+
raise CommandError(f"'{command}' needs an issue: pass --issue-id NUMBER, once per issue.")
|
|
58
|
+
return list(dict.fromkeys(issue_ids))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def require_labels(labels: list[str] | None, *, command: str, hint: str = "") -> list[str]:
|
|
62
|
+
"""Read the labels a command acts on.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
labels: The values passed as --label, or None when it was omitted.
|
|
66
|
+
command: The command being run, named as the user invoked it.
|
|
67
|
+
hint: A sentence appended to the error, pointing at a command that needs no label.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
The labels, as they were given.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
CommandError: If no label was given.
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
if not labels:
|
|
77
|
+
message = f"'{command}' needs a label: pass --label NAME_OR_ID, once per label."
|
|
78
|
+
raise CommandError(f"{message} {hint}" if hint else message)
|
|
79
|
+
return labels
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_label_ids(client: Gitea, owner: str, repository: str, labels: list[str], *, command: str) -> list[int]:
|
|
83
|
+
"""Resolve the labels given on the command line to their IDs.
|
|
84
|
+
|
|
85
|
+
The repository's labels are listed only when a name has to be resolved, and
|
|
86
|
+
then only once for all of them.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
client: The client to list the repository's labels with.
|
|
90
|
+
owner: The owner of the repository.
|
|
91
|
+
repository: The name of the repository.
|
|
92
|
+
labels: The labels, each a name or a numeric ID.
|
|
93
|
+
command: The command being run, named as the user invoked it.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
The label IDs, in the order given, each once.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
CommandError: If a name matches no label of the repository, or more than one.
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
names = [label for label in labels if not _NUMERIC_ID.fullmatch(label)]
|
|
103
|
+
by_name: dict[str, list[int]] = {}
|
|
104
|
+
if names:
|
|
105
|
+
defined, _ = collect_all_pages(
|
|
106
|
+
lambda page: client.label.list_labels(owner=owner, repository=repository, page=page, limit=PAGE_SIZE)
|
|
107
|
+
)
|
|
108
|
+
for label in defined:
|
|
109
|
+
by_name.setdefault(label["name"], []).append(label["id"])
|
|
110
|
+
|
|
111
|
+
unknown = [name for name in names if name not in by_name]
|
|
112
|
+
if unknown:
|
|
113
|
+
raise CommandError(
|
|
114
|
+
f"'{command}': no label named {', '.join(repr(name) for name in unknown)} in {owner}/{repository}. "
|
|
115
|
+
f"Names are matched exactly, case included; 'gitea-cli label list' shows the repository's labels. "
|
|
116
|
+
f"A label defined on the organization is not looked up by name: pass its numeric ID."
|
|
117
|
+
)
|
|
118
|
+
ambiguous = [name for name in names if len(by_name[name]) > 1]
|
|
119
|
+
if ambiguous:
|
|
120
|
+
raise CommandError(
|
|
121
|
+
f"'{command}': more than one label is named {', '.join(repr(name) for name in ambiguous)} "
|
|
122
|
+
f"in {owner}/{repository}. Pass the numeric ID of the one you mean."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
ids = [int(label) if _NUMERIC_ID.fullmatch(label) else by_name[label][0] for label in labels]
|
|
126
|
+
return list(dict.fromkeys(ids))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def for_each_issue(
|
|
130
|
+
issue_ids: list[int],
|
|
131
|
+
action: Callable[[int], tuple[list[dict[str, Any]], dict[str, Any]]],
|
|
132
|
+
*,
|
|
133
|
+
command: str,
|
|
134
|
+
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
135
|
+
"""Run a command on each issue in turn and shape what it reports.
|
|
136
|
+
|
|
137
|
+
The issues are acted on one after another, so a failure part-way leaves the
|
|
138
|
+
ones before it changed. The error then names them, since the command prints
|
|
139
|
+
nothing on stdout when it fails and the caller could not tell otherwise.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
issue_ids: The issues to act on.
|
|
143
|
+
action: Callable acting on one issue and returning its resulting labels
|
|
144
|
+
and the metadata of the last response. Extra keys in the metadata
|
|
145
|
+
beyond `status_code` are reported with that issue.
|
|
146
|
+
command: The command being run, named as the user invoked it.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
A tuple containing the data and the metadata of the envelope: for one
|
|
150
|
+
issue, its labels and its metadata; for several, one entry per issue.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
CommandError: If acting on an issue after the first failed. A failure
|
|
154
|
+
on the first issue is raised as it was, since nothing else changed.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
entries: list[dict[str, Any]] = []
|
|
158
|
+
metadata: dict[str, Any] = {}
|
|
159
|
+
for issue_id in issue_ids:
|
|
160
|
+
try:
|
|
161
|
+
labels, metadata = action(issue_id)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
if not entries:
|
|
164
|
+
raise
|
|
165
|
+
done = ", ".join(str(entry["issue_id"]) for entry in entries)
|
|
166
|
+
raise CommandError(
|
|
167
|
+
f"'{command}' failed on issue {issue_id}: {e}. "
|
|
168
|
+
f"Issues {done} were already done, and issue {issue_id} may be partly done; "
|
|
169
|
+
f"the rest were not attempted."
|
|
170
|
+
) from e
|
|
171
|
+
extra = {key: value for key, value in metadata.items() if key != "status_code"}
|
|
172
|
+
entries.append({"issue_id": issue_id, "labels": labels, **extra})
|
|
173
|
+
|
|
174
|
+
if len(entries) == 1:
|
|
175
|
+
return entries[0]["labels"], metadata
|
|
176
|
+
return entries, {"status_code": metadata["status_code"]}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def remove_carried_labels(
|
|
180
|
+
client: Gitea, owner: str, repository: str, issue_id: int, label_ids: list[int]
|
|
181
|
+
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
182
|
+
"""Remove from one issue those of the labels it carries, and read back what it is left with.
|
|
183
|
+
|
|
184
|
+
A label the issue does not carry is not an error: removing it would leave the
|
|
185
|
+
issue as it is either way, and a command run over several issues meets that
|
|
186
|
+
routinely. It is not silent either: it is logged, and named in the metadata
|
|
187
|
+
as `not_on_issue`, so a caller can tell a removal from a label that was never
|
|
188
|
+
there.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
client: The client to act with.
|
|
192
|
+
owner: The owner of the repository.
|
|
193
|
+
repository: The name of the repository.
|
|
194
|
+
issue_id: The issue to remove the labels from.
|
|
195
|
+
label_ids: The IDs of the labels to remove.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
A tuple containing the issue's resulting labels, and metadata naming the
|
|
199
|
+
IDs of the requested labels it did not carry.
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
current, _ = client.issue.list_issue_labels(owner=owner, repository=repository, index=issue_id)
|
|
203
|
+
carried = {label["id"] for label in current}
|
|
204
|
+
absent = [label_id for label_id in label_ids if label_id not in carried]
|
|
205
|
+
if absent:
|
|
206
|
+
logger.warning(
|
|
207
|
+
"Issue %s does not carry label %s; left as it is.",
|
|
208
|
+
issue_id,
|
|
209
|
+
", ".join(str(label_id) for label_id in absent),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
for label_id in label_ids:
|
|
213
|
+
if label_id in carried:
|
|
214
|
+
client.issue.remove_issue_label(owner=owner, repository=repository, index=issue_id, label=label_id)
|
|
215
|
+
|
|
216
|
+
remaining, metadata = client.issue.list_issue_labels(owner=owner, repository=repository, index=issue_id)
|
|
217
|
+
return remaining, {**metadata, "not_on_issue": absent}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""List issue labels command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from gitea.cli.issue.label.common import ISSUE_IDS_HELP
|
|
10
|
+
from gitea.cli.utils.options import REPOSITORY_REQUIRED_HELP
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def list_command(
|
|
14
|
+
ctx: typer.Context,
|
|
15
|
+
owner: Annotated[str, typer.Option("--owner", help="Owner of the repository.")],
|
|
16
|
+
repository: Annotated[str | None, typer.Option("--repository", help=REPOSITORY_REQUIRED_HELP)] = None,
|
|
17
|
+
issue_ids: Annotated[list[int] | None, typer.Option("--issue-id", help=ISSUE_IDS_HELP)] = None,
|
|
18
|
+
account_name: Annotated[
|
|
19
|
+
str | None,
|
|
20
|
+
typer.Option(
|
|
21
|
+
"--account-name",
|
|
22
|
+
help="Name of the account to use for authentication.",
|
|
23
|
+
),
|
|
24
|
+
] = None,
|
|
25
|
+
token: Annotated[
|
|
26
|
+
str | None,
|
|
27
|
+
typer.Option(
|
|
28
|
+
"--token",
|
|
29
|
+
help="Token for authentication. If not provided, the token from the specified account will be used.",
|
|
30
|
+
),
|
|
31
|
+
] = None,
|
|
32
|
+
base_url: Annotated[
|
|
33
|
+
str | None,
|
|
34
|
+
typer.Option(
|
|
35
|
+
"--base-url",
|
|
36
|
+
help="Base URL of the Gitea platform. If not provided, the base URL from the specified account will be used.",
|
|
37
|
+
),
|
|
38
|
+
] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""List the labels of one or more issues.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
ctx: The Typer context.
|
|
44
|
+
owner: The owner of the repository.
|
|
45
|
+
repository: The name of the repository, which this command requires.
|
|
46
|
+
issue_ids: The issue numbers shown in the web UI.
|
|
47
|
+
account_name: Name of the account to use for authentication.
|
|
48
|
+
token: Token for authentication.
|
|
49
|
+
base_url: Base URL of the Gitea platform.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
from typing import Any # noqa: PLC0415
|
|
53
|
+
|
|
54
|
+
from gitea.cli.issue.label.common import for_each_issue, require_issue_ids # noqa: PLC0415
|
|
55
|
+
from gitea.cli.utils.api import execute_api_command # noqa: PLC0415
|
|
56
|
+
from gitea.cli.utils.auth import get_auth_params # noqa: PLC0415
|
|
57
|
+
from gitea.cli.utils.options import require_repository # noqa: PLC0415
|
|
58
|
+
from gitea.client.gitea import Gitea # noqa: PLC0415
|
|
59
|
+
|
|
60
|
+
command = "gitea-cli issue label list"
|
|
61
|
+
token, base_url = get_auth_params(
|
|
62
|
+
config_path=ctx.obj.get("config_path"),
|
|
63
|
+
account_name=account_name,
|
|
64
|
+
token=token,
|
|
65
|
+
base_url=base_url,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def api_call() -> tuple[dict[str, Any] | list[dict[str, Any]], dict[str, Any]]:
|
|
69
|
+
"""List the labels of each issue.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A tuple containing the issue's resulting labels, or one entry per issue, and metadata.
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
target_repository = require_repository(repository, command=command)
|
|
76
|
+
targets = require_issue_ids(issue_ids, command=command)
|
|
77
|
+
|
|
78
|
+
with Gitea(token=token, base_url=base_url) as client:
|
|
79
|
+
return for_each_issue(
|
|
80
|
+
targets,
|
|
81
|
+
lambda issue_id: client.issue.list_issue_labels(
|
|
82
|
+
owner=owner, repository=target_repository, index=issue_id
|
|
83
|
+
),
|
|
84
|
+
command=command,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
execute_api_command(api_call=api_call, base_url=base_url, command_name=command)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""CLI commands for managing the labels of issues."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
label_app = typer.Typer(
|
|
8
|
+
name="label",
|
|
9
|
+
help="Commands for managing the labels of issues.",
|
|
10
|
+
rich_markup_mode="rich",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_commands() -> None:
|
|
15
|
+
"""Register issue-label commands to the label_app."""
|
|
16
|
+
from gitea.cli.issue.label.add import add_command # noqa: PLC0415
|
|
17
|
+
from gitea.cli.issue.label.clear import clear_command # noqa: PLC0415
|
|
18
|
+
from gitea.cli.issue.label.list import list_command # noqa: PLC0415
|
|
19
|
+
from gitea.cli.issue.label.remove import remove_command # noqa: PLC0415
|
|
20
|
+
from gitea.cli.issue.label.set import set_command # noqa: PLC0415
|
|
21
|
+
|
|
22
|
+
label_app.command("add", help="Add labels to issues, keeping the labels they already have.")(add_command)
|
|
23
|
+
label_app.command("clear", help="Remove every label from issues.")(clear_command)
|
|
24
|
+
label_app.command("list", help="List the labels of issues.")(list_command)
|
|
25
|
+
label_app.command("remove", help="Remove labels from issues.")(remove_command)
|
|
26
|
+
label_app.command("set", help="Replace every label of issues with the given labels.")(set_command)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
register_commands()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Remove issue labels command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from gitea.cli.issue.label.common import ISSUE_IDS_HELP, LABEL_HELP
|
|
10
|
+
from gitea.cli.utils.options import REPOSITORY_REQUIRED_HELP
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def remove_command(
|
|
14
|
+
ctx: typer.Context,
|
|
15
|
+
owner: Annotated[str, typer.Option("--owner", help="Owner of the repository.")],
|
|
16
|
+
repository: Annotated[str | None, typer.Option("--repository", help=REPOSITORY_REQUIRED_HELP)] = None,
|
|
17
|
+
issue_ids: Annotated[list[int] | None, typer.Option("--issue-id", help=ISSUE_IDS_HELP)] = None,
|
|
18
|
+
labels: Annotated[list[str] | None, typer.Option("--label", help=LABEL_HELP)] = None,
|
|
19
|
+
account_name: Annotated[
|
|
20
|
+
str | None,
|
|
21
|
+
typer.Option(
|
|
22
|
+
"--account-name",
|
|
23
|
+
help="Name of the account to use for authentication.",
|
|
24
|
+
),
|
|
25
|
+
] = None,
|
|
26
|
+
token: Annotated[
|
|
27
|
+
str | None,
|
|
28
|
+
typer.Option(
|
|
29
|
+
"--token",
|
|
30
|
+
help="Token for authentication. If not provided, the token from the specified account will be used.",
|
|
31
|
+
),
|
|
32
|
+
] = None,
|
|
33
|
+
base_url: Annotated[
|
|
34
|
+
str | None,
|
|
35
|
+
typer.Option(
|
|
36
|
+
"--base-url",
|
|
37
|
+
help="Base URL of the Gitea platform. If not provided, the base URL from the specified account will be used.",
|
|
38
|
+
),
|
|
39
|
+
] = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Remove labels from one or more issues.
|
|
42
|
+
|
|
43
|
+
A label an issue does not carry is left alone, logged as a warning, and
|
|
44
|
+
named in the issue's `not_on_issue` metadata.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
ctx: The Typer context.
|
|
48
|
+
owner: The owner of the repository.
|
|
49
|
+
repository: The name of the repository, which this command requires.
|
|
50
|
+
issue_ids: The issue numbers shown in the web UI.
|
|
51
|
+
labels: The labels, each a name or a numeric ID.
|
|
52
|
+
account_name: Name of the account to use for authentication.
|
|
53
|
+
token: Token for authentication.
|
|
54
|
+
base_url: Base URL of the Gitea platform.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
from typing import Any # noqa: PLC0415
|
|
58
|
+
|
|
59
|
+
from gitea.cli.issue.label.common import ( # noqa: PLC0415
|
|
60
|
+
for_each_issue,
|
|
61
|
+
remove_carried_labels,
|
|
62
|
+
require_issue_ids,
|
|
63
|
+
require_labels,
|
|
64
|
+
resolve_label_ids,
|
|
65
|
+
)
|
|
66
|
+
from gitea.cli.utils.api import execute_api_command # noqa: PLC0415
|
|
67
|
+
from gitea.cli.utils.auth import get_auth_params # noqa: PLC0415
|
|
68
|
+
from gitea.cli.utils.options import require_repository # noqa: PLC0415
|
|
69
|
+
from gitea.client.gitea import Gitea # noqa: PLC0415
|
|
70
|
+
|
|
71
|
+
command = "gitea-cli issue label remove"
|
|
72
|
+
token, base_url = get_auth_params(
|
|
73
|
+
config_path=ctx.obj.get("config_path"),
|
|
74
|
+
account_name=account_name,
|
|
75
|
+
token=token,
|
|
76
|
+
base_url=base_url,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def api_call() -> tuple[dict[str, Any] | list[dict[str, Any]], dict[str, Any]]:
|
|
80
|
+
"""Remove the labels from each issue.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
A tuple containing the issue's resulting labels, or one entry per issue, and metadata.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
target_repository = require_repository(repository, command=command)
|
|
87
|
+
targets = require_issue_ids(issue_ids, command=command)
|
|
88
|
+
requested = require_labels(labels, command=command)
|
|
89
|
+
|
|
90
|
+
with Gitea(token=token, base_url=base_url) as client:
|
|
91
|
+
label_ids = resolve_label_ids(client, owner, target_repository, requested, command=command)
|
|
92
|
+
|
|
93
|
+
return for_each_issue(
|
|
94
|
+
targets,
|
|
95
|
+
lambda issue_id: remove_carried_labels(client, owner, target_repository, issue_id, label_ids),
|
|
96
|
+
command=command,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
execute_api_command(api_call=api_call, base_url=base_url, command_name=command)
|