Skip to content

feat: support nested scenario directories in collection mode#4611

Merged
cidrblock merged 8 commits intomainfrom
nested_scenarios
Feb 23, 2026
Merged

feat: support nested scenario directories in collection mode#4611
cidrblock merged 8 commits intomainfrom
nested_scenarios

Conversation

@cidrblock
Copy link
Contributor

Summary

  • Adds nested scenario directory support for Ansible collections, allowing scenario names with / separators (e.g., molecule test -s appliance_vlans/merged) to target molecule.yml files in subdirectories under extensions/molecule/.
  • Introduces _resolve_scenario_glob() in command/base.py for path-based glob resolution and Config._derive_scenario_name() in config.py for hierarchical name derivation from file paths.
  • All changes are gated behind collection mode (extensions/molecule/) — role-mode testing is completely unaffected.
  • Adds 19 unit and integration tests covering targeting, naming, discovery, and round-trip consistency.
  • Documents the feature in docs/configuration.md, docs/getting-started-collections.md, and docs/usage.md.

Details

Large Ansible collections (e.g., 48 resource modules × 4 states = ~200 scenarios) benefit from grouping related scenarios under parent directories rather than using a flat layout. This PR enables:

extensions/molecule/
├── appliance_vlans/
│   ├── merged/
│   │   └── molecule.yml
│   └── replaced/
│       └── molecule.yml
molecule test -s appliance_vlans/merged
export MOLECULE_GLOB="extensions/molecule/**/molecule.yml"
molecule test --all

Test plan

  • All 19 new tests in tests/unit/test_nested_scenarios.py pass
  • Existing test suite shows no regressions (tox)
  • Manual verification with a real collection project using nested scenarios

Made with Cursor

cidrblock and others added 2 commits February 18, 2026 11:27
Allow scenario names to contain `/` path separators when running in
collection mode (extensions/molecule/), enabling hierarchical grouping
of scenarios (e.g. `appliance_vlans/merged`).

- Add `_resolve_scenario_glob()` helper in command/base.py for
  slash-aware scenario targeting
- Add `Config._derive_scenario_name()` to derive hierarchical names
  from molecule.yml paths relative to the collection root
- Gate all changes behind collection-mode detection so role-mode
  testing is unaffected
- Add comprehensive unit and integration tests
- Document the feature in configuration.md, getting-started-collections.md,
  and usage.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the slash guard from _resolve_scenario_glob so collection mode
always uses os.path.join instead of str.replace. This avoids corrupting
multi-star globs like '**' and simplifies the logic since both flat and
nested names produce identical results with the path-based approach.

Co-authored-by: Cursor <cursoragent@cursor.com>
Allow 'molecule test -s "appliance_vlans/*"' to expand and run all
scenarios under a nested group. Replaces scenario_names with the
actual discovered names after glob expansion so Scenarios._verify
and _filter_for_scenario work correctly.

Adds 3 new tests covering wildcard glob resolution, multi-scenario
discovery, and Scenarios verification with expanded names. Updates
docs to show wildcard usage.

Co-authored-by: Cursor <cursoragent@cursor.com>

This comment was marked as outdated.

- Replace os.path.join with Path / operator (PTH118)
- Use ternary for scenario_name assignment (SIM108)
- Move Path import into TYPE_CHECKING block (TC003)
- Add full Google-style docstrings with Args/Returns (pydoclint)
- Rename fixture to fixture_ prefix pattern (pylint W0621)
- Replace os.path.join in test expectations with string literals

Co-authored-by: Cursor <cursoragent@cursor.com>
- Fix ephemeral directory creation with nested names: replace '/' with
  '--' in scenario name when constructing filesystem paths to avoid
  creating unintended nested directories (scenario.py)
- Add path traversal validation: reject scenario names containing '..'
  or starting with '/' to prevent directory escape
- Add glob pattern assumptions comment in _resolve_scenario_glob
- Document _derive_scenario_name edge case for molecule.yml directly
  under collection root
- Add 7 new tests: path traversal rejection (4 cases), nonexistent
  scenario handling (2 cases), and ephemeral directory name safety

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

This comment was marked as outdated.

