-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathaddonmanager_devmode_metadata_checker.py
184 lines (157 loc) · 8 KB
/
addonmanager_devmode_metadata_checker.py
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
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
"""Metadata validation functions"""
from typing import List
import FreeCAD
from Addon import Addon
from addonmanager_metadata import Metadata
import NetworkManager
class MetadataValidators:
"""A collection of tools for validating various pieces of metadata. Prints
validation information to the console."""
def validate_all(self, repos):
"""Developer tool: check all repos for validity and print report"""
FreeCAD.Console.PrintMessage("\n\nADDON MANAGER DEVELOPER MODE CHECKS\n")
FreeCAD.Console.PrintMessage("-----------------------------------\n")
counter = 0
for addon in repos:
counter += 1
if addon.metadata is not None:
self.validate_package_xml(addon)
elif addon.repo_type == Addon.Kind.MACRO:
if addon.macro.parsed:
if len(addon.macro.icon) == 0 and len(addon.macro.xpm) == 0:
FreeCAD.Console.PrintMessage(
f"Macro '{addon.name}' does not have an icon\n"
)
else:
FreeCAD.Console.PrintMessage(
f"Addon '{addon.name}' does not have a package.xml file\n"
)
FreeCAD.Console.PrintMessage("-----------------------------------\n\n")
def validate_package_xml(self, addon: Addon):
"""Check the package.xml file for the specified Addon"""
if addon.metadata is None:
return
# The package.xml standard has some required elements that the basic XML
# reader is not actually checking for. In developer mode, actually make sure
# that all the rules are being followed for each element.
errors = []
errors.extend(self.validate_top_level(addon))
errors.extend(self.validate_content(addon))
if len(errors) > 0:
FreeCAD.Console.PrintError(f"Errors found in package.xml file for '{addon.name}'\n")
for error in errors:
FreeCAD.Console.PrintError(f" * {error}\n")
def validate_content(self, addon: Addon) -> List[str]:
"""Validate the Content items for this Addon"""
errors = []
contents = addon.metadata.content
missing_icon = True
if addon.metadata.icon and len(addon.metadata.icon) > 0:
missing_icon = False
else:
if "workbench" in contents:
wb = contents["workbench"][0]
if wb.icon:
missing_icon = False
if missing_icon:
errors.append("No <icon> element found, or <icon> element is invalid")
if "workbench" in contents:
for wb in contents["workbench"]:
errors.extend(self.validate_workbench_metadata(wb))
if "preferencepack" in contents:
for wb in contents["preferencepack"]:
errors.extend(self.validate_preference_pack_metadata(wb))
return errors
def validate_top_level(self, addon: Addon) -> List[str]:
"""Check for the presence of the required top-level elements"""
errors = []
if not addon.metadata.name or len(addon.metadata.name) == 0:
errors.append("No top-level <name> element found, or <name> element is empty")
if not addon.metadata.version:
errors.append("No top-level <version> element found, or <version> element is invalid")
if not addon.metadata.description or len(addon.metadata.description) == 0:
errors.append(
"No top-level <description> element found, or <description> element " "is invalid"
)
maintainers = addon.metadata.maintainer
if len(maintainers) == 0:
errors.append("No top-level <maintainers> found, at least one is required")
for maintainer in maintainers:
if len(maintainer.email) == 0:
errors.append(f"No email address specified for maintainer '{maintainer.name}'")
licenses = addon.metadata.license
if len(licenses) == 0:
errors.append("No top-level <license> found, at least one is required")
urls = addon.metadata.url
errors.extend(self.validate_urls(urls))
return errors
@staticmethod
def validate_urls(urls) -> List[str]:
"""Check the URLs provided by the addon"""
errors = []
if len(urls) == 0:
errors.append("No <url> elements found, at least a repo url must be provided")
else:
found_repo = False
found_readme = False
for url in urls:
if url.type == "repository":
found_repo = True
if len(url.branch) == 0:
errors.append("<repository> element is missing the 'branch' attribute")
elif url.type == "readme":
found_readme = True
location = url.location
p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(location)
if not p:
errors.append(f"Could not access specified readme at {location}")
else:
p = p.data().decode("utf8")
if "<html" in p or "<!DOCTYPE html>" in p:
pass
else:
errors.append(
f"Readme data found at {location}"
" does not appear to be rendered HTML"
)
if not found_repo:
errors.append("No repo url specified")
if not found_readme:
errors.append("No readme url specified (not required, but highly recommended)")
return errors
@staticmethod
def validate_workbench_metadata(workbench: Metadata) -> List[str]:
"""Validate the required element(s) for a workbench"""
errors = []
if not workbench.classname or len(workbench.classname) == 0:
errors.append("No <classname> specified for workbench")
return errors
@staticmethod
def validate_preference_pack_metadata(pack: Metadata) -> List[str]:
"""Validate the required element(s) for a preference pack"""
errors = []
if not pack.name or len(pack.name) == 0:
errors.append("No <name> specified for preference pack")
return errors