82b5d50c34
Don't import a specific draft validator (it isn't used anyway, and the validator is picked up from the schema's version)
136 lines
3.9 KiB
Python
Executable File
136 lines
3.9 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
#
|
|
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
#
|
|
usage = """
|
|
Validates a Calamares config file -- YAML syntax -- against a schema.
|
|
|
|
The schema is also written in YAML syntax, but the schema itself
|
|
is JSON-schema. This is possible because all JSON is YAML, and most
|
|
YAML is JSON. The limited subset of YAML that Calamares uses is
|
|
JSON-representable, anyway.
|
|
|
|
Usage:
|
|
configvalidator.py <schema> <file> ...
|
|
configvalidator.py -m <module>
|
|
configvalidator.py -x
|
|
|
|
Exits with value 0 on success, otherwise:
|
|
1 on missing dependencies
|
|
2 on invalid command-line arguments
|
|
3 on missing files
|
|
4 if files have invalid syntax
|
|
5 if files fail to validate
|
|
Use -x as only command-line argument to check the imports only.
|
|
|
|
Use -m <module> as shorthand for standard paths in src/modules/<module>/
|
|
"""
|
|
|
|
# The schemata originally lived outside the Calamares repository,
|
|
# without documented tooling. By putting them in the repository
|
|
# with the example files and explicit tooling, there's a better
|
|
# chance of them catching problems and acting as documentation.
|
|
|
|
dependencies = """
|
|
Dependencies for this tool are: py-yaml and py-jsonschema.
|
|
|
|
https://pyyaml.org/
|
|
https://github.com/Julian/jsonschema
|
|
|
|
Simple installation is `pip install pyyaml jsonschema`
|
|
"""
|
|
|
|
ERR_IMPORT, ERR_USAGE, ERR_FILE_NOT_FOUND, ERR_SYNTAX, ERR_INVALID = range(1,6)
|
|
|
|
### DEPENDENCIES
|
|
#
|
|
#
|
|
try:
|
|
from jsonschema import validate, SchemaError, ValidationError
|
|
from yaml import safe_load, YAMLError
|
|
except ImportError as e:
|
|
print(e)
|
|
print(dependencies)
|
|
exit(ERR_IMPORT)
|
|
|
|
from os.path import exists
|
|
import sys
|
|
|
|
### INPUT VALIDATION
|
|
#
|
|
#
|
|
if len(sys.argv) < 3:
|
|
# Special-case: called with -x to just test the imports
|
|
if len(sys.argv) == 2 and sys.argv[1] == "-x":
|
|
exit(0)
|
|
print(usage)
|
|
exit(ERR_USAGE)
|
|
|
|
if len(sys.argv) == 3 and sys.argv[1] == "-m":
|
|
module = sys.argv[2]
|
|
schema_file_name = f"src/modules/{module}/{module}.schema.yaml"
|
|
config_file_names = [ f"src/modules/{module}/{module}.conf" ]
|
|
else:
|
|
schema_file_name = sys.argv[1]
|
|
config_file_names = sys.argv[2:]
|
|
|
|
if not exists(schema_file_name):
|
|
print(usage)
|
|
print("\nSchema file '{}' does not exist.".format(schema_file_name))
|
|
exit(ERR_FILE_NOT_FOUND)
|
|
for f in config_file_names:
|
|
if not exists(f):
|
|
print(usage)
|
|
print("\nYAML file '{}' does not exist.".format(f))
|
|
exit(ERR_FILE_NOT_FOUND)
|
|
|
|
### FILES SYNTAX CHECK
|
|
#
|
|
#
|
|
with open(schema_file_name, "r") as data:
|
|
try:
|
|
schema = safe_load(data)
|
|
except YAMLError as e:
|
|
print("Schema error: {} {}.".format(e.problem, e.problem_mark))
|
|
print("\nSchema file '{}' is invalid YAML.".format(schema_file_name))
|
|
exit(ERR_SYNTAX)
|
|
|
|
try:
|
|
validate(instance={}, schema=schema)
|
|
# While developing the schemata, get full exceptions from schema failure
|
|
except SchemaError as e:
|
|
print(e)
|
|
print("\nSchema file '{}' is invalid JSON-Schema.".format(schema_file_name))
|
|
exit(ERR_INVALID)
|
|
except ValidationError:
|
|
# Just means that empty isn't valid, but the Schema itself is
|
|
pass
|
|
|
|
configs = []
|
|
for f in config_file_names:
|
|
config = None
|
|
with open(f, "r") as data:
|
|
try:
|
|
config = safe_load(data)
|
|
except YAMLError as e:
|
|
print("YAML error: {} {}.".format(e.problem, e.problem_mark))
|
|
print("\nYAML file '{}' is invalid.".format(f))
|
|
exit(ERR_SYNTAX)
|
|
if config is None:
|
|
print("YAML file '{}' is empty.".format(f))
|
|
configs.append(config)
|
|
|
|
assert len(configs) == len(config_file_names), "Not all configurations loaded."
|
|
|
|
### SCHEMA VALIDATION
|
|
#
|
|
#
|
|
for c, f in zip(configs, config_file_names):
|
|
try:
|
|
validate(instance=c, schema=schema)
|
|
except ValidationError as e:
|
|
print(e)
|
|
print("\nConfig file '{}' does not validate in schema.".format(f))
|
|
exit(ERR_INVALID)
|