-
Notifications
You must be signed in to change notification settings - Fork 0
211 lines (171 loc) · 9.1 KB
/
Copy pathauto-update-readme.yml
File metadata and controls
211 lines (171 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
name: Auto Update README
on:
push:
branches: [main]
paths:
- 'skills/**/rules/*.md'
- 'skills/**/SKILL.md'
jobs:
update-readme:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Generate and update README
run: |
python3 << 'PYTHON_SCRIPT'
import os
import re
from pathlib import Path
from typing import Dict, List, Tuple
def parse_yaml_frontmatter(file_path: Path) -> Dict[str, str]:
"""Parse YAML frontmatter from a markdown file."""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Extract frontmatter between --- markers
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
if not match:
return {}
frontmatter = {}
for line in match.group(1).split('\n'):
if ':' in line:
key, value = line.split(':', 1)
frontmatter[key.strip()] = value.strip()
return frontmatter
def get_skill_info(skill_dir: Path) -> Tuple[str, str, List[Dict]]:
"""Get skill name, description and rules from a skill directory."""
skill_md = skill_dir / 'SKILL.md'
if not skill_md.exists():
return '', '', []
with open(skill_md, 'r', encoding='utf-8') as f:
skill_content = f.read()
# Parse skill frontmatter
skill_meta = parse_yaml_frontmatter(skill_md)
skill_name = skill_meta.get('name', skill_dir.name)
skill_desc = skill_meta.get('description', '')
# Get all rule files
rules_dir = skill_dir / 'rules'
if not rules_dir.exists():
return skill_name, skill_desc, []
rules = []
for rule_file in sorted(rules_dir.glob('*.md')):
rule_meta = parse_yaml_frontmatter(rule_file)
if rule_meta:
rules.append({
'file': rule_file.name,
'title': rule_meta.get('title', rule_file.stem),
'type': rule_meta.get('type', 'capability'),
'impact': rule_meta.get('impact', 'MEDIUM'),
'tags': rule_meta.get('tags', ''),
'path': str(rule_file.relative_to(Path.cwd()))
})
return skill_name, skill_desc, rules
def format_skill_section(skill_name: str, rules: List[Dict]) -> str:
"""Format a skill section with its rules."""
if not rules:
return ''
# Count rules by type
capability_rules = [r for r in rules if r['type'] == 'capability']
efficiency_rules = [r for r in rules if r['type'] == 'efficiency']
# Map skill names to display names
skill_display_names = {
'nextdns-api': 'NextDNS API',
'nextdns-cli': 'NextDNS CLI',
'nextdns-ui': 'NextDNS UI',
'integrations': 'Integrations'
}
display_name = skill_display_names.get(skill_name, skill_name)
total_count = len(rules)
output = f"### {skill_name} ({total_count} rules)\n\n"
# Get description based on skill
descriptions = {
'nextdns-api': 'NextDNS API integration best practices covering authentication, profile management, analytics, logs, and real-time streaming.',
'nextdns-cli': 'NextDNS CLI client best practices for installation, configuration, and management of DNS-over-HTTPS proxy.',
'nextdns-ui': 'NextDNS Web UI configuration and management best practices via the web dashboard.',
'integrations': 'NextDNS integration guides for third-party platforms and services including routers, network management, and home automation.'
}
output += descriptions.get(skill_name, '') + "\n\n"
# Create table
output += "| Type | Count | Examples |\n"
output += "|:-----------|:------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|\n"
# Capability row
if capability_rules:
cap_examples = ', '.join([r['title'] for r in capability_rules[:8]])
if len(capability_rules) > 8:
cap_examples += '...'
output += f"| Capability | {len(capability_rules):<5} | {cap_examples:<154} |\n"
else:
output += f"| Capability | 0 | None |\n"
# Efficiency row
if efficiency_rules:
eff_examples = ', '.join([r['title'] for r in efficiency_rules[:8]])
if len(efficiency_rules) > 8:
eff_examples += '...'
output += f"| Efficiency | {len(efficiency_rules):<5} | {eff_examples:<154} |\n"
else:
output += f"| Efficiency | 0 | Future additions |\n"
output += "\n"
return output
def main():
"""Main function to update README.md."""
repo_root = Path.cwd()
skills_dir = repo_root / 'skills'
readme_path = repo_root / 'README.md'
if not skills_dir.exists() or not readme_path.exists():
print("Error: skills directory or README.md not found")
return
# Get all skill directories
skill_order = ['nextdns-api', 'nextdns-cli', 'nextdns-ui', 'integrations']
skills_data = {}
for skill_dir in sorted(skills_dir.iterdir()):
if skill_dir.is_dir():
skill_name, skill_desc, rules = get_skill_info(skill_dir)
if rules:
skills_data[skill_name] = rules
# Generate the skills section
skills_content = ""
for skill_name in skill_order:
if skill_name in skills_data:
skills_content += format_skill_section(skill_name, skills_data[skill_name])
# Read README.md
with open(readme_path, 'r', encoding='utf-8') as f:
readme = f.read()
# Find and replace content between markers
start_marker = "## Available Skills"
end_marker = "## Rule Types"
pattern = re.compile(
f"({re.escape(start_marker)})(.*?)({re.escape(end_marker)})",
re.DOTALL
)
if not pattern.search(readme):
print("Error: Could not find markers in README.md")
print(f"Looking for: '{start_marker}' and '{end_marker}'")
return
new_readme = pattern.sub(
f"\\1\n\n{skills_content}\\3",
readme
)
# Write back to README.md
with open(readme_path, 'w', encoding='utf-8') as f:
f.write(new_readme)
print("✅ README.md updated successfully!")
print(f"Total skills processed: {len(skills_data)}")
for skill_name, rules in skills_data.items():
print(f" - {skill_name}: {len(rules)} rules")
if __name__ == '__main__':
main()
PYTHON_SCRIPT
- name: Commit and push changes
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: 'docs: auto-update README.md with latest rules'
file_pattern: 'README.md'
commit_user_name: 'github-actions[bot]'
commit_user_email: 'github-actions[bot]@users.noreply.github.com'
commit_author: 'github-actions[bot] <github-actions[bot]@users.noreply.github.com>'