161 lines
9.2 KiB
Markdown
161 lines
9.2 KiB
Markdown
# Workspace Conventions
|
|
|
|
## Working Conventions
|
|
|
|
- Confirm the target repository before editing; never treat this directory as
|
|
a monorepo.
|
|
- Inspect the relevant upstream application before designing or changing its
|
|
Ansible role.
|
|
- Keep standalone application roles independently usable. Cross-application
|
|
policy belongs in `ansible-role-dnd-pipeline` and is exposed to consumers as
|
|
`rakestraw.dnd.pipeline`.
|
|
- Prefer codebase-memory graph tools for code discovery. Use text/file search
|
|
for documentation, configuration, literals, or when the graph is
|
|
insufficient.
|
|
- Preserve unrelated and pre-existing working-tree changes in every
|
|
repository.
|
|
- Never place credentials or secret values in plans, examples, inventory, or
|
|
tests.
|
|
- Validate changes in the repository that owns them and report the commands
|
|
run.
|
|
- Do not duplicate an application contract in workspace documentation when a
|
|
link and a concise deployment consequence are sufficient.
|
|
|
|
## Ansible House Style (Normative)
|
|
|
|
- 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.
|
|
|
|
## Testing House Style (Normative)
|
|
|
|
- Use a classical state-based approach, also commonly described as Chicago or
|
|
Detroit style. Exercise the real role through its public interface and
|
|
observe the resulting deployed system.
|
|
- Test behavior owned by the role, not the current arrangement of tasks that
|
|
implements it. A useful test should normally remain valid after an internal
|
|
refactor that preserves the same public behavior.
|
|
- Require an immediate second role application to report no changes. Clean
|
|
repeated convergence is a first-class role outcome, not a test of any one
|
|
module.
|
|
- Distinguish configurable behavior from current defaults. To prove version
|
|
selection, explicitly request a version and verify that the application
|
|
reports that version. Do not assert that the same version remains the role's
|
|
default. For example, a useful test sets `audita_version: v0.9.0` and verifies
|
|
that the installed Audita reports `v0.9.0`; a harmful test asserts that
|
|
`v0.9.0` is the role's current default. The former continues to prove version
|
|
selection after the default advances to `v0.10.0`, while the latter fails
|
|
merely because of the intended update.
|
|
- Use a default invocation as a smoke test when useful, but do not snapshot
|
|
exact default values, selected commits, complete default configuration
|
|
documents, or counts of default variables. Those assertions are change
|
|
detectors that turn intentional updates into unrelated test failures.
|
|
- Do not assert task names, task order, individual `changed` or `skipped`
|
|
results, or other implementation traces. Inspect the final system and the
|
|
overall clean-repeat result instead.
|
|
- Do not test documented behavior of `ansible.builtin`, widely used
|
|
`community.general`, or other trusted modules. In particular, do not
|
|
independently verify file existence, ownership, permissions, package state,
|
|
checkout state, or template deployment merely to repeat module arguments.
|
|
- Test role-authored decisions only when they express meaningful public
|
|
behavior. Examples include an explicitly selected application version, a
|
|
caller-supplied build executable, configuration rendering with representative
|
|
non-null and falsey values, and composition of caller-supplied mappings.
|
|
- Verify application outcomes with the application's native interface when
|
|
practical. Prefer executing the installed application or its configuration
|
|
validator over reconstructing its rules in Ansible assertions.
|
|
- Self-evident declarative guards, including straightforward platform
|
|
assertions, do not require a negative test matrix.
|
|
- Prefer one representative case for a behavior over exhaustive combinations.
|
|
Every additional test must protect a requirement or a plausible regression,
|
|
not merely increase coverage.
|
|
- Keep syntax checking and linting as static validation. They complement the
|
|
behavioral suite but do not justify tests that freeze implementation details.
|
|
|
|
## 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 above 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.
|