Skip to content

Commit b6cf3da

Browse files
authored
Update validate-yml.py
1 parent aab6952 commit b6cf3da

File tree

1 file changed

+43
-63
lines changed

1 file changed

+43
-63
lines changed

scripts/validate-yml.py

Lines changed: 43 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,12 @@
2020
import re
2121
import sys
2222

23-
# ANSI color codes
23+
# ANSI color codes (used sparingly)
2424
RED = "\033[91m"
2525
GREEN = "\033[92m"
2626
YELLOW = "\033[93m"
2727
RESET = "\033[0m"
2828

29-
# -----------------------
30-
# Required field definitions
31-
# -----------------------
32-
3329
REQUIRED_FIELDS = {
3430
'name': str,
3531
'repo': str,
@@ -45,90 +41,74 @@
4541
'avatar': str,
4642
}
4743

48-
# -----------------------
49-
# Regex patterns for validation
50-
# -----------------------
51-
5244
REPO_REGEX = re.compile(r'^[\w\-]+/[\w\-\.]+$')
5345
YOUTUBE_REGEX = re.compile(r'https://img\.youtube\.com/vi/([^/]+)/[^/]+\.jpg')
5446

55-
# -----------------------
56-
# Screenshot helper
57-
# -----------------------
58-
5947
def is_valid_screenshot_url(url: str) -> bool:
6048
return url.startswith("https://") or url.startswith("./Assets/")
6149

62-
# -----------------------
63-
# Entry validation logic
64-
# -----------------------
65-
6650
def validate_entry(entry, is_plugin=True, index=0):
6751
name = entry.get('name', f'<Unnamed {index}>')
6852
errors = []
6953

7054
for field, field_type in REQUIRED_FIELDS.items():
7155
if field not in entry:
72-
errors.append(f"❌ Missing field `{field}` in '{name}'\n ➤ Example: {field}: {field_type.__name__}")
56+
errors.append(
57+
f"{RED}❌ Missing field `{field}` in plugin '{name}'\n ➤ Example: {field}: {field_type.__name__}{RESET}"
58+
)
7359
elif not isinstance(entry[field], field_type):
74-
example_value = "\"1.20.1-1.21.4\"" if field == "mc_versions" else field_type.__name__
75-
errors.append(f"❌ `{field}` must be {field_type.__name__} in '{name}'\n ➤ Example: {field}: {example_value}")
60+
example = "\"1.20.4-1.21.1\"" if field == "mc_versions" else f"{field_type.__name__}"
61+
errors.append(
62+
f"{RED}❌ `{field}` must be {field_type.__name__} in plugin '{name}'\n ➤ Example: {field}: {example}{RESET}"
63+
)
7664

77-
# Repo format check
7865
repo = entry.get('repo')
7966
if repo and not REPO_REGEX.match(repo):
80-
errors.append(f"❌ `repo` in '{name}' must follow format 'owner/repo'\n ➤ Example: repo: \"GarlicRot/AutoBucket\"")
67+
errors.append(f"{RED}❌ `repo` must be in 'owner/repo' format in plugin '{name}'\n ➤ Example: repo: \"GarlicRot/AutoBucket\"{RESET}")
8168

82-
# Creator block checks
8369
creator = entry.get('creator', {})
8470
if not isinstance(creator, dict):
85-
errors.append(f"❌ `creator` must be a dictionary in '{name}'")
71+
errors.append(f"{RED}❌ `creator` must be a dictionary in plugin '{name}'{RESET}")
8672
else:
87-
for cfield, ctype in REQUIRED_CREATOR_FIELDS.items():
88-
if cfield not in creator:
89-
errors.append(f"❌ Missing `creator.{cfield}` in '{name}'\n ➤ Example: creator.{cfield}: \"https://github.com/user.png\"")
90-
elif not isinstance(creator[cfield], ctype):
91-
errors.append(f"❌ `creator.{cfield}` must be {ctype.__name__} in '{name}'")
92-
elif cfield in ['url', 'avatar'] and not creator[cfield].startswith("https://"):
93-
errors.append(f"❌ `creator.{cfield}` must start with https:// in '{name}'")
94-
95-
# Screenshot block checks
96-
for ss in entry.get('screenshots', []):
97-
if not isinstance(ss, dict):
98-
errors.append(f"❌ Each screenshot must be a dictionary in '{name}'")
73+
for field, field_type in REQUIRED_CREATOR_FIELDS.items():
74+
if field not in creator:
75+
errors.append(
76+
f"{RED}❌ Missing `creator.{field}` in plugin '{name}'\n ➤ Example: creator.{field}: \"https://github.com/user.png\"{RESET}"
77+
)
78+
elif not isinstance(creator[field], field_type):
79+
errors.append(f"{RED}❌ `creator.{field}` must be {field_type.__name__} in plugin '{name}'{RESET}")
80+
elif field in ['url', 'avatar'] and not creator[field].startswith("https://"):
81+
errors.append(f"{RED}❌ `creator.{field}` must start with https:// in plugin '{name}'{RESET}")
82+
83+
for screenshot in entry.get("screenshots", []):
84+
if not isinstance(screenshot, dict):
85+
errors.append(f"{RED}❌ Screenshot must be a dictionary in plugin '{name}'{RESET}")
9986
continue
87+
if "url" not in screenshot or not isinstance(screenshot["url"], str):
88+
errors.append(f"{RED}❌ Screenshot missing valid `url` in plugin '{name}'{RESET}")
89+
elif not is_valid_screenshot_url(screenshot["url"]):
90+
errors.append(f"{RED}❌ Screenshot `url` must start with https:// or ./Assets/ in plugin '{name}'{RESET}")
91+
elif screenshot["url"].startswith("https://img.youtube.com/") and not YOUTUBE_REGEX.match(screenshot["url"]):
92+
errors.append(f"{RED}❌ YouTube screenshot URL must match https://img.youtube.com/vi/<id>/hqdefault.jpg in plugin '{name}'{RESET}")
93+
if "alt" in screenshot and not isinstance(screenshot["alt"], str):
94+
errors.append(f"{RED}❌ Screenshot `alt` must be a string in plugin '{name}'{RESET}")
95+
if "width" in screenshot and not isinstance(screenshot["width"], int):
96+
errors.append(f"{RED}❌ Screenshot `width` must be an integer in plugin '{name}'{RESET}")
10097

