The lint accepted a link that resolved to a file or a directory. myst builds a document reference only when the target is_file(), so a link to a directory without a README is a bare file listing on GitHub and a dead cross-reference in the documentation build. It now reports which of the two reasons applies. The tightened rule found 41 links. Where a README backs the directory, bd15eae9fa's resolver carries the link and it stays. Where none exists, the link becomes a code span rather than an invented README: 15 of them point at a role's meta/addons/, whose addon table already sits below the sentence, and the rest at code directories such as utils/ or cli/administration/deploy/runner/. The changed sentences are msgids, so these entries return to the translator on the next extract. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4.1 KiB
users lookup 👥
Contributor-facing rules for the users.py lookup plugin. For general documentation rules such as links, writing style, RFC 2119 keywords, and Sphinx behavior, see documentation.md.
Access Pattern 🎯
The users lookup is the ONLY supported runtime entry point for merged user configuration.
- Runtime consumers (roles, playbooks, templates, filter plugins, lookup plugins, tests) MUST read user data via
lookup('users')or a wrapper that calls it. - Consumers MUST NOT materialize a top-level
usersfact viaansible.builtin.set_fact,combine, or any custom aggregation task. - Consumers MUST NOT reintroduce
group_vars/all/04_users.ymlor any equivalent generated dictionary file.
Lookup Forms 🧭
| Form | Returns |
|---|---|
lookup('users') |
Full merged mapping keyed by the canonical user key. |
lookup('users', '<canonical_user_key>') |
Merged entry for that user. Raises when missing. |
lookup('users', '<canonical_user_key>', default_value) |
Merged entry, or default_value when the entry is missing. |
Data Sources 📚
The lookup merges exactly two sources:
- Defaults discovered from every
roles/*/meta/users.ymlin the repository (file root IS the users map; nousers:wrapper). - Overrides supplied through the normal Ansible variable
usersin inventory, group vars, host vars, or role vars.
No intermediate merged users fact exists. No other source is consulted.
Canonical User Key 🔑
- The canonical key is the mapping key used at the file root of
roles/*/meta/users.yml. - The canonical key is NOT the rendered
usernamefield inside the value. - The canonical key is the same identifier that inventory, group vars, host vars, and role vars MUST use when overriding an entry.
- You MUST NOT introduce a new keying scheme.
Adding a User Entry ➕
You MUST add new user defaults in the owning role:
-
Create or edit
roles/<role_id>/meta/users.yml. -
Add the user at the file root, keyed by its canonical key (no
users:wrapper):# roles/<role_id>/meta/users.yml administrator: username: administrator roles: - admin -
Keep generated fields (UID, GID, email, description, password) unset when the shared aggregation logic SHOULD allocate or template them. Set them explicitly only when the role requires a specific value.
The lookup discovers the entry automatically on the next run. No generator step is required.
Overriding a User Entry 🛠️
You MUST override user values through the normal Ansible variable users:
# group_vars/<group>.yml or host_vars/<host>.yml
users:
administrator:
email: admin@example.org
- Overrides MUST use the canonical user key.
- Overrides merge recursively on top of role-local defaults.
- Overrides MUST NOT be written back into
roles/*/meta/users.yml; that file is for defaults only.
What Not To Do 🚫
- You MUST NOT create
group_vars/all/04_users.ymlor any similar generated users dictionary. - You MUST NOT add a make target, script, or role task whose purpose is to generate, render, merge, or rewrite such a file.
- You MUST NOT key user overrides by
username; use the canonical key. - You MUST NOT reintroduce
default_usersordefaults_usersreferences; they do not exist anymore.
Source Of Truth 📌
| File | Purpose |
|---|---|
| users.py | Runtime entry point for the users lookup. |
utils/cache/users/ |
Shared aggregation helper package that builds and caches defaults, handles reserved users, UID/GID allocation, and uniqueness validation. Placeholder substitution (DOMAIN_PRIMARY / ORGANIZATION / SOFTWARE_NAME) lives in users/placeholders.py. |
| test_users.py | Unit tests covering the full-dict, single-entry, override, strict missing, and non-strict missing cases. |
For the related applications pattern see applications.md.