Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations.
Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consumer lookup plugins now reach the merged config through the Ansible
lookup loader instead of importing get_merged_applications /
get_merged_domains from utils.cache. The merge stays one cached
implementation surfaced via the applications/config/domains/users lookups;
consumers call lookup_loader.get(...).run() (the compose_networks idiom).
- get_merged_applications walks the whole meta/ tree recursively
(meta/**/*.yml to applications.<app>.<path>, addons and any depth) and
folds the raw meta/volumes.yml subtree onto applications.<app>.volumes
after the templar render so the Jinja source: strings stay unrendered.
- 31 consumer lookups rerouted; providers (applications/config/
applications_current_play/users/domains) and the raw-volume accessors
(volume/nginx) keep the cache import behind # nocheck: lookup-cache-import.
- New lint test_no_cache_imports_in_lookups enforces the contract.
- nfs_flavor is now the flavor SPOT (svc-storage-nfs-server + nfs_client_src
resolve via lookup('nfs_flavor')); removed the unused STORAGE_NFS_FLAVOR
group_var. Extracted extract_default_credentials to
utils/cache/schema_credentials.py to keep applications.py under the cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Take the host IP as an optional third element of each term
([service, protocol, host_ip]) instead of an ip= kwarg, and build the whole
ports: block from declared ports.local/public (host) + ports.internal
(container). Convert every compose template across 84 roles, folding the
remaining raw var/numeric/specific-ip mappings (including mailu's per-interface
mail ports bound to MAILU_IP4_PUBLIC). Multi-term calls render one term per line.
Add the compose-ports-must-use-container-ports lint: it forbids any raw
"<host>:<published>:<container>" publish item under a ports: block and requires
every term to declare ports.internal. Remove
tests/lint/roles/test_swarm_internal_ports.py, now subsumed.
Fix a container_depends_on rendering regression. The lookup self-indented
(default indent=4), so the added 4-space placement + | indent(4) triple-indented
the block. Flip the lookup default to column 0 (consistent with
container_networks), make every call use the 4-space + | indent(4) form, and add
a lint enforcing it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Foundation for the central svc-db-*/svc-dns-* engine roles: the engine lookup and engines descriptor, per-consumer engine credentials in the inventory manager, shared-net overlay rendering, container_depends_on, the sys-svc-engine provisioning dispatcher, and the compose_volumes NFS-placement split. base.yml.j2 gates the embedded redis sidecar on lookup('engine','redis',application_id,'local').
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lookup read DEPLOYMENT_MODE via str(vars_.get(...)).strip() without
running it through templar.template(). DEPLOYMENT_MODE in
group_vars/all/18_swarm.yml is a Jinja expression
({{ 'swarm' if (groups['svc-swarm']|default([])|length) > 1 else 'compose' }}),
so the untemplated literal never equals 'swarm' and the lookup always
fell through to the compose branch — emitting depends_on as a mapping
(e.g. `redis: {condition: service_healthy}`). docker stack deploy
rejects that with `services.X.depends_on must be a list`, breaking
mastodon, friendica, gitea, onlyoffice and matomo on swarm.
Mirror the templating pattern already used by every sibling lookup
(compose_replicas.py, compose_volumes.py, compose_only.py,
compose_restart.py): capture self._templar, fetch DEPLOYMENT_MODE, and
pass it through templar.template() inside contextlib.suppress(Exception)
before comparing against 'swarm'. Behaviour for already-resolved values
('swarm', 'compose', missing default) is unchanged; only the unresolved
Jinja case is fixed.
Add a regression test (test_unresolved_jinja_deployment_mode_is_templated)
that drives the lookup with the exact group_vars expression plus a stub
templar and asserts the list form is emitted.
`docker stack deploy` rejects compose map-form `depends_on` with
`services.<svc>.depends_on must be a list`; the syntax is only valid
for `docker compose v2`. Until now the lookup always emitted the
map form, so every swarm-deployed app with a local DB or Redis
aborted the stack deploy handler in sys-svc-compose with rc=1
(CI job 79876947270 on friendica).
Branch on `DEPLOYMENT_MODE`:
* compose: keep the map form so health-check ordering still applies.
* swarm: emit the bare service-name list; swarm has no health-check
gated ordering anyway.
Adds two regression tests pinning the emitted shape per mode.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eliminate include-style depends_on assembly. The lookup resolves DB/redis
inductively from applications config and returns indented YAML (or "" when
no entries), removing the empty `depends_on:` block bug in libretranslate.
Migrates 43 compose templates from `{% include … dmbs_*.yml.j2 %}` to
`{{ lookup('container_depends_on', application_id) }}`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>