1414 authors Verify GitHub handles and author URLs only
1515 paths Verify registry paths only
1616 registry Verify registry authors exist in authors.yaml
17+ schema Verify YAML files match their JSON schemas
1718"""
1819
1920import yaml
2021import requests
2122import sys
23+ import json
2224from pathlib import Path
2325
26+ try :
27+ from jsonschema import validate , ValidationError
28+
29+ HAS_JSONSCHEMA = True
30+ except ImportError :
31+ HAS_JSONSCHEMA = False
32+
2433
2534def check_github_handle (username ):
2635 """Check if a GitHub handle exists."""
@@ -38,7 +47,7 @@ def check_github_handle(username):
3847def check_url (url ):
3948 """Check if a URL is accessible."""
4049 # Skip x.com URLs as they block HEAD requests
41- if ' x.com' in url :
50+ if " x.com" in url :
4251 return True , "skipped (x.com)"
4352
4453 try :
@@ -64,16 +73,16 @@ def verify_authors(authors):
6473 failed_handles .append (f"{ username } ({ error } )" )
6574 print (f" ❌ { error } " )
6675 else :
67- print (f " ✓ OK" )
76+ print (" ✓ OK" )
6877
6978 # Verify URLs
7079 print ("\n === Verifying Author URLs ===\n " )
7180 for username , details in authors .items ():
7281 print (f"Checking URLs for { username } ..." )
7382
7483 # Check website URL
75- if ' website' in details :
76- url = details [' website' ]
84+ if " website" in details :
85+ url = details [" website" ]
7786 success , error = check_url (url )
7887 if not success :
7988 failed_urls .append (f"{ username } .website: { url } ({ error } )" )
@@ -84,8 +93,8 @@ def verify_authors(authors):
8493 print (f" ✓ Website URL OK: { url } " )
8594
8695 # Check avatar URL
87- if ' avatar' in details :
88- url = details [' avatar' ]
96+ if " avatar" in details :
97+ url = details [" avatar" ]
8998 success , error = check_url (url )
9099 if not success :
91100 failed_urls .append (f"{ username } .avatar: { url } ({ error } )" )
@@ -105,8 +114,8 @@ def verify_registry_authors(registry, authors):
105114 print ("\n === Verifying Registry Authors ===\n " )
106115 registry_authors = set ()
107116 for entry in registry :
108- if ' authors' in entry :
109- for author in entry [' authors' ]:
117+ if " authors" in entry :
118+ for author in entry [" authors" ]:
110119 registry_authors .add (author )
111120
112121 print (f"Found { len (registry_authors )} unique authors in registry.yaml" )
@@ -129,28 +138,82 @@ def verify_paths(registry, repo_root):
129138 print (f"Found { len (registry )} cookbooks in registry.yaml" )
130139
131140 for entry in registry :
132- if ' path' in entry :
133- path = entry [' path' ]
141+ if " path" in entry :
142+ path = entry [" path" ]
134143 full_path = repo_root / path
135- title = entry .get (' title' , ' Unknown' )
144+ title = entry .get (" title" , " Unknown" )
136145
137146 if not full_path .exists ():
138147 missing_paths .append (f"{ path } (title: { title } )" )
139148 print (f" ❌ Path not found: { path } " )
140149 else :
141150 print (f" ✓ { path } " )
142151 else :
143- missing_paths .append (f"Entry missing 'path' field (title: { entry .get ('title' , 'Unknown' )} )" )
152+ missing_paths .append (
153+ f"Entry missing 'path' field (title: { entry .get ('title' , 'Unknown' )} )"
154+ )
144155 print (f" ❌ Entry missing 'path' field: { entry .get ('title' , 'Unknown' )} " )
145156
146157 return missing_paths
147158
148159
160+ def verify_schemas (repo_root , authors , registry ):
161+ """Verify YAML files match their JSON schemas."""
162+ if not HAS_JSONSCHEMA :
163+ print ("\n ⚠️ Skipping schema validation (jsonschema not installed)" )
164+ return []
165+
166+ schema_errors = []
167+
168+ print ("\n === Verifying JSON Schemas ===\n " )
169+
170+ # Verify authors.yaml against schema
171+ authors_schema_path = repo_root / ".github" / "authors_schema.json"
172+ if authors_schema_path .exists ():
173+ print ("Checking authors.yaml against schema..." )
174+ try :
175+ with open (authors_schema_path , "r" ) as f :
176+ authors_schema = json .load (f )
177+ validate (instance = authors , schema = authors_schema )
178+ print (" ✓ authors.yaml matches schema" )
179+ except ValidationError as e :
180+ schema_errors .append (f"authors.yaml: { e .message } at { '.' .join (str (p ) for p in e .path )} " )
181+ print (f" ❌ authors.yaml schema validation failed: { e .message } " )
182+ except json .JSONDecodeError as e :
183+ schema_errors .append (f"authors_schema.json: Invalid JSON - { e } " )
184+ print (f" ❌ authors_schema.json is invalid: { e } " )
185+ else :
186+ print (" ⊘ authors_schema.json not found, skipping" )
187+
188+ # Verify registry.yaml against schema
189+ registry_schema_path = repo_root / ".github" / "registry_schema.json"
190+ if registry_schema_path .exists ():
191+ print ("\n Checking registry.yaml against schema..." )
192+ try :
193+ with open (registry_schema_path , "r" ) as f :
194+ registry_schema = json .load (f )
195+ validate (instance = registry , schema = registry_schema )
196+ print (" ✓ registry.yaml matches schema" )
197+ except ValidationError as e :
198+ path_str = "." .join (str (p ) for p in e .path ) if e .path else "root"
199+ schema_errors .append (f"registry.yaml: { e .message } at { path_str } " )
200+ print (f" ❌ registry.yaml schema validation failed: { e .message } " )
201+ if e .path :
202+ print (f" at path: { path_str } " )
203+ except json .JSONDecodeError as e :
204+ schema_errors .append (f"registry_schema.json: Invalid JSON - { e } " )
205+ print (f" ❌ registry_schema.json is invalid: { e } " )
206+ else :
207+ print (" ⊘ registry_schema.json not found, skipping" )
208+
209+ return schema_errors
210+
211+
149212def main ():
150213 # Parse command line argument
151214 command = sys .argv [1 ] if len (sys .argv ) > 1 else "all"
152215
153- if command not in ["all" , "authors" , "paths" , "registry" ]:
216+ if command not in ["all" , "authors" , "paths" , "registry" , "schema" ]:
154217 print (f"Unknown command: { command } " )
155218 print (__doc__ )
156219 sys .exit (1 )
@@ -163,19 +226,20 @@ def main():
163226 authors = None
164227 registry = None
165228
166- if command in ["all" , "authors" , "registry" ]:
167- with open (authors_path , 'r' ) as f :
229+ if command in ["all" , "authors" , "registry" , "schema" ]:
230+ with open (authors_path , "r" ) as f :
168231 authors = yaml .safe_load (f )
169232
170- if command in ["all" , "paths" , "registry" ]:
171- with open (registry_path , 'r' ) as f :
233+ if command in ["all" , "paths" , "registry" , "schema" ]:
234+ with open (registry_path , "r" ) as f :
172235 registry = yaml .safe_load (f )
173236
174237 # Run verifications based on command
175238 failed_handles = []
176239 failed_urls = []
177240 missing_authors = []
178241 missing_paths = []
242+ schema_errors = []
179243
180244 if command in ["all" , "authors" ]:
181245 failed_handles , failed_urls = verify_authors (authors )
@@ -186,6 +250,9 @@ def main():
186250 if command in ["all" , "paths" ]:
187251 missing_paths = verify_paths (registry , repo_root )
188252
253+ if command in ["all" , "schema" ]:
254+ schema_errors = verify_schemas (repo_root , authors , registry )
255+
189256 # Report results
190257 has_failures = False
191258
@@ -213,6 +280,12 @@ def main():
213280 print (f" - { path } " )
214281 has_failures = True
215282
283+ if schema_errors :
284+ print ("\n ❌ The following schema validation errors occurred:" )
285+ for error in schema_errors :
286+ print (f" - { error } " )
287+ has_failures = True
288+
216289 if has_failures :
217290 sys .exit (1 )
218291 else :
0 commit comments