zt-devops-cli 0.2.1__tar.gz → 0.2.3__tar.gz
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.
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/PKG-INFO +1 -1
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/pyproject.toml +1 -1
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli/cli.py +117 -27
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/PKG-INFO +1 -1
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/README.md +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/setup.cfg +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli/__init__.py +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli/api.py +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli/auth.py +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli/config.py +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/SOURCES.txt +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/dependency_links.txt +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/entry_points.txt +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/requires.txt +0 -0
- {zt_devops_cli-0.2.1 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import click
|
|
4
4
|
import json
|
|
5
5
|
import shutil
|
|
6
|
-
from typing import Optional
|
|
6
|
+
from typing import Callable, Optional
|
|
7
7
|
|
|
8
8
|
from .api import BUG_DIRECTION_NEXT_NODE_IDS, DevOpsAPI
|
|
9
9
|
from .auth import login
|
|
@@ -98,6 +98,85 @@ def render_table(headers, rows, max_width=120, no_truncate_headers=None):
|
|
|
98
98
|
click.echo(border)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
def _extract_api_message(payload) -> str:
|
|
102
|
+
if not isinstance(payload, dict):
|
|
103
|
+
return ""
|
|
104
|
+
for key in ("message", "msg", "errorMsg", "error"):
|
|
105
|
+
value = payload.get(key)
|
|
106
|
+
if isinstance(value, str) and value.strip():
|
|
107
|
+
return value.strip()
|
|
108
|
+
return ""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _is_api_success(payload) -> bool:
|
|
112
|
+
"""兼容不同后端返回结构,判断请求是否成功。"""
|
|
113
|
+
if payload is None:
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
# HTTP 2xx 但无 body 的场景,_request 会返回 {}
|
|
117
|
+
if isinstance(payload, dict) and not payload:
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
if not isinstance(payload, dict):
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
for key in ("success", "ok"):
|
|
124
|
+
if key in payload:
|
|
125
|
+
return bool(payload.get(key))
|
|
126
|
+
|
|
127
|
+
code = payload.get("code")
|
|
128
|
+
if code is not None:
|
|
129
|
+
if str(code).strip() in {"0", "200"}:
|
|
130
|
+
return True
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
status = payload.get("status")
|
|
134
|
+
if isinstance(status, str):
|
|
135
|
+
norm_status = status.strip().lower()
|
|
136
|
+
if norm_status in {"success", "ok", "succeeded"}:
|
|
137
|
+
return True
|
|
138
|
+
if norm_status in {"fail", "failed", "error"}:
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
message = _extract_api_message(payload).lower()
|
|
142
|
+
if message:
|
|
143
|
+
if any(word in message for word in ("失败", "错误", "error", "fail")):
|
|
144
|
+
return False
|
|
145
|
+
if any(word in message for word in ("成功", "success")):
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
if "data" in payload:
|
|
149
|
+
return payload.get("data") is not None
|
|
150
|
+
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _print_action_result(
|
|
155
|
+
ctx,
|
|
156
|
+
result,
|
|
157
|
+
success_text: str = "",
|
|
158
|
+
fail_prefix: str = "操作失败",
|
|
159
|
+
success_checker: Optional[Callable[[dict], bool]] = None,
|
|
160
|
+
success_text_builder: Optional[Callable[[dict], str]] = None,
|
|
161
|
+
) -> bool:
|
|
162
|
+
"""统一处理命令结果输出;返回是否成功。"""
|
|
163
|
+
checker = success_checker or _is_api_success
|
|
164
|
+
|
|
165
|
+
if ctx.obj.get("output_format") == "json":
|
|
166
|
+
click.echo(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
167
|
+
return checker(result)
|
|
168
|
+
|
|
169
|
+
if checker(result):
|
|
170
|
+
text = success_text_builder(result) if success_text_builder else success_text
|
|
171
|
+
if text:
|
|
172
|
+
click.echo(text)
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
msg = _extract_api_message(result) or "未返回成功状态"
|
|
176
|
+
click.echo(f"{fail_prefix}: {msg}", err=True)
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
101
180
|
@click.group()
|
|
102
181
|
@click.option("--project", "-p", default=None, help="项目 ID")
|
|
103
182
|
@click.option(
|
|
@@ -190,11 +269,20 @@ def create_sprint(ctx, project, title, start_date, end_date, purpose, test_start
|
|
|
190
269
|
test_start_date=test_start,
|
|
191
270
|
test_end_date=test_end,
|
|
192
271
|
)
|
|
193
|
-
|
|
272
|
+
|
|
273
|
+
_print_action_result(
|
|
274
|
+
ctx,
|
|
275
|
+
result,
|
|
276
|
+
fail_prefix="创建失败",
|
|
277
|
+
success_checker=lambda payload: bool(
|
|
278
|
+
payload.get("data", {}).get("id")
|
|
279
|
+
) if isinstance(payload, dict) else False,
|
|
280
|
+
success_text_builder=lambda payload: f"创建成功! 迭代 ID: {payload.get('data', {}).get('id')}",
|
|
281
|
+
)
|
|
282
|
+
|
|
194
283
|
except Exception as e:
|
|
195
284
|
click.echo(f"错误: {e}", err=True)
|
|
196
285
|
|
|
197
|
-
|
|
198
286
|
@sprint.command("start")
|
|
199
287
|
@click.option("--project", "-p", default=None, help="项目 ID")
|
|
200
288
|
@click.option("--sprint-id", "-i", required=True, help="迭代 ID")
|
|
@@ -220,7 +308,7 @@ def start_sprint(ctx, project, sprint_id):
|
|
|
220
308
|
return
|
|
221
309
|
|
|
222
310
|
result = api.start_sprint(sprint_data)
|
|
223
|
-
|
|
311
|
+
_print_action_result(ctx, result, "启用成功!", "启用失败")
|
|
224
312
|
except Exception as e:
|
|
225
313
|
click.echo(f"错误: {e}", err=True)
|
|
226
314
|
|
|
@@ -241,8 +329,8 @@ def delete_sprint(ctx, project, sprint_id):
|
|
|
241
329
|
return
|
|
242
330
|
|
|
243
331
|
try:
|
|
244
|
-
api.delete_sprint(sprint_id)
|
|
245
|
-
|
|
332
|
+
result = api.delete_sprint(sprint_id)
|
|
333
|
+
_print_action_result(ctx, result, "删除成功!", "删除失败")
|
|
246
334
|
except Exception as e:
|
|
247
335
|
click.echo(f"错误: {e}", err=True)
|
|
248
336
|
|
|
@@ -263,8 +351,8 @@ def done_sprint(ctx, project, sprint_id):
|
|
|
263
351
|
return
|
|
264
352
|
|
|
265
353
|
try:
|
|
266
|
-
api.done_sprint(sprint_id)
|
|
267
|
-
|
|
354
|
+
result = api.done_sprint(sprint_id)
|
|
355
|
+
_print_action_result(ctx, result, "完成成功!", "完成失败")
|
|
268
356
|
except Exception as e:
|
|
269
357
|
click.echo(f"错误: {e}", err=True)
|
|
270
358
|
|
|
@@ -1085,11 +1173,15 @@ def create_bug(
|
|
|
1085
1173
|
demand_classify=demand_classify,
|
|
1086
1174
|
instance_values=instance_values,
|
|
1087
1175
|
)
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1176
|
+
_print_action_result(
|
|
1177
|
+
ctx,
|
|
1178
|
+
result,
|
|
1179
|
+
fail_prefix="创建失败",
|
|
1180
|
+
success_checker=lambda payload: bool(
|
|
1181
|
+
payload.get("data", {}).get("id")
|
|
1182
|
+
) if isinstance(payload, dict) else False,
|
|
1183
|
+
success_text_builder=lambda payload: f"创建成功! 缺陷 ID: {payload.get('data', {}).get('id')}",
|
|
1184
|
+
)
|
|
1093
1185
|
except Exception as e:
|
|
1094
1186
|
click.echo(f"错误: {e}", err=True)
|
|
1095
1187
|
|
|
@@ -1134,10 +1226,7 @@ def _bug_direction_go(ctx, project, bug_id, target_key, comment, operators):
|
|
|
1134
1226
|
operators=op_list,
|
|
1135
1227
|
comment=comment,
|
|
1136
1228
|
)
|
|
1137
|
-
|
|
1138
|
-
click.echo(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
1139
|
-
return
|
|
1140
|
-
click.echo("流转请求已提交")
|
|
1229
|
+
_print_action_result(ctx, result, "流转请求已提交", "流转失败")
|
|
1141
1230
|
except Exception as e:
|
|
1142
1231
|
click.echo(f"错误: {e}", err=True)
|
|
1143
1232
|
|
|
@@ -1253,10 +1342,7 @@ def bug_delete(ctx, project, bug_id, yes):
|
|
|
1253
1342
|
api = DevOpsAPI(project_id)
|
|
1254
1343
|
try:
|
|
1255
1344
|
result = api.delete_issue(bug_id)
|
|
1256
|
-
|
|
1257
|
-
click.echo(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
1258
|
-
return
|
|
1259
|
-
click.echo("删除成功")
|
|
1345
|
+
_print_action_result(ctx, result, "删除成功", "删除失败")
|
|
1260
1346
|
except Exception as e:
|
|
1261
1347
|
click.echo(f"错误: {e}", err=True)
|
|
1262
1348
|
|
|
@@ -1329,15 +1415,19 @@ def create_task(
|
|
|
1329
1415
|
demand_classify=demand_classify,
|
|
1330
1416
|
instance_values=instance_values,
|
|
1331
1417
|
)
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1418
|
+
_print_action_result(
|
|
1419
|
+
ctx,
|
|
1420
|
+
result,
|
|
1421
|
+
fail_prefix="创建失败",
|
|
1422
|
+
success_checker=lambda payload: bool(
|
|
1423
|
+
payload.get("data", {}).get("id")
|
|
1424
|
+
) if isinstance(payload, dict) else False,
|
|
1425
|
+
success_text_builder=lambda payload: f"创建成功! 任务 ID: {payload.get('data', {}).get('id')}",
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1337
1428
|
except Exception as e:
|
|
1338
1429
|
click.echo(f"错误: {e}", err=True)
|
|
1339
1430
|
|
|
1340
|
-
|
|
1341
1431
|
def main():
|
|
1342
1432
|
cli()
|
|
1343
1433
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|