From 1a218f3c5e237da1d692d3fdc5bdb72122d196f3 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Fri, 3 Jan 2025 14:50:33 +0100 Subject: [PATCH] Improve extra sanity test for docker action group. --- tests/sanity/extra/action-group.json | 3 +- tests/sanity/extra/action-group.py | 127 +++++++++++++++++---------- 2 files changed, 85 insertions(+), 45 deletions(-) diff --git a/tests/sanity/extra/action-group.json b/tests/sanity/extra/action-group.json index 477ca3f8b..db6a92bcb 100644 --- a/tests/sanity/extra/action-group.json +++ b/tests/sanity/extra/action-group.json @@ -2,7 +2,8 @@ "include_symlinks": true, "prefixes": [ "meta/runtime.yml", - "plugins/modules/" + "plugins/modules/", + "tests/sanity/extra/action-group." ], "output": "path-message", "requirements": [ diff --git a/tests/sanity/extra/action-group.py b/tests/sanity/extra/action-group.py index efe31ab91..5ff6f25ff 100755 --- a/tests/sanity/extra/action-group.py +++ b/tests/sanity/extra/action-group.py @@ -3,27 +3,50 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later """Make sure all modules that should show up in the action group.""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type + +from __future__ import annotations import os +import re import yaml +ACTION_GROUPS = { + # The format is as follows: + # * 'pattern': a regular expression matching all module names potentially belonging to the action group; + # * 'exclusions': a list of modules that are not part of the action group; all other modules matching 'pattern' must be part of it; + # * 'doc_fragment': the docs fragment that documents membership of the action group. + 'docker': { + 'pattern': re.compile('^.*$'), + 'exclusions': [ + 'current_container_facts', + ], + 'doc_fragment': 'community.docker.attributes.actiongroup_docker', + }, +} + + def main(): """Main entry point.""" # Load redirects meta_runtime = 'meta/runtime.yml' + self_path = 'tests/sanity/extra/action-group.py' try: with open(meta_runtime, 'rb') as f: data = yaml.safe_load(f) - action_group = data['action_groups']['docker'] + action_groups = data['action_groups'] except Exception as exc: - print('%s: cannot load action group: %s' % (meta_runtime, exc)) + print(f'{meta_runtime}: cannot load action groups: {exc}') return - exclusions = ['current_container_facts'] + for action_group in action_groups: + if action_group not in ACTION_GROUPS: + print(f'{meta_runtime}: found unknown action group {action_group!r}; likely {self_path} needs updating') + for action_group, action_group_data in list(ACTION_GROUPS.items()): + if action_group not in action_groups: + print(f'{meta_runtime}: cannot find action group {action_group!r}; likely {self_path} needs updating') + modules_directory = 'plugins/modules/' modules_suffix = '.py' @@ -32,50 +55,66 @@ def main(): continue module_name = file[:-len(modules_suffix)] - should_be_in_action_group = module_name not in exclusions - - if should_be_in_action_group: - if module_name not in action_group: - print('%s: module %s is not part of docker action group' % (meta_runtime, module_name)) - else: - action_group.remove(module_name) - - path = os.path.join(modules_directory, file) - documentation = [] - in_docs = False - with open(path, 'r', encoding='utf-8') as f: - for line in f: - if line.startswith('DOCUMENTATION ='): - in_docs = True - elif line.startswith(("'''", '"""')) and in_docs: - in_docs = False - elif in_docs: - documentation.append(line) - if in_docs: - print('%s: cannot find DOCUMENTATION end' % (path)) - if not documentation: - print('%s: cannot find DOCUMENTATION' % (path)) - continue + for action_group, action_group_data in ACTION_GROUPS.items(): + action_group_content = action_groups.get(action_group) or [] + path = os.path.join(modules_directory, file) - try: - docs = yaml.safe_load('\n'.join(documentation)) - if not isinstance(docs, dict): - raise Exception('is not a top-level dictionary') - except Exception as exc: - print('%s: cannot load DOCUMENTATION as YAML: %s' % (path, exc)) - continue + if not action_group_data['pattern'].match(module_name): + if module_name in action_group_content: + print(f'{path}: module is in action group {action_group!r} despite not matching its pattern as defined in {self_path}') + continue - docs_fragments = docs.get('extends_documentation_fragment') or [] - is_in_action_group = 'community.docker.attributes.actiongroup_docker' in docs_fragments + should_be_in_action_group = module_name not in action_group_data['exclusions'] - if should_be_in_action_group != is_in_action_group: if should_be_in_action_group: - print('%s: module does not document itself as part of action group, but it should' % (path)) - else: - print('%s: module documents itself as part of action group, but it should not be' % (path)) + if module_name not in action_group_content: + print(f'{meta_runtime}: module {module_name!r} is not part of {action_group!r} action group') + else: + action_group_content.remove(module_name) + + documentation = [] + in_docs = False + with open(path, 'r', encoding='utf-8') as f: + for line in f: + if line.startswith('DOCUMENTATION ='): + in_docs = True + elif line.startswith(("'''", '"""')) and in_docs: + in_docs = False + elif in_docs: + documentation.append(line) + if in_docs: + print(f'{path}: cannot find DOCUMENTATION end') + if not documentation: + print(f'{path}: cannot find DOCUMENTATION') + continue + + try: + docs = yaml.safe_load('\n'.join(documentation)) + if not isinstance(docs, dict): + raise Exception('is not a top-level dictionary') + except Exception as exc: + print(f'{path}: cannot load DOCUMENTATION as YAML: {exc}') + continue + + docs_fragments = docs.get('extends_documentation_fragment') or [] + is_in_action_group = action_group_data['doc_fragment'] in docs_fragments + + if should_be_in_action_group != is_in_action_group: + if should_be_in_action_group: + print( + f'{path}: module does not document itself as part of action group {action_group!r}, but it should;' + f' you need to add {action_group_data["doc_fragment"]} to "extends_documentation_fragment" in DOCUMENTATION' + ) + else: + print(f'{path}: module documents itself as part of action group {action_group!r}, but it should not be') - for module_name in action_group: - print('%s: module %s mentioned in docker action group does not exist' % (meta_runtime, module_name)) + for action_group, action_group_data in ACTION_GROUPS.items(): + action_group_content = action_groups.get(action_group) or [] + for module_name in action_group_content: + print( + f'{meta_runtime}: module {module_name} mentioned in {action_group!r} action group' + f' does not exist or does not match pattern defined in {self_path}' + ) if __name__ == '__main__':