mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
A whitelist-scoped update preloaded and fully executed every provider role the host is a member of, because the gate only asked whether the role was in group_names. On this inventory that meant a single-app update built compose contexts, rendered configs and vhosts for 17 unrelated applications. reachable_roles() is lifted out of applications_if_group_and_all_deps() so the same traversal can start from an arbitrary seed set instead of group_names. The old function now calls it and filters as before, so every existing caller keeps its result. A new application_closure lookup exposes it to Ansible, and sys-service-loader gates each preload on the whitelist's closure. An empty whitelist yields an empty closure, which the gate reads as the full inventory scope, so a no-apps deploy is unchanged. Measured on mode=update apps=web-app-snipe-it: 42:49 before, 17:18 after, with ok tasks dropping from 4903 to 2030. Nothing outside the closure ran, and snipe-it's real dependencies (mariadb, redis, openldap, postgres, keycloak, mailu, prometheus, seaweedfs, cdn, logout) all still did. The closure also gates the required branch, deliberately. `required` reads as a global property but plugins/lookup/service.py derives it from group_names, so it means "some provisioned app needs this" and admits nearly every service. Exempting it was measured at 42:39 against 17:18 for the same deploy, which is the entire saving. A service whose provider lies outside the closure is by definition required by an application outside the deploy scope. Out of scope: seeding applications_current_play from the closure. That shrinks CURRENT_PLAY_DOMAINS_ALL, which cert SANs, openresty vhosts, DNS records and the webserver health check all consume at full inventory scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>