Skip to content

Commit 9a3b392

Browse files
feat: pre-commit of readme metadata (#103)
2 parents e5d0969 + fa9ee0c commit 9a3b392

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,13 @@ repos:
2323
- markdownlint-cli2
2424
language: system
2525
pass_filenames: false
26+
- id: check-use-cases-readme-metadata
27+
name: check-use-cases-readme-metadata
28+
entry: python
29+
args:
30+
- workflow_utils/check_readme_metadata.py
31+
language: python
32+
additional_dependencies:
33+
- python-frontmatter
34+
files: ^02-use-cases/.*/README\.md$
35+
types_or: [ markdown ]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# pip install python-frontmatter
2+
from typing import Any
3+
from pathlib import Path
4+
import argparse
5+
import subprocess
6+
import sys
7+
import frontmatter
8+
9+
10+
def check_name(metadata: dict[str, Any]):
11+
name = metadata.get("name")
12+
if not name:
13+
raise ValueError("name is required")
14+
15+
16+
def check_description(metadata: dict[str, Any]):
17+
description = metadata.get("description")
18+
if not description:
19+
raise ValueError("description is required")
20+
21+
22+
def check_senarios(metadata: dict[str, Any]):
23+
senarios = metadata.get("senarios")
24+
if not senarios:
25+
raise ValueError("senarios is required")
26+
if len(senarios) != 4:
27+
raise ValueError("senarios should have 4 items")
28+
29+
30+
def check_components(metadata: dict[str, Any]):
31+
standard_components = ["Identity", "MCP Toolset"]
32+
components = metadata.get("components")
33+
if not components:
34+
raise ValueError("components is required")
35+
if len(components) == 0:
36+
raise ValueError("components should have at least 1 item")
37+
for component in components:
38+
name = component.get("name")
39+
if not name:
40+
raise ValueError("component name is required")
41+
desc = component.get("desc")
42+
if not desc:
43+
raise ValueError("component desc is required")
44+
if component["name"] not in standard_components:
45+
raise ValueError(f"component name should be one of {standard_components}")
46+
47+
48+
def main(argv: list[str] | None = None) -> int:
49+
parser = argparse.ArgumentParser()
50+
parser.add_argument("files", nargs="*")
51+
args = parser.parse_args(argv)
52+
53+
if not args.files:
54+
post = frontmatter.load("README.md")
55+
check_name(post.metadata)
56+
check_description(post.metadata)
57+
check_senarios(post.metadata)
58+
check_components(post.metadata)
59+
return 0
60+
61+
repo_root = Path(__file__).resolve().parent.parent
62+
use_cases_dir = repo_root / "02-use-cases"
63+
64+
diff_commands = [
65+
["git", "diff", "--name-only", "--diff-filter=ACMRT"],
66+
["git", "diff", "--cached", "--name-only", "--diff-filter=ACMRT"],
67+
]
68+
69+
changed_files: set[str] = set()
70+
for cmd in diff_commands:
71+
completed = subprocess.run(
72+
cmd,
73+
capture_output=True,
74+
text=True,
75+
cwd=str(repo_root),
76+
)
77+
changed_files.update(
78+
line.strip() for line in completed.stdout.splitlines() if line.strip()
79+
)
80+
81+
failed = False
82+
83+
for file_path in args.files:
84+
path = Path(file_path).resolve()
85+
try:
86+
rel = path.relative_to(repo_root)
87+
except ValueError:
88+
continue
89+
90+
if path.name.lower() != "readme.md":
91+
continue
92+
93+
if use_cases_dir not in path.parents:
94+
continue
95+
96+
if str(rel) not in changed_files:
97+
continue
98+
99+
post = frontmatter.load(path)
100+
101+
try:
102+
check_name(post.metadata)
103+
check_description(post.metadata)
104+
check_senarios(post.metadata)
105+
check_components(post.metadata)
106+
except ValueError as exc:
107+
sys.stderr.write(f"{rel}: {exc}\n")
108+
failed = True
109+
110+
return 1 if failed else 0
111+
112+
113+
if __name__ == "__main__":
114+
raise SystemExit(main())

0 commit comments

Comments
 (0)