zt-devops-cli 0.2.3__tar.gz → 0.2.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zt-devops-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: DevOps 平台迭代管理 CLI
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zt-devops-cli"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "DevOps 平台迭代管理 CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -346,6 +346,13 @@ class DevOpsAPI:
346
346
  data=data,
347
347
  )
348
348
 
349
+ def get_bug(self, bug_id: str) -> dict:
350
+ """查询单个工作项详情(GET issue/{projectId}/{issueId},缺陷详情页同源接口)。"""
351
+ return self._request(
352
+ "GET",
353
+ f"/ms/vteam/api/user/issue/{self.project_id}/{bug_id}",
354
+ )
355
+
349
356
  def bug_direction_next(
350
357
  self,
351
358
  issue_id: str,
@@ -152,12 +152,12 @@ def _is_api_success(payload) -> bool:
152
152
 
153
153
 
154
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,
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
161
  ) -> bool:
162
162
  """统一处理命令结果输出;返回是否成功。"""
163
163
  checker = success_checker or _is_api_success
@@ -249,8 +249,8 @@ def zteam_project():
249
249
  @click.option("--start-date", "-s", required=True, help="开始日期 (YYYY-MM-DD)")
250
250
  @click.option("--end-date", "-e", required=True, help="结束日期 (YYYY-MM-DD)")
251
251
  @click.option("--purpose", required=True, help="迭代目标")
252
- @click.option("--test-start", required=True, help="测试开始日期")
253
- @click.option("--test-end",required=True, help="测试结束日期")
252
+ @click.option("--test-start", required=True, help="测试开始日期")
253
+ @click.option("--test-end", required=True, help="测试结束日期")
254
254
  @click.pass_context
255
255
  def create_sprint(ctx, project, title, start_date, end_date, purpose, test_start, test_end):
256
256
  """创建迭代"""
