zt-devops-cli 0.2.2__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.2 → zt_devops_cli-0.2.3}/PKG-INFO +1 -1
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/pyproject.toml +1 -1
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli/cli.py +115 -30
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/PKG-INFO +1 -1
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/README.md +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/setup.cfg +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli/__init__.py +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli/api.py +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli/auth.py +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli/config.py +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/SOURCES.txt +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/dependency_links.txt +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/entry_points.txt +0 -0
- {zt_devops_cli-0.2.2 → zt_devops_cli-0.2.3}/src/zt_devops_cli.egg-info/requires.txt +0 -0
- {zt_devops_cli-0.2.2 → 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(
|
|
@@ -191,15 +270,19 @@ def create_sprint(ctx, project, title, start_date, end_date, purpose, test_start
|
|
|
191
270
|
test_end_date=test_end,
|
|
192
271
|
)
|
|
193
272
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
+
)
|
|
198
282
|
|
|
199
283
|
except Exception as e:
|
|
200
284
|
click.echo(f"错误: {e}", err=True)
|
|
201
285
|
|
|
202
|
-
|
|
203
286
|
@sprint.command("start")
|
|
204
287
|
@click.option("--project", "-p", default=None, help="项目 ID")
|
|
205
288
|
@click.option("--sprint-id", "-i", required=True, help="迭代 ID")
|
|
@@ -225,7 +308,7 @@ def start_sprint(ctx, project, sprint_id):
|
|
|
225
308
|
return
|
|
226
309
|
|
|
227
310
|
result = api.start_sprint(sprint_data)
|
|
228
|
-
|
|
311
|
+
_print_action_result(ctx, result, "启用成功!", "启用失败")
|
|
229
312
|
except Exception as e:
|
|
230
313
|
click.echo(f"错误: {e}", err=True)
|
|
231
314
|
|
|
@@ -246,8 +329,8 @@ def delete_sprint(ctx, project, sprint_id):
|
|
|
246
329
|
return
|
|
247
330
|
|
|
248
331
|
try:
|
|
249
|
-
api.delete_sprint(sprint_id)
|
|
250
|
-
|
|
332
|
+
result = api.delete_sprint(sprint_id)
|
|
333
|
+
_print_action_result(ctx, result, "删除成功!", "删除失败")
|
|
251
334
|
except Exception as e:
|
|
252
335
|
click.echo(f"错误: {e}", err=True)
|
|
253
336
|
|
|
@@ -268,8 +351,8 @@ def done_sprint(ctx, project, sprint_id):
|
|
|
268
351
|
return
|
|
269
352
|
|
|
270
353
|
try:
|
|
271
|
-
api.done_sprint(sprint_id)
|
|
272
|
-
|
|
354
|
+
result = api.done_sprint(sprint_id)
|
|
355
|
+
_print_action_result(ctx, result, "完成成功!", "完成失败")
|
|
273
356
|
except Exception as e:
|
|
274
357
|
click.echo(f"错误: {e}", err=True)
|
|
275
358
|
|
|
@@ -1090,11 +1173,15 @@ def create_bug(
|
|
|
1090
1173
|
demand_classify=demand_classify,
|
|
1091
1174
|
instance_values=instance_values,
|
|
1092
1175
|
)
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
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
|
+
)
|
|
1098
1185
|
except Exception as e:
|
|
1099
1186
|
click.echo(f"错误: {e}", err=True)
|
|
1100
1187
|
|
|
@@ -1139,10 +1226,7 @@ def _bug_direction_go(ctx, project, bug_id, target_key, comment, operators):
|
|
|
1139
1226
|
operators=op_list,
|
|
1140
1227
|
comment=comment,
|
|
1141
1228
|
)
|
|
1142
|
-
|
|
1143
|
-
click.echo(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
1144
|
-
return
|
|
1145
|
-
click.echo("流转请求已提交")
|
|
1229
|
+
_print_action_result(ctx, result, "流转请求已提交", "流转失败")
|
|
1146
1230
|
except Exception as e:
|
|
1147
1231
|
click.echo(f"错误: {e}", err=True)
|
|
1148
1232
|
|
|
@@ -1258,10 +1342,7 @@ def bug_delete(ctx, project, bug_id, yes):
|
|
|
1258
1342
|
api = DevOpsAPI(project_id)
|
|
1259
1343
|
try:
|
|
1260
1344
|
result = api.delete_issue(bug_id)
|
|
1261
|
-
|
|
1262
|
-
click.echo(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
|
1263
|
-
return
|
|
1264
|
-
click.echo("删除成功")
|
|
1345
|
+
_print_action_result(ctx, result, "删除成功", "删除失败")
|
|
1265
1346
|
except Exception as e:
|
|
1266
1347
|
click.echo(f"错误: {e}", err=True)
|
|
1267
1348
|
|
|
@@ -1334,15 +1415,19 @@ def create_task(
|
|
|
1334
1415
|
demand_classify=demand_classify,
|
|
1335
1416
|
instance_values=instance_values,
|
|
1336
1417
|
)
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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
|
+
|
|
1342
1428
|
except Exception as e:
|
|
1343
1429
|
click.echo(f"错误: {e}", err=True)
|
|
1344
1430
|
|
|
1345
|
-
|
|
1346
1431
|
def main():
|
|
1347
1432
|
cli()
|
|
1348
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
|