chore: initialize DevHarness template
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""检查 DevHarness 必需文件和任务归档的基本结构。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REQUIRED_FILES = (
|
||||
"AGENTS.md",
|
||||
"README.md",
|
||||
"docs/00-project-profile.md",
|
||||
"docs/01-workflow.md",
|
||||
"docs/templates/task-archive.md",
|
||||
".gitea/issue_template/epic.md",
|
||||
".gitea/issue_template/mvp.md",
|
||||
".gitea/issue_template/task.md",
|
||||
)
|
||||
ARCHIVE_HEADINGS = (
|
||||
"## 背景与目标",
|
||||
"## 最终方案",
|
||||
"## 修改文件",
|
||||
"## 验收结果",
|
||||
"## 测试",
|
||||
"## 相关提交",
|
||||
)
|
||||
|
||||
|
||||
def check_required_files(errors: list[str]) -> None:
|
||||
for relative_path in REQUIRED_FILES:
|
||||
if not (ROOT / relative_path).is_file():
|
||||
errors.append(f"缺少必需文件:{relative_path}")
|
||||
|
||||
|
||||
def check_project_profile(errors: list[str], warnings: list[str], strict: bool) -> None:
|
||||
profile = ROOT / "docs" / "00-project-profile.md"
|
||||
if not profile.is_file():
|
||||
return
|
||||
if "<填写" in profile.read_text(encoding="utf-8"):
|
||||
message = "项目档案仍有未填写内容"
|
||||
(errors if strict else warnings).append(message)
|
||||
|
||||
|
||||
def check_archives(errors: list[str]) -> None:
|
||||
task_dir = ROOT / "docs" / "task"
|
||||
for path in task_dir.glob("*.md"):
|
||||
if not re.match(r"^\d+-.+\.md$", path.name):
|
||||
errors.append(f"归档文件名不符合 <编号>-<标题>.md:{path.name}")
|
||||
content = path.read_text(encoding="utf-8")
|
||||
for heading in ARCHIVE_HEADINGS:
|
||||
if heading not in content:
|
||||
errors.append(f"{path.name} 缺少章节:{heading}")
|
||||
if "**未验证部分**:" not in content:
|
||||
errors.append(f"{path.name} 没有记录未验证部分")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="检查 DevHarness 项目结构")
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
action="store_true",
|
||||
help="项目档案有占位内容时返回失败",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
check_required_files(errors)
|
||||
check_project_profile(errors, warnings, args.strict)
|
||||
check_archives(errors)
|
||||
|
||||
for warning in warnings:
|
||||
print(f"警告:{warning}")
|
||||
for error in errors:
|
||||
print(f"错误:{error}")
|
||||
|
||||
if errors:
|
||||
print(f"检查失败:{len(errors)} 个问题")
|
||||
return 1
|
||||
print("DevHarness 检查通过")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,56 @@
|
||||
"""根据模板创建任务归档草稿。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
TEMPLATE = ROOT / "docs" / "templates" / "task-archive.md"
|
||||
TASK_DIR = ROOT / "docs" / "task"
|
||||
|
||||
|
||||
def safe_title(title: str) -> str:
|
||||
"""把标题转换为适合文件名的短文本。"""
|
||||
|
||||
cleaned = re.sub(r'[<>:"/\\|?*]', "-", title.strip())
|
||||
cleaned = re.sub(r"\s+", "-", cleaned)
|
||||
return cleaned.strip(".-")
|
||||
|
||||
|
||||
def create_archive(issue_number: str, title: str) -> Path:
|
||||
"""创建归档草稿;目标文件存在时拒绝覆盖。"""
|
||||
|
||||
short_title = safe_title(title)
|
||||
if not issue_number.isdigit():
|
||||
raise ValueError("工单号必须是数字")
|
||||
if not short_title:
|
||||
raise ValueError("标题不能为空")
|
||||
|
||||
target = TASK_DIR / f"{issue_number}-{short_title}.md"
|
||||
if target.exists():
|
||||
raise FileExistsError(f"文件已存在:{target}")
|
||||
|
||||
content = TEMPLATE.read_text(encoding="utf-8")
|
||||
content = content.replace("<工单号>", issue_number, 1)
|
||||
content = content.replace("<标题>", title.strip(), 1)
|
||||
content = content.replace("YYYY-MM-DD", date.today().isoformat(), 1)
|
||||
target.write_text(content, encoding="utf-8")
|
||||
return target
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="创建 docs/task 任务归档草稿")
|
||||
parser.add_argument("issue_number", help="Gitea 工单号,例如 123")
|
||||
parser.add_argument("title", help="简短任务标题")
|
||||
args = parser.parse_args()
|
||||
|
||||
target = create_archive(args.issue_number, args.title)
|
||||
print(f"已创建:{target.relative_to(ROOT)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user