@@ -283,6 +283,7 @@ def create_sprint(ctx, project, title, start_date, end_date, purpose, test_start
283
283
  except Exception as e:
284
284
  click.echo(f"错误: {e}", err=True)
285
285
 
286
+
286
287
  @sprint.command("start")
287
288
  @click.option("--project", "-p", default=None, help="项目 ID")
288
289
  @click.option("--sprint-id", "-i", required=True, help="迭代 ID")
@@ -881,6 +882,79 @@ def list_bugs(
881
882
  click.echo(f"错误: {e}", err=True)
882
883
 
883
884
 
885
+ def _flatten_issue_detail_for_table(detail):
886
+ """将详情接口返回的 data 展平为 {字段: 显示值},供 human 表格使用。"""
887
+ if not isinstance(detail, dict):
888
+ return {}
889
+
890
+ def _unwrap_value(v):
891
+ if not isinstance(v, dict):
892
+ return v
893
+ for key in ("value", "label", "name", "displayName", "text", "title"):
894
+ if key in v and v.get(key) not in (None, ""):
895
+ return v.get(key)
896
+ return json.dumps(v, ensure_ascii=False)
897
+
898
+ rows = {}
899
+ prop = detail.get("property")
900
+ if isinstance(prop, dict):
901
+ for k, v in prop.items():
902
+ rows[k] = _unwrap_value(v)
903
+
904
+ for k, v in detail.items():
905
+ if k == "property":
906
+ continue
907
+ if k in rows:
908
+ continue
909
+ if isinstance(v, (dict, list)):
910
+ rows[k] = json.dumps(v, ensure_ascii=False) if v else ""
911
+ else:
912
+ rows[k] = v
913
+
914
+ return rows
915
+
916
+
917
+ @bug.command("detail")
918
+ @click.option("--project", "-p", default=None, help="项目 ID")
919
+ @click.option("--bug-id", "-i", required=True, help="缺陷 ID(与列表「缺陷ID」、详情页 URL 中 id 一致)")
920
+ @click.pass_context
921
+ def get_bug_detail(ctx, project, bug_id):
922
+ """查询单个缺陷详情"""
923
+ project_id = project or ctx.obj["project"]
924
+ if not project_id:
925
+ click.echo("错误: 请通过 -p 指定项目 ID 或在配置中设置 default_project", err=True)
926
+ raise click.Abort()
927
+
928
+ api = DevOpsAPI(project_id)
929
+ try:
930
+ payload = api.get_bug(bug_id)
931
+ data = payload.get("data") if isinstance(payload, dict) else None
932
+
933
+ if ctx.obj.get("output_format") == "json":
934
+ out = data if data is not None else payload
935
+ click.echo(json.dumps(out, ensure_ascii=False, indent=2, default=str))
936
+ return
937
+
938
+ if data is None:
939
+ msg = _extract_api_message(payload) if isinstance(payload, dict) else ""
940
+ click.echo(msg or "未返回详情数据", err=True)
941
+ return
942
+
943
+ flat = _flatten_issue_detail_for_table(data)
944
+ if not flat:
945
+ click.echo(json.dumps(data, ensure_ascii=False, indent=2, default=str))
946
+ return
947
+
948
+ keys = sorted(flat.keys(), key=lambda x: (str(x).lower(), str(x)))
949
+ render_table(
950
+ headers=["字段", "值"],
951
+ rows=[[k, flat[k]] for k in keys],
952
+ no_truncate_headers=["字段", "值"],
953
+ )
954
+ except Exception as e:
955
+ click.echo(f"错误: {e}", err=True)
956
+
957
+
884
958
  @bug.command("get-states")
885
959
  @click.option("--project", "-p", required=True, help="项目 ID")
886
960
  @click.option(
@@ -1110,12 +1184,13 @@ def list_bug_types(ctx, project, all_options, field_id):
1110
1184
  @click.option("--zteam-project-id", required=True, help="ZTeam 项目 ID")
1111
1185
  @click.option("--sprint-id", required=True, default="", show_default=True, help="迭代 id")
1112
1186
  @click.option("--title", "-t", required=True, help="缺陷标题")
1113
- @click.option("--priority", default="CENTRAL", show_default=True, help="缺陷优先级")
1187
+ @click.option("--priority", default="CENTRAL", show_default=True, help="缺陷优先级")
1114
1188
  @click.option("--desc-editor-type", default="RICHTEXT", show_default=True, help="缺陷描述编辑器类型")
1115
- @click.option("--desc",default="<h2><span style=\"font-size: 16px;\">【前置条件】</span></h2>\n"
1116
- "<h2><span style=\"font-size: 16px;\">【操作步骤】</span></h2>\n"
1117
- "<h2><span style=\"font-size: 16px;\">【实际结果】</span></h2>\n"
1118
- "<h2><span style=\"font-size: 16px;\">【预期结果】</span></h2>",show_default=True, help="缺陷描述(支持 HTML")
1189
+ @click.option("--desc", default="<h2><span style=\"font-size: 16px;\">【前置条件】</span></h2>\n"
1190
+ "<h2><span style=\"font-size: 16px;\">【操作步骤】</span></h2>\n"
1191
+ "<h2><span style=\"font-size: 16px;\">【实际结果】</span></h2>\n"
1192
+ "<h2><span style=\"font-size: 16px;\">【预期结果】</span></h2>", show_default=True,
1193
+ help="缺陷描述(支持 HTML")
1119
1194
  @click.option("--severity", required=True, default="", show_default=True, help="缺陷严重程度")
1120
1195
  @click.option("--type", required=True, default="", show_default=True, help="缺陷分类")
1121
1196
  @click.option("--discovery-phase", required=True, default="", show_default=True, help="缺陷发现阶段")
@@ -1232,7 +1307,6 @@ def _bug_direction_go(ctx, project, bug_id, target_key, comment, operators):
1232
1307
 
1233
1308
 
1234
1309
  def _bug_direction_options(cmd):
1235
-
1236
1310
  cmd = click.option(
1237
1311
  "-i",
1238
1312
  "--bug-id",
@@ -1428,6 +1502,7 @@ def create_task(
1428
1502
  except Exception as e:
1429
1503
  click.echo(f"错误: {e}", err=True)
1430
1504
 
1505
+
1431
1506
  def main():
1432
1507
  cli()
1433
1508
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zt-devops-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: DevOps 平台迭代管理 CLI
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes