FreeCAD
184 строки · 8.0 Кб
1# SPDX-License-Identifier: LGPL-2.1-or-later
2# ***************************************************************************
3# * *
4# * Copyright (c) 2022 FreeCAD Project Association *
5# * *
6# * This file is part of FreeCAD. *
7# * *
8# * FreeCAD is free software: you can redistribute it and/or modify it *
9# * under the terms of the GNU Lesser General Public License as *
10# * published by the Free Software Foundation, either version 2.1 of the *
11# * License, or (at your option) any later version. *
12# * *
13# * FreeCAD is distributed in the hope that it will be useful, but *
14# * WITHOUT ANY WARRANTY; without even the implied warranty of *
15# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16# * Lesser General Public License for more details. *
17# * *
18# * You should have received a copy of the GNU Lesser General Public *
19# * License along with FreeCAD. If not, see *
20# * <https://www.gnu.org/licenses/>. *
21# * *
22# ***************************************************************************
23
24""" Metadata validation functions """
25
26from typing import List27
28import FreeCAD29
30from Addon import Addon31from addonmanager_metadata import Metadata32import NetworkManager33
34
35class MetadataValidators:36"""A collection of tools for validating various pieces of metadata. Prints37validation information to the console."""
38
39def validate_all(self, repos):40"""Developer tool: check all repos for validity and print report"""41
42FreeCAD.Console.PrintMessage("\n\nADDON MANAGER DEVELOPER MODE CHECKS\n")43FreeCAD.Console.PrintMessage("-----------------------------------\n")44
45counter = 046for addon in repos:47counter += 148if addon.metadata is not None:49self.validate_package_xml(addon)50elif addon.repo_type == Addon.Kind.MACRO:51if addon.macro.parsed:52if len(addon.macro.icon) == 0 and len(addon.macro.xpm) == 0:53FreeCAD.Console.PrintMessage(54f"Macro '{addon.name}' does not have an icon\n"55)56else:57FreeCAD.Console.PrintMessage(58f"Addon '{addon.name}' does not have a package.xml file\n"59)60
61FreeCAD.Console.PrintMessage("-----------------------------------\n\n")62
63def validate_package_xml(self, addon: Addon):64"""Check the package.xml file for the specified Addon"""65if addon.metadata is None:66return67
68# The package.xml standard has some required elements that the basic XML69# reader is not actually checking for. In developer mode, actually make sure70# that all the rules are being followed for each element.71
72errors = []73
74errors.extend(self.validate_top_level(addon))75errors.extend(self.validate_content(addon))76
77if len(errors) > 0:78FreeCAD.Console.PrintError(f"Errors found in package.xml file for '{addon.name}'\n")79for error in errors:80FreeCAD.Console.PrintError(f" * {error}\n")81
82def validate_content(self, addon: Addon) -> List[str]:83"""Validate the Content items for this Addon"""84errors = []85contents = addon.metadata.content86
87missing_icon = True88if addon.metadata.icon and len(addon.metadata.icon) > 0:89missing_icon = False90else:91if "workbench" in contents:92wb = contents["workbench"][0]93if wb.icon:94missing_icon = False95if missing_icon:96errors.append("No <icon> element found, or <icon> element is invalid")97
98if "workbench" in contents:99for wb in contents["workbench"]:100errors.extend(self.validate_workbench_metadata(wb))101
102if "preferencepack" in contents:103for wb in contents["preferencepack"]:104errors.extend(self.validate_preference_pack_metadata(wb))105
106return errors107
108def validate_top_level(self, addon: Addon) -> List[str]:109"""Check for the presence of the required top-level elements"""110errors = []111if not addon.metadata.name or len(addon.metadata.name) == 0:112errors.append("No top-level <name> element found, or <name> element is empty")113if not addon.metadata.version:114errors.append("No top-level <version> element found, or <version> element is invalid")115if not addon.metadata.description or len(addon.metadata.description) == 0:116errors.append(117"No top-level <description> element found, or <description> element " "is invalid"118)119
120maintainers = addon.metadata.maintainer121if len(maintainers) == 0:122errors.append("No top-level <maintainers> found, at least one is required")123for maintainer in maintainers:124if len(maintainer.email) == 0:125errors.append(f"No email address specified for maintainer '{maintainer.name}'")126
127licenses = addon.metadata.license128if len(licenses) == 0:129errors.append("No top-level <license> found, at least one is required")130
131urls = addon.metadata.url132errors.extend(self.validate_urls(urls))133return errors134
135@staticmethod136def validate_urls(urls) -> List[str]:137"""Check the URLs provided by the addon"""138errors = []139if len(urls) == 0:140errors.append("No <url> elements found, at least a repo url must be provided")141else:142found_repo = False143found_readme = False144for url in urls:145if url.type == "repository":146found_repo = True147if len(url.branch) == 0:148errors.append("<repository> element is missing the 'branch' attribute")149elif url.type == "readme":150found_readme = True151location = url.location152p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(location)153if not p:154errors.append(f"Could not access specified readme at {location}")155else:156p = p.data().decode("utf8")157if "<html" in p or "<!DOCTYPE html>" in p:158pass159else:160errors.append(161f"Readme data found at {location}"162" does not appear to be rendered HTML"163)164if not found_repo:165errors.append("No repo url specified")166if not found_readme:167errors.append("No readme url specified (not required, but highly recommended)")168return errors169
170@staticmethod171def validate_workbench_metadata(workbench: Metadata) -> List[str]:172"""Validate the required element(s) for a workbench"""173errors = []174if not workbench.classname or len(workbench.classname) == 0:175errors.append("No <classname> specified for workbench")176return errors177
178@staticmethod179def validate_preference_pack_metadata(pack: Metadata) -> List[str]:180"""Validate the required element(s) for a preference pack"""181errors = []182if not pack.name or len(pack.name) == 0:183errors.append("No <name> specified for preference pack")184return errors185