Files
devx/docs/specs/DEVX-177.md
T
kireto ca55cfd29a
Post-merge / detect-and-configure (push) Successful in 9s
Post-merge / release-and-maintain (push) Successful in 1m3s
DEVX-177: fix: fail-open molecule selection and honest fast-path contract
2026-09-23 08:10:19 +00:00

77 lines
3.6 KiB
Markdown

# DEVX-177: Honest molecule selection — fail-open coverage and real command contract
## Problem
`devx.molecule.molecule_changed` and `devx.ci.fast_molecule` violate the
S09/REQ-9 honesty contract in three ways:
1. **Silent skips.** `detect_changed_roles` only knows four playbooks and
`ansible/roles/`. Changes to `restore.yml`, `deploy-sso-bridge.yml`,
`upgrade-postgres.yml`, `rolling-update-gitea.yml`,
`update-alertmanager.yml`, `build-image.yml`, `playbooks/_tasks/`,
`playbooks/tasks/`, or `ansible/group_vars/` trigger zero molecule
coverage — the fast path passes by skipping work.
2. **Phantom targets.** `ROLE_TARGET_MAP` includes `sso_config` (role
moved to the sso-bridge repo) and emits `molecule-crowdsec` /
`molecule-disk-cleanup` targets that do not exist in infra's Makefile.
Playbook mappings also inject absent roles, so `make molecule-changed`
can select nonexistent targets.
3. **Dishonest contract.** `fast_molecule` documents "converge + verify
only, no idempotence" and builds `molecule test -s X --destroy=never
--platform-name=...` commands that nothing executes — CI actually runs
full `molecule test -s X` via `run_molecule_scenario.py` (which does
include idempotence where the scenario defines it).
## Approach
REQ-1: Fail-open coverage in `detect_changed_roles` — add the missing
playbook→role mappings; `ansible/group_vars/**` and any other
`ansible/playbooks/**` file (including `_tasks/`/`tasks/`) not explicitly
mapped select all molecule-covered roles. `ansible/environments/` stays
unmapped (env data is covered by unit/deploy tests, not molecule) and is
documented as such.
REQ-2: Selection only emits roles that exist — `detect_changed_roles`
gains a `roles_dir` parameter; role names from file paths and playbook
maps are kept only when `<roles_dir>/<role>` exists. Shared-path and
fail-open "all roles" resolution returns only directories under
`roles_dir` containing a `molecule/` dir (untestable roles select
nothing rather than phantom targets).
REQ-3: Honest fast-molecule contract — `build_molecule_commands` emits
exactly what the CI runner executes (`molecule test -s <scenario>`);
docstrings state the real sequence (full `molecule test` per scenario on
changed roles, single platform as configured by the scenario) instead of
the old "converge + verify only" claim.
## Test Plan
- Update `test_molecule_changed.py`: unmapped playbook → all present
roles; `group_vars` change → all; mapped playbook → mapped roles only;
role absent from `roles_dir` → filtered out; shared path → only roles
with `molecule/` dirs.
- Update `test_fast_molecule.py`: emitted commands are `molecule test -s
<scenario>` with no `--destroy`/`--platform-name` flags.
- `make pytest-cov` (100% gate), `make lint-all`.
## Deploy Plan
Merge → devx release publishes automatically → infra dep-PR bumps the
pin; the honest selection takes effect on the next infra CI run.
## Rollback Plan
Revert the squash commit; previous (under-covering) selection returns —
acceptable short-term because coverage only widens with this change.
## Acceptance Criteria
- [x] REQ-1: All `ansible/playbooks/**` and `ansible/group_vars/**`
changes select molecule coverage; unmapped files fail open to all
testable roles.
- [x] REQ-2: No nonexistent roles or make targets are emitted; `sso_config`
no longer appears when absent from `roles_dir`.
- [x] REQ-3: `build_molecule_commands` output matches the executed CI
command shape; module docstrings describe the real sequence.
- [x] Unit tests cover every new behavior; 100% coverage maintained.