#!/usr/bin/python3 import gi import git import os import sys import logging from logging import error, info from gi.repository import GLib gi.require_version("Modulemd", "2.0") from gi.repository import Modulemd # noqa logging.getLogger().setLevel(logging.INFO) def do_validate(filename): # Valid filenames must end in ".yaml" to be properly included by Pungi if not filename.endswith(".yaml"): error( "{} does not end with .yaml. It will not be included by " "Pungi. If this file does not contain defaults, it should " "be added to the tests/exclusions.txt file.".format(filename) ) return False, None # The files must parse correctly idx = Modulemd.ModuleIndex() try: (objects, failures) = idx.update_from_file(filename, True) except GLib.Error as e: error("{}".format(e.message)) return False, None if failures: for failure in failures: error( "Failed subdocument ({}): \n{}\n".format( failure.get_gerror().message, failure.get_yaml() ) ) return False, None # There must be exactly one object per file names = idx.get_module_names() if len(names) != 1: error("There must be exactly one module represented by this file") return False, None # The files must have exactly one Modulemd.Defaults object module = idx.get_module(names[0]) defaults = module.get_defaults() if defaults is None: error( "No defaults document provided for {}".format( module.props.module_name ) ) return False, None # The files must not contain any streams if len(module.get_stream_names()) != 0: error("File included a stream document.") return False, None # Filenames must match their contents expected_name = os.path.basename(filename).rsplit(".", maxsplit=1)[0] if expected_name != defaults.props.module_name: error( 'Module name "{}" doesn\'t match filename "{}.yaml"'.format( defaults.props.module_name, expected_name ) ) return False, None default_stream = defaults.get_default_stream() if default_stream: # Default streams must also appear in the profiles list if defaults.get_default_profiles_for_stream(default_stream) is None: error( "Stream '{}' is missing from the profiles for '{}'".format( default_stream, defaults.get_module_name() ) ) return False, None # Modules in Fedora must not specify "Intents" # TODO: This needs a new interface exposed in libmodulemd v2 info("{} is valid".format(filename)) return (True, idx) def main(): result = os.EX_OK if sys.argv[1]: script_dir = os.path.abspath(sys.argv[1]) else: script_dir = os.path.dirname(os.path.realpath(__file__)) defaults_dir = os.path.abspath(os.path.join(script_dir, '..')) overrides_dir = os.path.join(defaults_dir, 'overrides') # Get the repo we're running in repo = git.Repo(defaults_dir, search_parent_directories=True) # Get the list of files in this repository files = [x for (x, y) in repo.index.entries.keys()] # Get the list of excluded files exclusions = [] with open(os.path.join(script_dir, "exclusions.txt")) as f: for line in f: stripped_line = line.strip() if not stripped_line or stripped_line.startswith("#"): # Ignore comments and empty lines continue exclusions.append(line.strip()) # Validate all of the files for file in files: excluded = False for excl in exclusions: if file.startswith(excl): excluded = True break if not excluded: (valid, idx) = do_validate(file) if not valid: error("{} failed to validate".format(file)) result = os.EX_DATAERR if result == os.EX_DATAERR: return result # For sanity's sake, also do a merge of all the defaults to make sure no # conflicts arise that weren't detected by the above tests. This should be # impossible. try: idx = Modulemd.ModuleIndex() idx.update_from_defaults_directory(path=defaults_dir, strict=True) except GLib.Error as e: error("Could not merge all defaults: {}".format(e.message)) result = os.EX_DATAERR if result == os.EX_OK: info("Merging all of the documents encountered no errors.") print("\nDefault streams (Runtime):") print("================") for m, s in idx.get_default_streams().items(): print("{}:{}".format(m, s)) try: idx = Modulemd.ModuleIndex() idx.update_from_defaults_directory(path=defaults_dir, overrides_path=overrides_dir, strict=True) except GLib.Error as e: error("Could not merge all defaults: {}".format(e.message)) result = os.EX_DATAERR if result == os.EX_OK: info("Merging all of the documents encountered no errors.") print("\nDefault streams (Buildroot):") print("================") for m, s in idx.get_default_streams().items(): print("{}:{}".format(m, s)) return result if __name__ == "__main__": sys.exit(main())