Organize workspace policies by task
This commit is contained in:
97
docs/ansible.md
Normal file
97
docs/ansible.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Ansible House Style
|
||||
|
||||
This policy is normative for designing, implementing, and reviewing Ansible
|
||||
roles and collections in this workspace.
|
||||
|
||||
## Role Implementation
|
||||
|
||||
- Always use the smallest clear sequence of declarative tasks that reaches the
|
||||
required end state.
|
||||
- Prefer `ansible.builtin` modules where they directly fit the task.
|
||||
`community.general` is an approved, stable dependency and its modules may be
|
||||
selected freely when appropriate; declare and pin the collection wherever a
|
||||
role or collection depends on it.
|
||||
- Do not strengthen the specification. Implement the requested invariants
|
||||
without inventing additional guarantees for integrity, provenance, drift
|
||||
recovery, corruption detection, rollback, or similar behavior.
|
||||
- Prefer convergence over state inspection. Declare what should be true and
|
||||
let the relevant module reconcile it.
|
||||
- Trust Ansible's abstraction boundary. A module that manages state owns the
|
||||
checks necessary to manage that state. Inspect it beforehand only when the
|
||||
observed state is independently required.
|
||||
- State inspection is justified when its result is necessary to decide whether
|
||||
an imperative operation must run, such as checking whether an arbitrary
|
||||
build command's output exists.
|
||||
- Production role tasks establish required application state; they do not test
|
||||
the role's own implementation. Put implementation verification in
|
||||
repository-local tests unless the specification explicitly requires runtime
|
||||
validation during convergence.
|
||||
- For an unavoidable `command` or `shell` task, declare the operation's change
|
||||
semantics as required by ansible-lint. A conditionally executed mutation may
|
||||
use `changed_when: true`; a read-only command should use
|
||||
`changed_when: false`. This describes an imperative operation and does not
|
||||
replace the idempotence of a state-management module.
|
||||
- Apply `become: true` to individual tasks that require elevated privileges.
|
||||
Do not require callers to escalate an entire play merely to use a role.
|
||||
- Complexity requires a requirement. Every additional task, conditional,
|
||||
registered variable, or state probe must correspond to behavior the role is
|
||||
required to provide.
|
||||
- Persisted role-private state is a design smell. Fingerprints, checksum
|
||||
databases, stamps, manifests, and similar bookkeeping require specific
|
||||
justification.
|
||||
- **Assume convergence first.** Before adding machinery for an edge case,
|
||||
determine whether rerunning the ordinary desired-state operations already
|
||||
repairs it.
|
||||
- Consider whether a conditional task or a notified handler best expresses the
|
||||
dependency. A `when` condition keeps triggers visible at the action site but
|
||||
grows with their number. Notifications keep the action independent of the
|
||||
number of notifiers, but distribute the control flow and defer execution
|
||||
until the next handler flush. An explicit
|
||||
`ansible.builtin.meta: flush_handlers` affects all pending handlers in the
|
||||
play.
|
||||
- Treat `rakestraw-ansible` as a style reference for direct tasks, shallow
|
||||
control flow, and concise variable use, while retaining current practices
|
||||
such as fully qualified collection names.
|
||||
|
||||
## Application Configuration Interfaces
|
||||
|
||||
- Expose application configuration through documented, field-specific role
|
||||
variables suitable for `host_vars`, `group_vars`, play variables, and
|
||||
composing roles.
|
||||
- Give structurally required fields concrete defaults. Define optional
|
||||
application settings as `null` unless the role intentionally owns the
|
||||
corresponding policy.
|
||||
- Treat a null optional variable as "omit this field from the rendered
|
||||
configuration." Do not copy documented upstream defaults into role defaults
|
||||
merely to write them back to the application.
|
||||
- Let the application supply its built-in defaults for omitted fields. This
|
||||
avoids freezing a duplicate defaults database in Ansible and permits upstream
|
||||
default changes to take effect naturally.
|
||||
- Use `is not none` when conditionally rendering optional values. Do not use
|
||||
truthiness, because false, zero, an empty string, or an empty collection may
|
||||
be an intentional value.
|
||||
- Emit a nested configuration section only when at least one of its descendant
|
||||
variables is non-null.
|
||||
- Serialize strings and collections with appropriate Ansible or Jinja filters.
|
||||
Do not construct YAML quoting manually.
|
||||
- Keep argument specifications aligned with the public variables. Optional
|
||||
nullable variables are not required; use a permissive type only when the
|
||||
native application accepts more than one scalar type.
|
||||
- Prefer one field-specific interface. Do not provide a parallel raw mapping,
|
||||
template override, or merge mechanism unless a concrete requirement defines
|
||||
its precedence and behavior.
|
||||
- Application roles expose native configuration choices. The suite-level role
|
||||
supplies cross-application and deployment-specific policy through those
|
||||
variables.
|
||||
- Manage the resulting file with one ordinary `ansible.builtin.template` task.
|
||||
Do not add configuration assembly facts, preliminary file inspection, or
|
||||
production validation unless independently required.
|
||||
- Configuration tests should use explicitly supplied representative values to
|
||||
exercise conditional rendering, null omission, intentional falsey values,
|
||||
native configuration validity, and caller-facing composition boundaries.
|
||||
They should follow [the testing house style](testing.md) rather than
|
||||
snapshotting the current default document.
|
||||
|
||||
Role defaults define what Ansible intends to manage, not a mirror of every
|
||||
application default. A concrete default means the role deliberately owns that
|
||||
policy; a null default leaves the policy with the application.
|
||||
Reference in New Issue
Block a user