From 0c284161eb0d26120816c4909ac8230ee555ad1c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 23:45:09 -0700 Subject: [PATCH] Validate manifests in CI (#22708) * Validate manifests * Fix mode * Activate venv * Validate manifests after installing HA which includes voluptuous --- .circleci/config.yml | 7 +++ script/manifest/validate.py | 86 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100755 script/manifest/validate.py diff --git a/.circleci/config.yml b/.circleci/config.yml index b1fdc2be93b..d31fd512abd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -99,6 +99,13 @@ jobs: mypy $TYPING_FILES - install + + - run: + name: validate manifests + command: | + . venv/bin/activate + python script/manifest/validate.py + - run: name: run gen_requirements_all command: | diff --git a/script/manifest/validate.py b/script/manifest/validate.py new file mode 100755 index 00000000000..d0d59529d20 --- /dev/null +++ b/script/manifest/validate.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Validate all integrations have manifests and that they are valid.""" +import json +import pathlib +import sys + +import voluptuous as vol +from voluptuous.humanize import humanize_error + + +MANIFEST_SCHEMA = vol.Schema({ + vol.Required('domain'): str, + vol.Required('name'): str, + vol.Required('documentation'): str, + vol.Required('requirements'): [str], + vol.Required('dependencies'): [str], + vol.Required('codeowners'): [str], +}) + + +components_path = pathlib.Path('homeassistant/components') + + +def validate_integration(path): + """Validate that an integrations has a valid manifest.""" + errors = [] + path = pathlib.Path(path) + + manifest_path = path / 'manifest.json' + + if not manifest_path.is_file(): + errors.append('File manifest.json not found') + return errors # Fatal error + + try: + manifest = json.loads(manifest_path.read_text()) + except ValueError as err: + errors.append("Manifest contains invalid JSON: {}".format(err)) + return errors # Fatal error + + try: + MANIFEST_SCHEMA(manifest) + except vol.Invalid as err: + errors.append(humanize_error(manifest, err)) + + if manifest['domain'] != path.name: + errors.append('Domain does not match dir name') + + for dep in manifest['dependencies']: + dep_manifest = path.parent / dep / 'manifest.json' + if not dep_manifest.is_file(): + errors.append("Unable to find dependency {}".format(dep)) + + return errors + + +def validate_all(): + """Validate all integrations.""" + invalid = [] + + for fil in components_path.iterdir(): + if fil.is_file() or fil.name == '__pycache__': + continue + + errors = validate_integration(fil) + + if errors: + invalid.append((fil, errors)) + + if not invalid: + return 0 + + print("Found invalid manifests") + print() + + for integration, errors in invalid: + print(integration) + for error in errors: + print("*", error) + print() + + return 1 + + +if __name__ == '__main__': + sys.exit(validate_all())