Files
OneOS1.2/.cursor/skills/yunxiao-requirement-lifecycle/scripts/profile_tool.py

537 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Validate a project-scoped Yunxiao lifecycle profile and render an execution plan."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
REQUIRED_LABELS = {"分析", "设计", "开发", "测试"}
REQUIRED_RULE_IDS = {f"R{index:02d}" for index in range(1, 13)}
REQUIRED_TASK_RULE_IDS = {f"TK{index:02d}" for index in range(1, 9)}
REQUIRED_CONTROL_IDS = {
*REQUIRED_RULE_IDS,
*REQUIRED_TASK_RULE_IDS,
*(f"TC{index:02d}" for index in range(1, 4)),
*(f"DF{index:02d}" for index in range(1, 4)),
*(f"CI{index:02d}" for index in range(1, 4)),
"L01",
"L02",
}
VALID_MODES = {
"manual",
"native_rule",
"integration_bridge",
"disabled_legacy",
"not_supported",
}
VALID_RELEASE_EVIDENCE_MODES = {
"native_release_change",
"pipeline_callback",
"manual_release_confirmation",
}
SENSITIVE_FRAGMENTS = (
"password",
"passwd",
"secret",
"token",
"cookie",
"credential",
"private_key",
"otp",
"密码",
"密钥",
)
def load_profile(path: Path) -> dict[str, Any]:
with path.open("r", encoding="utf-8-sig") as handle:
data = json.load(handle)
if not isinstance(data, dict):
raise ValueError("配置根节点必须是 JSON 对象")
return data
def is_text(value: Any) -> bool:
return isinstance(value, str) and bool(value.strip())
def require_text(container: dict[str, Any], key: str, label: str, errors: list[str]) -> None:
if not is_text(container.get(key)):
errors.append(f"{label}.{key} 不能为空")
def walk_sensitive_keys(value: Any, prefix: str = "") -> list[str]:
found: list[str] = []
if isinstance(value, dict):
for key, nested in value.items():
current = f"{prefix}.{key}" if prefix else str(key)
lower = str(key).lower()
if any(fragment in lower for fragment in SENSITIVE_FRAGMENTS):
found.append(current)
found.extend(walk_sensitive_keys(nested, current))
elif isinstance(value, list):
for index, nested in enumerate(value):
found.extend(walk_sensitive_keys(nested, f"{prefix}[{index}]"))
return found
def validate_control_list(
controls: Any, label: str, errors: list[str]
) -> None:
if not isinstance(controls, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
for index, control in enumerate(controls):
item_label = f"{label}[{index}]"
if not isinstance(control, dict):
errors.append(f"{item_label} 必须是对象")
continue
control_id = control.get("id")
if not is_text(control_id):
errors.append(f"{item_label}.id 不能为空")
continue
if control_id in seen:
errors.append(f"{label} 控制项重复:{control_id}")
seen.add(control_id)
if control_id not in REQUIRED_CONTROL_IDS:
errors.append(f"{label} 未知控制项:{control_id}")
mode = control.get("mode")
if mode not in VALID_MODES:
errors.append(f"{item_label}.mode 不合法")
if not isinstance(control.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
for key in ("evidence", "owner", "fallback"):
require_text(control, key, item_label, errors)
if mode in {"disabled_legacy", "not_supported"} and control.get("enabled") is not False:
errors.append(f"{item_label}{mode} 模式要求 enabled=false")
if control_id in {"L01", "L02"} and mode not in {
"disabled_legacy",
"native_rule",
"manual",
}:
errors.append(f"{item_label} 必须明确为停用旧规则、获批原生规则或人工处置")
missing = sorted(REQUIRED_CONTROL_IDS - seen)
if missing:
errors.append(f"{label} 缺少控制项:" + "".join(missing))
def validate_rule_instances(
rules: Any, lifecycle: list[str], label: str, errors: list[str]
) -> None:
if not isinstance(rules, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
orders: set[int] = set()
expected = {
f"R{index + 1:02d}": (lifecycle[index], lifecycle[index + 1])
for index in range(12)
}
for index, rule in enumerate(rules):
item_label = f"{label}[{index}]"
if not isinstance(rule, dict):
errors.append(f"{item_label} 必须是对象")
continue
rule_id = rule.get("id")
if not is_text(rule_id):
errors.append(f"{item_label}.id 不能为空")
continue
if rule_id in seen:
errors.append(f"{label} 规则实例重复:{rule_id}")
seen.add(rule_id)
if rule_id not in REQUIRED_RULE_IDS:
errors.append(f"{label} 未知规则实例:{rule_id}")
mode = rule.get("mode")
if mode not in {"manual", "native_rule", "integration_bridge", "not_supported"}:
errors.append(f"{item_label}.mode 不合法")
for key in ("actual_rule_name", "trigger", "source_status", "target_status", "action", "evidence"):
require_text(rule, key, item_label, errors)
conditions = rule.get("conditions")
if not isinstance(conditions, list) or not conditions or not all(is_text(v) for v in conditions):
errors.append(f"{item_label}.conditions 必须是非空文本数组")
order = rule.get("order")
if not isinstance(order, int) or order < 1:
errors.append(f"{item_label}.order 必须是正整数")
elif order in orders:
errors.append(f"{label} 规则顺序重复:{order}")
else:
orders.add(order)
if not isinstance(rule.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
if rule.get("has_current_state_condition") is not True:
errors.append(f"{item_label}.has_current_state_condition 必须为 true")
if rule_id in expected:
expected_source, expected_target = expected[rule_id]
if rule.get("source_status") != expected_source:
errors.append(f"{item_label}.source_status 应为 {expected_source}")
if rule.get("target_status") != expected_target:
errors.append(f"{item_label}.target_status 应为 {expected_target}")
missing = sorted(REQUIRED_RULE_IDS - seen)
if missing:
errors.append(f"{label} 缺少规则实例:" + "".join(missing))
def validate_task_rule_instances(rules: Any, label: str, errors: list[str]) -> None:
if not isinstance(rules, list):
errors.append(f"{label} 必须是数组")
return
seen: set[str] = set()
orders: set[int] = set()
for index, rule in enumerate(rules):
item_label = f"{label}[{index}]"
if not isinstance(rule, dict):
errors.append(f"{item_label} 必须是对象")
continue
rule_id = rule.get("id")
if not is_text(rule_id):
errors.append(f"{item_label}.id 不能为空")
continue
if rule_id in seen:
errors.append(f"{label} 规则实例重复:{rule_id}")
seen.add(rule_id)
if rule_id not in REQUIRED_TASK_RULE_IDS:
errors.append(f"{label} 未知任务规则实例:{rule_id}")
if rule.get("mode") not in {"manual", "native_rule", "integration_bridge", "not_supported"}:
errors.append(f"{item_label}.mode 不合法")
for key in ("actual_rule_name", "trigger", "scope", "source_state", "evidence"):
require_text(rule, key, item_label, errors)
for key in ("conditions", "actions"):
values = rule.get(key)
if not isinstance(values, list) or not values or not all(is_text(value) for value in values):
errors.append(f"{item_label}.{key} 必须是非空文本数组")
order = rule.get("order")
if not isinstance(order, int) or order < 1:
errors.append(f"{item_label}.order 必须是正整数")
elif order in orders:
errors.append(f"{label} 规则顺序重复:{order}")
else:
orders.add(order)
if not isinstance(rule.get("enabled"), bool):
errors.append(f"{item_label}.enabled 必须是布尔值")
missing = sorted(REQUIRED_TASK_RULE_IDS - seen)
if missing:
errors.append(f"{label} 缺少任务规则实例:" + "".join(missing))
def validate_project(
project: Any, index: int, lifecycle: list[str], errors: list[str]
) -> None:
label = f"projects[{index}]"
if not isinstance(project, dict):
errors.append(f"{label} 必须是对象")
return
for key in ("name", "work_item_type", "release_status", "final_status"):
require_text(project, key, label, errors)
if project.get("release_status") != lifecycle[11]:
errors.append(f"{label}.release_status 必须与 lifecycle[11] 一致")
if project.get("final_status") != lifecycle[12]:
errors.append(f"{label}.final_status 必须与 lifecycle[12] 一致")
labels = project.get("labels_observed")
if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)):
errors.append(f"{label}.labels_observed 必须包含:分析、设计、开发、测试")
repositories = project.get("repositories")
if not isinstance(repositories, list) or not repositories:
errors.append(f"{label}.repositories 至少包含一个仓库")
else:
for repo_index, repo in enumerate(repositories):
repo_label = f"{label}.repositories[{repo_index}]"
if not isinstance(repo, dict):
errors.append(f"{repo_label} 必须是对象")
continue
for key in ("name", "role", "integration_branch", "evidence"):
require_text(repo, key, repo_label, errors)
if repo.get("integration_branch") not in {"dev", "develop"}:
errors.append(f"{repo_label}.integration_branch 只能是实际存在的 dev/develop")
for key in ("codeup_integrated", "required_for_mr_gate"):
if not isinstance(repo.get(key), bool):
errors.append(f"{repo_label}.{key} 必须是布尔值")
pipeline = project.get("test_pipeline")
if not isinstance(pipeline, dict):
errors.append(f"{label}.test_pipeline 必须是对象")
else:
for key in ("name", "environment", "evidence"):
require_text(pipeline, key, f"{label}.test_pipeline", errors)
for key in (
"is_clone_for_callback_poc",
"original_pipeline_unchanged",
"docker_success_callback_enabled",
):
if not isinstance(pipeline.get(key), bool):
errors.append(f"{label}.test_pipeline.{key} 必须是布尔值")
security = pipeline.get("callback_security")
if not isinstance(security, dict):
errors.append(f"{label}.test_pipeline.callback_security 必须是对象")
else:
security_keys = (
"signed",
"timestamp_checked",
"replay_protected",
"idempotent_by_execution_id",
"failure_retry_tested",
)
for key in security_keys:
if not isinstance(security.get(key), bool):
errors.append(f"{label}.test_pipeline.callback_security.{key} 必须是布尔值")
if pipeline.get("docker_success_callback_enabled") is True:
if pipeline.get("is_clone_for_callback_poc") is not True:
errors.append(f"{label} 启用回调时必须使用test流水线副本")
if pipeline.get("original_pipeline_unchanged") is not True:
errors.append(f"{label} 启用回调时必须保持原流水线不变")
if any(security.get(key) is not True for key in security_keys):
errors.append(f"{label} 启用回调时必须完成全部安全与重试验证")
release = project.get("release_evidence")
if not isinstance(release, dict):
errors.append(f"{label}.release_evidence 必须是对象")
else:
if release.get("mode") not in VALID_RELEASE_EVIDENCE_MODES:
errors.append(f"{label}.release_evidence.mode 不合法")
for key in ("target_environment", "evidence"):
require_text(release, key, f"{label}.release_evidence", errors)
if release.get("test_success_is_production_release") is not False:
errors.append(f"{label}.release_evidence.test_success_is_production_release 必须为 false")
test_plan = project.get("test_plan")
if not isinstance(test_plan, dict):
errors.append(f"{label}.test_plan 必须是对象")
else:
for key in ("name", "evidence"):
require_text(test_plan, key, f"{label}.test_plan", errors)
if not isinstance(test_plan.get("requirement_formally_related"), bool):
errors.append(f"{label}.test_plan.requirement_formally_related 必须是布尔值")
states = test_plan.get("case_states_observed")
if not isinstance(states, list) or not states or not all(is_text(v) for v in states):
errors.append(f"{label}.test_plan.case_states_observed 必须是非空文本数组")
defect = project.get("defect_workflow")
if not isinstance(defect, dict):
errors.append(f"{label}.defect_workflow 必须是对象")
else:
for key in ("fixed_status", "closed_status", "reopen_status", "evidence"):
require_text(defect, key, f"{label}.defect_workflow", errors)
if defect.get("fixed_is_terminal") is not False:
errors.append(f"{label}.defect_workflow.fixed_is_terminal 必须为 false")
if defect.get("tester_retest_required") is not True:
errors.append(f"{label}.defect_workflow.tester_retest_required 必须为 true")
validate_rule_instances(project.get("rule_instances"), lifecycle, f"{label}.rule_instances", errors)
validate_task_rule_instances(
project.get("task_rule_instances"), f"{label}.task_rule_instances", errors
)
validate_control_list(project.get("control_coverage"), f"{label}.control_coverage", errors)
if not isinstance(project.get("adjunct_automations"), list):
errors.append(f"{label}.adjunct_automations 必须是数组,可为空")
def validate(data: dict[str, Any]) -> list[str]:
errors: list[str] = []
if data.get("schema_version") != 3:
errors.append("schema_version 必须为 3旧版台账不包含阶段任务闭环")
for key in ("organization", "work_item_type"):
require_text(data, key, "profile", errors)
lifecycle = data.get("lifecycle")
if not isinstance(lifecycle, list) or len(lifecycle) != 13 or not all(is_text(v) for v in lifecycle):
errors.append("lifecycle 必须包含 13 个非空有序状态")
lifecycle = [str(index) for index in range(13)]
elif len(set(lifecycle)) != len(lifecycle):
errors.append("lifecycle 状态名称不能重复")
labels = data.get("requirement_labels")
if not isinstance(labels, list) or not REQUIRED_LABELS.issubset(set(labels)):
errors.append("requirement_labels 必须包含:分析、设计、开发、测试")
policy = data.get("automation_policy")
if not isinstance(policy, dict):
errors.append("automation_policy 必须是对象")
else:
for key in ("require_current_state_condition", "inspect_rule_order_and_cascades"):
if policy.get(key) is not True:
errors.append(f"automation_policy.{key} 必须为 true")
for key in ("allow_automatic_final_close_without_acceptance", "zero_related_items_are_complete"):
if policy.get(key) is not False:
errors.append(f"automation_policy.{key} 必须为 false")
events = policy.get("development_start_event_candidates")
if not isinstance(events, list) or not events or not all(is_text(v) for v in events):
errors.append("automation_policy.development_start_event_candidates 不能为空")
if policy.get("require_code_assets_linked_to_requirement_and_development_task") is not True:
errors.append(
"automation_policy.require_code_assets_linked_to_requirement_and_development_task 必须为 true"
)
idempotency = policy.get("stage_task_creation_idempotency_key_components")
if not isinstance(idempotency, list) or not idempotency or not all(is_text(v) for v in idempotency):
errors.append("automation_policy.stage_task_creation_idempotency_key_components 不能为空")
test_policy = data.get("test_closure_policy")
if not isinstance(test_policy, dict):
errors.append("test_closure_policy 必须是对象")
else:
if test_policy.get("defect_fixed_is_terminal") is not False:
errors.append("test_closure_policy.defect_fixed_is_terminal 必须为 false")
if test_policy.get("require_tester_retest") is not True:
errors.append("test_closure_policy.require_tester_retest 必须为 true")
for key in ("accepted_case_states", "rejected_case_states"):
values = test_policy.get(key)
if not isinstance(values, list) or not values or not all(is_text(v) for v in values):
errors.append(f"test_closure_policy.{key} 不能为空")
projects = data.get("projects")
if not isinstance(projects, list) or not projects:
errors.append("projects 至少包含一个项目")
else:
names: set[str] = set()
for index, project in enumerate(projects):
validate_project(project, index, lifecycle, errors)
if isinstance(project, dict) and is_text(project.get("name")):
name = project["name"]
if name in names:
errors.append(f"项目名称重复:{name}")
names.add(name)
sensitive = walk_sensitive_keys(data)
if sensitive:
errors.append("配置中禁止出现敏感字段:" + "".join(sensitive))
if "请填写" in json.dumps(data, ensure_ascii=False):
errors.append("仍存在“请填写”占位符")
return errors
def render(data: dict[str, Any]) -> str:
lines = [
"# 云效需求生命周期自动化执行计划",
"",
f"- 组织:{data['organization']}",
f"- 工作项类型:{data['work_item_type']}",
f"- 项目数:{len(data['projects'])}",
"- 完整性口径每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。",
"- 安全边界:不修改现有生产流水线;不记录任何凭据或密钥。",
"",
]
for project in data["projects"]:
lines.extend([
f"## 项目:{project['name']}",
"",
f"- 发布状态:{project['release_status']}",
f"- 最终状态:{project['final_status']}",
f"- test流水线{project['test_pipeline']['name']}",
f"- 发布证据方式:{project['release_evidence']['mode']}",
"",
"### 仓库覆盖",
"",
"| 仓库 | 角色 | 集成分支 | Codeup已集成 | 纳入全部MR门禁 | 证据 |",
"|---|---|---|---|---|---|",
])
for repo in project["repositories"]:
lines.append(
f"| {repo['name']} | {repo['role']} | {repo['integration_branch']} | "
f"{'' if repo['codeup_integrated'] else ''} | "
f"{'' if repo['required_for_mr_gate'] else ''} | {repo['evidence']} |"
)
lines.extend([
"",
"### R01R12实际规则实例",
"",
"| ID | 实现 | 实际规则/门禁 | 触发 | 流转 | 条件 | 顺序 | 启用 | 证据 |",
"|---|---|---|---|---|---|---:|---|---|",
])
for rule in sorted(project["rule_instances"], key=lambda item: item["order"]):
conditions = "".join(rule["conditions"])
lines.append(
f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | "
f"{rule['source_status']}{rule['target_status']} | {conditions} | {rule['order']} | "
f"{'' if rule['enabled'] else ''} | {rule['evidence']} |"
)
lines.extend([
"",
"### TK01TK08任务规则实例",
"",
"| ID | 实现 | 实际规则/门禁 | 触发 | 作用域 | 源状态 | 条件 | 动作 | 顺序 | 启用 | 证据 |",
"|---|---|---|---|---|---|---|---|---:|---|---|",
])
for rule in sorted(project["task_rule_instances"], key=lambda item: item["order"]):
conditions = "".join(rule["conditions"])
actions = "".join(rule["actions"])
lines.append(
f"| {rule['id']} | {rule['mode']} | {rule['actual_rule_name']} | {rule['trigger']} | "
f"{rule['scope']} | {rule['source_state']} | {conditions} | {actions} | {rule['order']} | "
f"{'' if rule['enabled'] else ''} | {rule['evidence']} |"
)
lines.extend([
"",
"### 31项完整性台账",
"",
"| 控制ID | 实现方式 | 启用 | 观察证据 | 负责人 | 回退方案 |",
"|---|---|---|---|---|---|",
])
for control in sorted(project["control_coverage"], key=lambda item: item["id"]):
lines.append(
f"| {control['id']} | {control['mode']} | {'' if control['enabled'] else ''} | "
f"{control['evidence']} | {control['owner']} | {control['fallback']} |"
)
adjunct = project["adjunct_automations"]
lines.extend([
"",
"### 范围外通用自动化盘点",
"",
"- " + ("".join(map(str, adjunct)) if adjunct else "未纳入如需通知、自动指派、SLA、字段同步等应单独授权和设计。"),
"",
])
lines.extend([
"## 执行检查",
"",
"- [ ] 两个或多个项目的规则实例、控制台账和证据已分别填写。",
"- [ ] 已逐条读取真实触发配置、规则顺序、启用状态和执行账号。",
"- [ ] 已检查后继规则、重复规则和跨阶段连锁流转。",
"- [ ] 已验证失败用例、缺陷已修复、复测通过/失败时双方状态一致。",
"- [ ] 已逐仓库确认真实dev/develop分支、正式关联和全部MR门禁。",
"- [ ] 已区分test部署、生产发布、发布变更和业务验收。",
"- [ ] 已执行正向、负向、异步、多仓库、失败重试和旧规则连锁测试。",
"",
])
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers(dest="command", required=True)
validate_parser = subparsers.add_parser("validate", help="验证项目配置")
validate_parser.add_argument("profile", type=Path)
render_parser = subparsers.add_parser("render", help="生成 Markdown 执行计划")
render_parser.add_argument("profile", type=Path)
render_parser.add_argument("--output", required=True, type=Path)
args = parser.parse_args()
try:
data = load_profile(args.profile)
errors = validate(data)
except (OSError, json.JSONDecodeError, ValueError) as error:
print(f"配置读取失败:{error}", file=sys.stderr)
return 2
if errors:
for error in errors:
print(f"[错误] {error}", file=sys.stderr)
return 1
if args.command == "validate":
print(
f"[通过] 配置有效:{len(data['projects'])} 个项目;"
"每项目12个需求规则实例、8个任务规则实例、31个闭环控制项。"
)
return 0
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(render(data), encoding="utf-8")
print(f"[完成] 已生成执行计划:{args.output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())