mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-06-25 03:38:59 +02:00
79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
import unittest
|
|
import os
|
|
import yaml
|
|
|
|
def load_yaml_file(file_path):
|
|
"""Load a YAML file and return its content."""
|
|
with open(file_path, 'r') as file:
|
|
return yaml.safe_load(file) or {}
|
|
|
|
def get_meta_info(role_path):
|
|
"""Extract before and after dependencies from the meta/main.yml of a role."""
|
|
meta_file = os.path.join(role_path, 'meta', 'main.yml')
|
|
if not os.path.isfile(meta_file):
|
|
return [], []
|
|
meta_data = load_yaml_file(meta_file)
|
|
run_order = meta_data.get('applications_run_order', {})
|
|
before = run_order.get('before', [])
|
|
after = run_order.get('after', [])
|
|
return before, after
|
|
|
|
def resolve_dependencies(roles_dir):
|
|
"""Resolve all role dependencies and detect circular dependencies in before/after."""
|
|
visited = set() # Tracks roles that have been processed
|
|
stack = set() # Tracks roles being processed in the current recursion path
|
|
|
|
def visit(role_path, stack):
|
|
role_name = os.path.basename(role_path)
|
|
|
|
# Check for circular dependencies in the recursion path (for before/after)
|
|
if role_name in stack:
|
|
raise ValueError(f"Circular dependency detected in 'before'/'after' between roles: {' -> '.join(stack)} -> {role_name}")
|
|
|
|
# Check if role is already processed
|
|
if role_name in visited:
|
|
return []
|
|
|
|
# Mark role as visited and add to stack
|
|
visited.add(role_name)
|
|
stack.append(role_name)
|
|
|
|
# Get before and after dependencies
|
|
before, after = get_meta_info(role_path)
|
|
|
|
# Resolve before and after roles
|
|
for before_role in before:
|
|
before_role_path = os.path.join(roles_dir, before_role)
|
|
visit(before_role_path, stack) # Recurse into before dependencies
|
|
|
|
for after_role in after:
|
|
after_role_path = os.path.join(roles_dir, after_role)
|
|
visit(after_role_path, stack) # Recurse into after dependencies
|
|
|
|
stack.pop() # Remove the current role from the stack
|
|
|
|
for role_name in os.listdir(roles_dir):
|
|
role_path = os.path.join(roles_dir, role_name)
|
|
if os.path.isdir(role_path):
|
|
try:
|
|
visit(role_path, []) # Start recursion from this role
|
|
except ValueError as e:
|
|
raise ValueError(f"Error processing role '{role_name}' at path '{role_path}': {str(e)}")
|
|
|
|
return "No Circular Dependency Detected"
|
|
|
|
class TestRoleBeforeAfterDependencies(unittest.TestCase):
|
|
def test_no_circular_before_after_dependencies(self):
|
|
roles_dir = "roles" # Path to the roles directory
|
|
|
|
try:
|
|
result = resolve_dependencies(roles_dir)
|
|
except ValueError as e:
|
|
self.fail(f"Circular dependency detected: {e}")
|
|
|
|
# If no exception, the test passed
|
|
self.assertEqual(result, "No Circular Dependency Detected")
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|