mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
129 lines
3.4 KiB
Python
129 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
R114 implementation-record lint for defect-first regression evidence.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Iterable, List, Sequence, Tuple
|
|
|
|
DEFAULT_KEYWORD_PATTERN = r"(bug|regression|security[ _-]?fix|hotfix|漏洞|瑕疵|修補)"
|
|
|
|
|
|
def _discover_record_files(paths: Sequence[str]) -> List[Path]:
|
|
files: List[Path] = []
|
|
for raw in paths:
|
|
p = Path(raw)
|
|
if p.is_dir():
|
|
files.extend(sorted(p.glob("*_IMPLEMENTATION_RECORD.md")))
|
|
elif p.is_file():
|
|
files.append(p)
|
|
dedup: List[Path] = []
|
|
seen = set()
|
|
for f in files:
|
|
resolved = f.resolve()
|
|
if resolved in seen:
|
|
continue
|
|
seen.add(resolved)
|
|
dedup.append(f)
|
|
return dedup
|
|
|
|
|
|
def lint_record_text(
|
|
text: str,
|
|
*,
|
|
strict: bool,
|
|
keyword_pattern: str = DEFAULT_KEYWORD_PATTERN,
|
|
) -> Tuple[bool, List[str]]:
|
|
enforce = strict
|
|
if not enforce:
|
|
enforce = re.search(keyword_pattern, text, re.IGNORECASE) is not None
|
|
if not enforce:
|
|
return True, []
|
|
|
|
issues: List[str] = []
|
|
if not re.search(r"(?im)^##\s+Regression Evidence\b", text):
|
|
issues.append("missing section: '## Regression Evidence'")
|
|
|
|
required_fields = (
|
|
r"(?im)^\s*[-*]\s*Defect(?:\s+ID)?\s*:\s*\S+",
|
|
r"(?im)^\s*[-*]\s*Regression Test(?:\s+ID)?\s*:\s*\S+",
|
|
r"(?im)^\s*[-*]\s*Failing Evidence\s*:\s*\S+",
|
|
r"(?im)^\s*[-*]\s*Passing Evidence\s*:\s*\S+",
|
|
)
|
|
for pattern in required_fields:
|
|
if not re.search(pattern, text):
|
|
issues.append(f"missing required field matching: {pattern}")
|
|
|
|
return len(issues) == 0, issues
|
|
|
|
|
|
def lint_records(
|
|
paths: Sequence[str],
|
|
*,
|
|
strict: bool,
|
|
keyword_pattern: str = DEFAULT_KEYWORD_PATTERN,
|
|
) -> Tuple[bool, List[str]]:
|
|
files = _discover_record_files(paths)
|
|
if not files:
|
|
return True, []
|
|
|
|
errors: List[str] = []
|
|
for file_path in files:
|
|
text = file_path.read_text(encoding="utf-8", errors="replace")
|
|
ok, issues = lint_record_text(
|
|
text,
|
|
strict=strict,
|
|
keyword_pattern=keyword_pattern,
|
|
)
|
|
if ok:
|
|
continue
|
|
for issue in issues:
|
|
errors.append(f"{file_path}: {issue}")
|
|
return len(errors) == 0, errors
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Lint implementation records for defect-first regression evidence."
|
|
)
|
|
parser.add_argument(
|
|
"--path",
|
|
action="append",
|
|
default=None,
|
|
help="Record file or directory. Repeatable. Defaults to .planning.",
|
|
)
|
|
parser.add_argument(
|
|
"--strict",
|
|
action="store_true",
|
|
help="Force regression-evidence lint on all implementation records.",
|
|
)
|
|
parser.add_argument(
|
|
"--keyword-pattern",
|
|
default=DEFAULT_KEYWORD_PATTERN,
|
|
help="Regex used to trigger lint in non-strict mode.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
paths = args.path or [".planning"]
|
|
ok, errors = lint_records(
|
|
paths,
|
|
strict=args.strict,
|
|
keyword_pattern=args.keyword_pattern,
|
|
)
|
|
if ok:
|
|
print("IMPLEMENTATION_RECORD_LINT: PASS")
|
|
return 0
|
|
|
|
for err in errors:
|
|
print(f"IMPLEMENTATION_RECORD_LINT: FAIL: {err}", file=sys.stdout)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|