mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-26 21:45:20 +02:00
- Introduced module_utils/role_dependency_resolver.py with full support for include_role, import_role, meta dependencies, and run_after. - Refactored cli/build/tree.py to use RoleDependencyResolver (added toggles for include/import/dependencies/run_after). - Extended filter_plugins/canonical_domains_map.py with optional 'recursive' mode (ignores run_after by design). - Updated roles/web-app-nextcloud to properly include Collabora dependency. - Added comprehensive unittests under tests/unit/module_utils for RoleDependencyResolver. Ref: https://chatgpt.com/share/68a519c8-8e54-800f-83c0-be38546620d9
105 lines
4.4 KiB
Python
105 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
import os
|
||
import argparse
|
||
import json
|
||
from typing import Dict, Any
|
||
|
||
from cli.build.graph import build_mappings, output_graph
|
||
from module_utils.role_dependency_resolver import RoleDependencyResolver
|
||
|
||
|
||
def find_roles(roles_dir: str):
|
||
for entry in os.listdir(roles_dir):
|
||
path = os.path.join(roles_dir, entry)
|
||
if os.path.isdir(path):
|
||
yield entry, path
|
||
|
||
|
||
def main():
|
||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||
default_roles_dir = os.path.abspath(os.path.join(script_dir, "..", "..", "roles"))
|
||
|
||
parser = argparse.ArgumentParser(
|
||
description="Generate all graphs for each role and write meta/tree.json"
|
||
)
|
||
parser.add_argument("-d", "--role_dir", default=default_roles_dir,
|
||
help=f"Path to roles directory (default: {default_roles_dir})")
|
||
parser.add_argument("-D", "--depth", type=int, default=0,
|
||
help="Max recursion depth (>0) or <=0 to stop on cycle")
|
||
parser.add_argument("-o", "--output", choices=["yaml", "json", "console"],
|
||
default="json", help="Output format")
|
||
parser.add_argument("-p", "--preview", action="store_true",
|
||
help="Preview graphs to console instead of writing files")
|
||
parser.add_argument("-s", "--shadow-folder", type=str, default=None,
|
||
help="If set, writes tree.json to this shadow folder instead of the role's actual meta/ folder")
|
||
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose logging")
|
||
|
||
# Toggles
|
||
parser.add_argument("--no-include-role", action="store_true", help="Do not scan include_role")
|
||
parser.add_argument("--no-import-role", action="store_true", help="Do not scan import_role")
|
||
parser.add_argument("--no-dependencies", action="store_true", help="Do not read meta/main.yml dependencies")
|
||
parser.add_argument("--no-run-after", action="store_true",
|
||
help="Do not read galaxy_info.run_after from meta/main.yml")
|
||
|
||
args = parser.parse_args()
|
||
|
||
if args.verbose:
|
||
print(f"Roles directory: {args.role_dir}")
|
||
print(f"Max depth: {args.depth}")
|
||
print(f"Output format: {args.output}")
|
||
print(f"Preview mode: {args.preview}")
|
||
print(f"Shadow folder: {args.shadow_folder}")
|
||
|
||
resolver = RoleDependencyResolver(args.role_dir)
|
||
|
||
for role_name, role_path in find_roles(args.role_dir):
|
||
if args.verbose:
|
||
print(f"Processing role: {role_name}")
|
||
|
||
graphs: Dict[str, Any] = build_mappings(
|
||
start_role=role_name,
|
||
roles_dir=args.role_dir,
|
||
max_depth=args.depth
|
||
)
|
||
|
||
# Direct deps (depth=1) – getrennt erfasst für buckets
|
||
inc_roles, imp_roles = resolver._scan_tasks(role_path)
|
||
meta_deps = resolver._extract_meta_dependencies(role_path)
|
||
run_after = set()
|
||
if not args.no_run_after:
|
||
run_after = resolver._extract_meta_run_after(role_path)
|
||
|
||
if any([not args.no_include_role and inc_roles,
|
||
not args.no_import_role and imp_roles,
|
||
not args.no_dependencies and meta_deps,
|
||
not args.no_run_after and run_after]):
|
||
deps_root = graphs.setdefault("dependencies", {})
|
||
if not args.no_include_role and inc_roles:
|
||
deps_root["include_role"] = sorted(inc_roles)
|
||
if not args.no_import_role and imp_roles:
|
||
deps_root["import_role"] = sorted(imp_roles)
|
||
if not args.no_dependencies and meta_deps:
|
||
deps_root["dependencies"] = sorted(meta_deps)
|
||
if not args.no_run_after and run_after:
|
||
deps_root["run_after"] = sorted(run_after)
|
||
graphs["dependencies"] = deps_root
|
||
|
||
if args.preview:
|
||
for key, data in graphs.items():
|
||
if args.verbose:
|
||
print(f"Previewing graph '{key}' for role '{role_name}'")
|
||
output_graph(data, "console", role_name, key)
|
||
else:
|
||
if args.shadow_folder:
|
||
tree_file = os.path.join(args.shadow_folder, role_name, "meta", "tree.json")
|
||
else:
|
||
tree_file = os.path.join(role_path, "meta", "tree.json")
|
||
os.makedirs(os.path.dirname(tree_file), exist_ok=True)
|
||
with open(tree_file, "w", encoding="utf-8") as f:
|
||
json.dump(graphs, f, indent=2)
|
||
print(f"Wrote {tree_file}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|