-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathcatalog_linter.py
More file actions
executable file
·135 lines (111 loc) · 4.41 KB
/
Copy pathcatalog_linter.py
File metadata and controls
executable file
·135 lines (111 loc) · 4.41 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
#!/usr/bin/env python3
import argparse
import json
import sys
from pathlib import Path
from difflib import SequenceMatcher
from typing import Any, Dict, Generator, List, Tuple
import jsonschema
import appslib.get_apps_repo as get_apps_repo
from appslib.utils import (
get_antifeatures, # pylint: disable=import-error
get_catalog,
get_categories,
get_graveyard,
get_wishlist,
)
def validate_schema(data: dict, schema_path: Path) -> List[str]:
schema = json.load(schema_path.open("r", encoding="utf-8"))
validator = jsonschema.Draft202012Validator(schema)
return [
f"at .{'.'.join(error.path)}: {error.message}"
for error in validator.iter_errors(data)
]
def validate_schema_pretty(apps_path: Path, data: dict, name: str) -> bool:
schema_path = apps_path / "schemas" / f"{name}.toml.schema.json"
schema_errors = list(validate_schema(data, schema_path))
if schema_errors:
print(f"Error while validating {name} against schema:")
for error in schema_errors:
print(f" - {error}")
print()
return bool(schema_errors)
def check_app(
app: str, infos: Dict[str, Any]
) -> Generator[Tuple[str, bool], None, None]:
if "state" not in infos:
yield "state is missing", True
return
# validate that the app is not (anymore?) in the wishlist
# we use fuzzy matching because the id in catalog may not be the same exact id as in the wishlist
# some entries are ignore-hard-coded, because e.g. radarr an readarr are really different apps...
ignored_wishlist_entries = ["readarr"]
wishlist_matches = [
wish
for wish in get_wishlist()
if wish not in ignored_wishlist_entries
and SequenceMatcher(None, app, wish).ratio() > 0.9
]
if wishlist_matches:
yield f"app seems to be listed in wishlist: {wishlist_matches}", True
# validate that the app is not (anymore?) in the wishlist
# we use fuzzy matching because the id in catalog may not be the same exact id as in the wishlist
# some entries are ignore-hard-coded, because e.g. radarr an readarr are really different apps...
ignored_graveyard_entries = [
"mailman",
"invoiceninja",
"civicrm_drupal7",
"drupal7",
]
graveyard_matches = [
grave
for grave in get_graveyard()
if grave not in ignored_graveyard_entries
and SequenceMatcher(None, app, grave).ratio() > 0.9
]
if graveyard_matches:
yield f"app seems to be listed in graveyard: {graveyard_matches}", True
repo_name = infos.get("url", "").split("/")[-1]
if repo_name != f"{app}_ynh":
yield f"repo name should be {app}_ynh, not in {repo_name}", True
antifeatures = infos.get("antifeatures", [])
for antifeature in antifeatures:
if antifeature not in get_antifeatures():
yield f"unknown antifeature {antifeature}", True
category = infos.get("category")
if not category:
yield "category is missing", True
else:
if category not in get_categories():
yield f"unknown category {category}", True
subtags = infos.get("subtags", [])
for subtag in subtags:
if subtag not in get_categories().get(category, {}).get("subtags", []):
yield f"unknown subtag {category} / {subtag}", False
def check_all_apps() -> bool:
has_errors = False
for app, info in get_catalog().items():
errors = list(check_app(app, info))
if errors:
print(f"{app}:")
for error, is_fatal in errors:
if is_fatal:
has_errors = True
level = "error" if is_fatal else "warning"
print(f" - {level}: {error}")
return has_errors
def main() -> None:
parser = argparse.ArgumentParser()
get_apps_repo.add_args(parser)
args = parser.parse_args()
apps_path = get_apps_repo.from_args(args)
has_errors = False
has_errors |= validate_schema_pretty(apps_path, get_antifeatures(), "antifeatures")
has_errors |= validate_schema_pretty(apps_path, get_catalog(), "apps")
has_errors |= validate_schema_pretty(apps_path, get_categories(), "categories")
has_errors |= validate_schema_pretty(apps_path, get_graveyard(), "graveyard")
has_errors |= validate_schema_pretty(apps_path, get_wishlist(), "wishlist")
has_errors |= check_all_apps()
sys.exit(has_errors)
if __name__ == "__main__":
main()