101-
if 'url' not in ss or not isinstance(ss['url'], str):
102-
errors.append(f"❌ Missing or invalid `url` in a screenshot of '{name}'")
103-
elif not is_valid_screenshot_url(ss['url']):
104-
errors.append(f"❌ Screenshot `url` in '{name}' must start with https:// or ./Assets/")
105-
elif ss['url'].startswith("https://img.youtube.com/") and not YOUTUBE_REGEX.match(ss['url']):
106-
errors.append(f"❌ YouTube thumbnail `url` in '{name}' must match: https://img.youtube.com/vi/<video-id>/0.jpg")
107-
108-
if 'alt' in ss and not isinstance(ss['alt'], str):
109-
errors.append(f"❌ `alt` must be a string in screenshot of '{name}'")
110-
if 'width' in ss and not isinstance(ss['width'], int):
111-
errors.append(f"❌ `width` must be an integer in screenshot of '{name}'")
112-
113-
# Plugin-only checks
11498
if is_plugin:
115-
if 'mc_versions' not in entry:
116-
errors.append(f"❌ Missing `mc_versions` in plugin '{name}'\n ➤ Example: mc_versions: \"1.20.4-1.21.1\"")
117-
elif not isinstance(entry['mc_versions'], str):
118-
errors.append(f"❌ `mc_versions` must be a string in plugin '{name}'")
99+
if "mc_versions" not in entry:
100+
errors.append(f"{RED}❌ Missing `mc_versions` in plugin '{name}'\n ➤ Example: mc_versions: \"1.20.4-1.21.1\"{RESET}")
101+
elif not isinstance(entry["mc_versions"], str):
102+
errors.append(f"{RED}❌ `mc_versions` must be a string in plugin '{name}'\n ➤ Example: mc_versions: \"1.20.4-1.21.1\"{RESET}")
119103

120-
if 'is_core' in entry and not isinstance(entry['is_core'], bool):
121-
errors.append(f"❌ `is_core` must be a boolean in plugin '{name}'")
104+
if "is_core" in entry and not isinstance(entry["is_core"], bool):
105+
errors.append(f"{RED}❌ `is_core` must be a boolean in plugin '{name}'{RESET}")
122106

123107
return errors
124108

125-
# -----------------------
126-
# Entrypoint
127-
# -----------------------
128-
129109
def main():
130110
try:
131-
with open('data/plugins-and-themes.yml', 'r') as f:
111+
with open("data/plugins-and-themes.yml", "r") as f:
132112
data = yaml.safe_load(f)
133113
except Exception as e:
134114
print(f"{RED}❌ Failed to load YAML: {e}{RESET}")
@@ -147,9 +127,9 @@ def main():
147127
all_errors.extend(validate_entry(theme, is_plugin=False, index=i))
148128

149129
if all_errors:
150-
print(f"{YELLOW}\n--- VALIDATION ERRORS ---{RESET}\n")
151-
for err in all_errors:
152-
print(err)
130+
print(f"\n{YELLOW}--- VALIDATION ERRORS ---{RESET}\n")
131+
for error in all_errors:
132+
print(error)
153133
print(f"\n{RED}{len(all_errors)} issue(s) found. Fix the above problems to continue.{RESET}")
154134
sys.exit(1)
155135
else:

0 commit comments

Comments
 (0)