feat(ack): bind test env to deployer and add regression mode

ACK 0.19.0 hands test-environment deploys to the deployer skill,
documents bug-fix as a first-class scenario, and adds
docs/ack/regression.yaml harvest plus a /ack regression run.
This commit is contained in:
2026-08-25 14:41:05 +08:00
parent ee31278947
commit ad6695245b
39 changed files with 1919 additions and 206 deletions
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""选择 ACK 回归目录中的 active 用例。
默认返回 smoke 套件。本脚本只输出数据,不执行 steps 或 automationRef。
用法:
python3 select_regression.py docs/ack/regression.yaml
python3 select_regression.py docs/ack/regression.yaml --suite full
python3 select_regression.py docs/ack/regression.yaml --case-id REG-login-001
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
from validate_regression import ID_RE, load_yaml, validate_builtin
DEFAULT_LIMIT = 50
MAX_LIMIT = 200
def select_cases(
data: dict[str, Any],
*,
suite: str,
case_ids: list[str],
limit: int,
) -> list[dict[str, Any]]:
cases = data.get("cases")
if not isinstance(cases, list):
raise ValueError("cases 必须是列表")
wanted = set(case_ids)
selected: list[dict[str, Any]] = []
for case in cases:
if not isinstance(case, dict):
continue
if case.get("status") != "active":
continue
case_id = case.get("id")
if wanted:
if case_id not in wanted:
continue
elif suite == "smoke" and case.get("suite") != "smoke":
continue
selected.append(case)
missing = wanted - {
case.get("id") for case in selected if isinstance(case.get("id"), str)
}
if missing:
raise ValueError("找不到 active 用例: " + ", ".join(sorted(missing)))
if len(selected) > limit:
raise ValueError(
f"命中 {len(selected)} 条,超过 --limit {limit}"
"请用 --suite / --case-id 缩小范围"
)
return selected
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="选择 ACK 回归用例")
parser.add_argument(
"regression", nargs="?", default="docs/ack/regression.yaml"
)
parser.add_argument(
"--suite",
choices=("smoke", "full"),
default="smoke",
help="smoke 只返回 suite=smokefull 返回全部 active 用例",
)
parser.add_argument("--case-id", action="append", default=[], dest="case_ids")
parser.add_argument("--limit", type=int, default=DEFAULT_LIMIT)
parser.add_argument(
"--format", choices=("json", "ids"), default="json", dest="output_format"
)
args = parser.parse_args(argv)
regression_path = Path(args.regression)
if not regression_path.is_file():
sys.stderr.write(f"找不到回归目录: {regression_path}\n")
return 2
if not 1 <= args.limit <= MAX_LIMIT:
sys.stderr.write(f"--limit 必须在 1..{MAX_LIMIT} 之间\n")
return 2
for case_id in args.case_ids:
if ID_RE.fullmatch(case_id) is None:
sys.stderr.write(f"--case-id 必须使用 REG-<id> 格式: {case_id}\n")
return 2
data = load_yaml(regression_path, "回归目录")
errors = validate_builtin(data)
if errors:
sys.stderr.write(f"回归目录无效,拒绝选择,共 {len(errors)} 项:\n")
for error in errors:
sys.stderr.write(f" - {error}\n")
return 1
try:
selected = select_cases(
data,
suite=args.suite,
case_ids=args.case_ids,
limit=args.limit,
)
except ValueError as exc:
sys.stderr.write(f"回归选择失败: {exc}\n")
return 1
ids = [case.get("id") for case in selected]
if args.output_format == "ids":
if ids:
sys.stdout.write("\n".join(str(item) for item in ids) + "\n")
return 0
payload = {
"count": len(selected),
"limit": args.limit,
"suite": args.suite,
"ids": ids,
"cases": selected,
}
sys.stdout.write(json.dumps(payload, ensure_ascii=False, indent=2) + "\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())