@github-actions github-actions bot added feat and removed feat labels Feb 18, 2026
- Add standard MIT license header to test_nested_scenarios.py
- Replace unit-level ephemeral name test with integration test that
  creates a real Scenario object and verifies the ephemeral_directory
  property produces a flat path (no nested dirs from '/' in name)

Co-authored-by: Cursor <cursoragent@cursor.com>

This comment was marked as outdated.

@cidrblock cidrblock merged commit e10ab99 into main Feb 23, 2026
29 checks passed
@cidrblock cidrblock deleted the nested_scenarios branch February 23, 2026 23:07
rija added a commit to rmenage/ansible-role-hardened-docker that referenced this pull request Mar 3, 2026
This MR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [molecule](https://github.com/ansible-community/molecule) ([changelog](https://github.com/ansible-community/molecule/releases)) | `>=25.0.0,<26.0.0` → `>=26.2.0,<26.3.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/molecule/26.2.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/molecule/25.12.0/26.2.0?slim=true) |

---

### Release Notes

<details>
<summary>ansible-community/molecule (molecule)</summary>

### [`v26.2.0`](https://github.com/ansible/molecule/releases/tag/v26.2.0)

[Compare Source](ansible/molecule@v25.12.0...v26.2.0)

#### Features

- feat: improve scenario selection and filtering for large collection trees ([#&#8203;4613](ansible/molecule#4613)) [@&#8203;cidrblock](https://github.com/cidrblock)
- feat: support nested scenario directories in collection mode ([#&#8203;4611](ansible/molecule#4611)) [@&#8203;cidrblock](https://github.com/cidrblock)

#### Fixes

- fix: remove use of click-help-colors ([#&#8203;4569](ansible/molecule#4569)) [@&#8203;ssbarnea](https://github.com/ssbarnea)
- fix: validate .git directories in find\_vcs\_root() ([#&#8203;4610](ansible/molecule#4610)) [@&#8203;skeetmtp](https://github.com/skeetmtp)
- fix: handle missing molecule\_yml\_date\_modified key in state ([#&#8203;4606](ansible/molecule#4606)) [@&#8203;dmzoneill](https://github.com/dmzoneill)
- Add execution flow documentation ([#&#8203;4589](ansible/molecule#4589)) [@&#8203;Qalthos](https://github.com/Qalthos)
- Modify contact links in issue template config ([#&#8203;4586](ansible/molecule#4586)) [@&#8203;alisonlhart](https://github.com/alisonlhart)

#### Maintenance

- chore(deps): update all dependencies ([#&#8203;4612](ansible/molecule#4612)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore: replace pre-commit with prek ([#&#8203;4614](ansible/molecule#4614)) [@&#8203;ssbarnea](https://github.com/ssbarnea)
- chore: use tox>=4.46.0 during testing ([#&#8203;4615](ansible/molecule#4615)) [@&#8203;ssbarnea](https://github.com/ssbarnea)
- chore(deps): update pep621 ([#&#8203;4609](ansible/molecule#4609)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- Bump cryptography from 46.0.4 to 46.0.5 ([#&#8203;4604](ansible/molecule#4604)) @&#8203;[dependabot\[bot\]](https://github.com/apps/dependabot)
- Bump pillow from 12.1.0 to 12.1.1 ([#&#8203;4605](ansible/molecule#4605)) @&#8203;[dependabot\[bot\]](https://github.com/apps/dependabot)
- chore(deps): update all dependencies ([#&#8203;4608](ansible/molecule#4608)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4603](ansible/molecule#4603)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4599](ansible/molecule#4599)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update dependencies \[security] ([#&#8203;4601](ansible/molecule#4601)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4598](ansible/molecule#4598)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4595](ansible/molecule#4595)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4594](ansible/molecule#4594)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4593](ansible/molecule#4593)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4592](ansible/molecule#4592)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4591](ansible/molecule#4591)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4590](ansible/molecule#4590)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore(deps): update all dependencies ([#&#8203;4587](ansible/molecule#4587)) @&#8203;[renovate\[bot\]](https://github.com/apps/renovate)
- chore: adopt pytest>=9.0.0 config ([#&#8203;4583](ansible/molecule#4583)) [@&#8203;Qalthos](https://github.com/Qalthos)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuOTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->


Refs:  rmenage/ansible-role-hardened-docker!23
See-also:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants