--- # nocheck: run-once — main.yml is the orchestrator entry point and intentionally # re-runnable; per-service idempotency is enforced by each loaded role's own # run_once_ flag inside load_service.yml. # # SPOT: global loader for all shared services. # # Called from tasks/stages/01_constructor.yml before the normal server app pass. # # Two-phase contract: # 1. Preload pass — iterate `_ordered_services` in dependency order and # load each service body once (`service_loader_preloading: true`). # 2. Post-load pass — flush the `service_loader_post_run` queue that # roles registered during preload via this role's `list_or_shoot.yml`. # Tasks listed there run with `service_loader_preloading: false`, # so they see the fully preloaded service graph. - name: "🔍 Discover ordered shared services" ansible.builtin.set_fact: _ordered_services: >- {{ query( 'service_registry', 'ordered', roles_dir=(lookup('path_absolute', 'roles')) ) | first }} - name: "🔍 Precompute required shared services (single applications load)" ansible.builtin.set_fact: _required_service_ids: >- {{ query( 'service', (_ordered_services | map(attribute='id') | list), roles_dir=(lookup('path_absolute', 'roles')) ) | selectattr('required') | map(attribute='id') | list }} - name: "🔍 Scope the preload to the whitelist's dependency closure" ansible.builtin.set_fact: _preload_closure: "{{ lookup('application_closure', lookup('deployment').whitelist) }}" - name: "🔌 Load shared service" include_tasks: load_service.yml vars: service_key: "{{ service_item.id }}" service_role: "{{ service_item.role }}" service_run_once_var: "{{ 'run_once_' ~ (service_item.role | replace('-', '_')) }}" loop: "{{ _ordered_services }}" loop_control: loop_var: service_item label: "{{ service_item.id }}" - name: "▶️ Run service-loader post-load roles" ansible.builtin.include_role: name: "{{ post_run_role }}" loop: "{{ service_loader_post_run | unique }}" loop_control: loop_var: post_run_role