mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-11-04 12:18:17 +00:00 
			
		
		
		
	Implemented new run_after logic
This commit is contained in:
		@@ -105,32 +105,13 @@ def print_dependency_tree(graph):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def generate_playbook_entries(roles_dir, prefix=None):
 | 
					def generate_playbook_entries(roles_dir, prefix=None):
 | 
				
			||||||
    """Generate playbook entries based on the sorted order."""
 | 
					    """Generate playbook entries based on the sorted order."""
 | 
				
			||||||
    # Build dependency graph
 | 
					 | 
				
			||||||
    graph, in_degree, roles = build_dependency_graph(roles_dir, prefix)
 | 
					    graph, in_degree, roles = build_dependency_graph(roles_dir, prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Print and collect roles in tree order
 | 
					    # Detect cycles and get correct topological order
 | 
				
			||||||
    tree_sorted_roles = print_dependency_tree(graph)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Topologically sort the roles
 | 
					 | 
				
			||||||
    sorted_role_names = topological_sort(graph, in_degree)
 | 
					    sorted_role_names = topological_sort(graph, in_degree)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Ensure that roles that appear in the tree come first
 | 
					 | 
				
			||||||
    final_sorted_roles = [role for role in tree_sorted_roles if role in sorted_role_names]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Include the remaining unsorted roles
 | 
					 | 
				
			||||||
    final_sorted_roles += [role for role in sorted_role_names if role not in final_sorted_roles]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Remove duplicates, keeping only the first occurrence to preserve dependency order
 | 
					 | 
				
			||||||
    seen = set()
 | 
					 | 
				
			||||||
    deduplicated_roles = []
 | 
					 | 
				
			||||||
    for role in final_sorted_roles:
 | 
					 | 
				
			||||||
        if role not in seen:
 | 
					 | 
				
			||||||
            deduplicated_roles.append(role)
 | 
					 | 
				
			||||||
            seen.add(role)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Generate the playbook entries
 | 
					 | 
				
			||||||
    entries = []
 | 
					    entries = []
 | 
				
			||||||
    for role_name in deduplicated_roles:
 | 
					    for role_name in sorted_role_names:
 | 
				
			||||||
        role = roles[role_name]
 | 
					        role = roles[role_name]
 | 
				
			||||||
        entries.append(
 | 
					        entries.append(
 | 
				
			||||||
            f"- name: setup {role['application_id']}\n"
 | 
					            f"- name: setup {role['application_id']}\n"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,3 +19,7 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-file-invoice-dollar"
 | 
					    class: "fa-solid fa-file-invoice-dollar"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
@@ -19,4 +19,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-calendar-check"
 | 
					    class: "fa-solid fa-calendar-check"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,4 +18,7 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-table"
 | 
					    class: "fa-solid fa-table"
 | 
				
			||||||
dependencies: []
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,4 +18,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-sun"
 | 
					    class: "fa-solid fa-sun"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,4 +18,6 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-users"
 | 
					    class: "fa-solid fa-users"
 | 
				
			||||||
dependencies: []
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,4 +19,7 @@ galaxy_info:
 | 
				
			|||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-music"
 | 
					    class: "fa-solid fa-music"
 | 
				
			||||||
  run_after:
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
    - docker-ldap
 | 
					    - docker-ldap
 | 
				
			||||||
@@ -19,3 +19,7 @@ galaxy_info:
 | 
				
			|||||||
  repository: "https://s.veen.world/cymais"
 | 
					  repository: "https://s.veen.world/cymais"
 | 
				
			||||||
  issue_tracker_url: "https://s.veen.world/cymaisissues"
 | 
					  issue_tracker_url: "https://s.veen.world/cymaisissues"
 | 
				
			||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,4 +19,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-code"
 | 
					    class: "fa-solid fa-code"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,4 +19,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-code-branch"
 | 
					    class: "fa-solid fa-code-branch"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,4 +19,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: https://s.veen.world/cymais
 | 
					  documentation: https://s.veen.world/cymais
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-sitemap"
 | 
					    class: "fa-solid fa-sitemap"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
@@ -19,5 +19,7 @@ galaxy_info:
 | 
				
			|||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-list"
 | 
					    class: "fa-solid fa-list"
 | 
				
			||||||
dependencies:
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
    - docker-mailu
 | 
					    - docker-mailu
 | 
				
			||||||
@@ -20,4 +20,8 @@ galaxy_info:
 | 
				
			|||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-satellite-dish"
 | 
					    class: "fa-solid fa-satellite-dish"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,3 +19,7 @@ galaxy_info:
 | 
				
			|||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-book"
 | 
					    class: "fa-solid fa-book"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
@@ -19,4 +19,8 @@ galaxy_info:
 | 
				
			|||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-calendar-days"
 | 
					    class: "fa-solid fa-calendar-days"
 | 
				
			||||||
  run_after:
 | 
					  run_after:
 | 
				
			||||||
    - "docker-postgres"
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
 | 
					    - docker-ldap
 | 
				
			||||||
 | 
					    - docker-postgres
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,5 +19,9 @@ galaxy_info:
 | 
				
			|||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-comments"
 | 
					    class: "fa-solid fa-comments"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies:
 | 
					dependencies:
 | 
				
			||||||
  - nginx-docker-reverse-proxy
 | 
					  - nginx-docker-reverse-proxy
 | 
				
			||||||
@@ -26,5 +26,9 @@ galaxy_info:
 | 
				
			|||||||
  documentation: "https://s.veen.world/cymais"
 | 
					  documentation: "https://s.veen.world/cymais"
 | 
				
			||||||
  logo:
 | 
					  logo:
 | 
				
			||||||
    class: "fa-solid fa-box"
 | 
					    class: "fa-solid fa-box"
 | 
				
			||||||
 | 
					  run_after:
 | 
				
			||||||
 | 
					    - docker-matomo
 | 
				
			||||||
 | 
					    - docker-keycloak
 | 
				
			||||||
 | 
					    - docker-mailu
 | 
				
			||||||
dependencies: []
 | 
					dependencies: []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										85
									
								
								tests/unit/test_generate_playbook.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								tests/unit/test_generate_playbook.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					import tempfile
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					import yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Adjust path to include cli/ folder
 | 
				
			||||||
 | 
					sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..", "cli")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from generate_playbook import build_dependency_graph, topological_sort, generate_playbook_entries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestGeneratePlaybook(unittest.TestCase):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        # Create a temporary directory to simulate roles
 | 
				
			||||||
 | 
					        self.temp_dir = tempfile.mkdtemp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Define mock roles and dependencies
 | 
				
			||||||
 | 
					        self.roles = {
 | 
				
			||||||
 | 
					            'role-a': {'run_after': [], 'application_id': 'a'},
 | 
				
			||||||
 | 
					            'role-b': {'run_after': ['role-a'], 'application_id': 'b'},
 | 
				
			||||||
 | 
					            'role-c': {'run_after': ['role-b'], 'application_id': 'c'},
 | 
				
			||||||
 | 
					            'role-d': {'run_after': [], 'application_id': 'd'},
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for role_name, meta in self.roles.items():
 | 
				
			||||||
 | 
					            role_path = os.path.join(self.temp_dir, role_name)
 | 
				
			||||||
 | 
					            os.makedirs(os.path.join(role_path, 'meta'), exist_ok=True)
 | 
				
			||||||
 | 
					            os.makedirs(os.path.join(role_path, 'vars'), exist_ok=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            meta_file = {
 | 
				
			||||||
 | 
					                'galaxy_info': {
 | 
				
			||||||
 | 
					                    'run_after': meta['run_after']
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            vars_file = {
 | 
				
			||||||
 | 
					                'application_id': meta['application_id']
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            with open(os.path.join(role_path, 'meta', 'main.yml'), 'w') as f:
 | 
				
			||||||
 | 
					                yaml.dump(meta_file, f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            with open(os.path.join(role_path, 'vars', 'main.yml'), 'w') as f:
 | 
				
			||||||
 | 
					                yaml.dump(vars_file, f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        # Clean up the temporary directory
 | 
				
			||||||
 | 
					        shutil.rmtree(self.temp_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dependency_graph_and_sort(self):
 | 
				
			||||||
 | 
					        graph, in_degree, roles = build_dependency_graph(self.temp_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn('role-a', graph)
 | 
				
			||||||
 | 
					        self.assertIn('role-b', graph)
 | 
				
			||||||
 | 
					        self.assertEqual(graph['role-a'], ['role-b'])
 | 
				
			||||||
 | 
					        self.assertEqual(graph['role-b'], ['role-c'])
 | 
				
			||||||
 | 
					        self.assertEqual(graph['role-c'], [])
 | 
				
			||||||
 | 
					        self.assertEqual(in_degree['role-c'], 1)
 | 
				
			||||||
 | 
					        self.assertEqual(in_degree['role-b'], 1)
 | 
				
			||||||
 | 
					        self.assertEqual(in_degree['role-a'], 0)
 | 
				
			||||||
 | 
					        self.assertEqual(in_degree['role-d'], 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sorted_roles = topological_sort(graph, in_degree)
 | 
				
			||||||
 | 
					        # The expected order must be a → b → c, d can be anywhere before or after
 | 
				
			||||||
 | 
					        self.assertTrue(sorted_roles.index('role-a') < sorted_roles.index('role-b') < sorted_roles.index('role-c'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_generate_playbook_entries(self):
 | 
				
			||||||
 | 
					        entries = generate_playbook_entries(self.temp_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        text = ''.join(entries)
 | 
				
			||||||
 | 
					        self.assertIn("setup a", text)
 | 
				
			||||||
 | 
					        self.assertIn("setup b", text)
 | 
				
			||||||
 | 
					        self.assertIn("setup c", text)
 | 
				
			||||||
 | 
					        self.assertIn("setup d", text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Order must preserve run_after
 | 
				
			||||||
 | 
					        a_index = text.index("setup a")
 | 
				
			||||||
 | 
					        b_index = text.index("setup b")
 | 
				
			||||||
 | 
					        c_index = text.index("setup c")
 | 
				
			||||||
 | 
					        self.assertTrue(a_index < b_index < c_index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
		Reference in New Issue
	
	Block a user