---
# 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_<role> 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