fix(resources): support multiple named devcontainer configurations in auto-discovery
CI / lint (pull_request) Failing after 19s
CI / typecheck (pull_request) Successful in 46s
CI / quality (pull_request) Successful in 34s
CI / security (pull_request) Successful in 1m0s
CI / coverage (pull_request) Has been skipped
CI / build (pull_request) Successful in 17s
CI / helm (pull_request) Successful in 22s
CI / unit_tests (pull_request) Successful in 7m9s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 17m9s
CI / integration_tests (pull_request) Failing after 23m2s
CI / status-check (pull_request) Failing after 2s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped

Extend DevcontainerDiscoveryResult and discover_devcontainers() to scan
.devcontainer/<name>/devcontainer.json patterns (one subdirectory level)
in addition to the existing fixed paths. Each named configuration produces
a distinct result with config_name set to the subdirectory name (e.g.
'api', 'frontend'). Root-level configs retain config_name=None.

- Replace _SCAN_PATHS with _FIXED_SCAN_PATHS for root-level configs
- Add glob-based scan of .devcontainer/<name>/devcontainer.json
- Add config_name: str | None attribute to DevcontainerDiscoveryResult
- Validate config_name (must be non-empty str or None)
- Add 8 new Behave scenarios covering named, multiple, mixed, empty cases
- Add 4 new Robot Framework integration tests for named config discovery
- All existing tests continue to pass (no regression)

ISSUES CLOSED: #2615
This commit is contained in:
2026-04-05 09:02:05 +00:00
parent b82a9b6962
commit a05f4c69a7
6 changed files with 281 additions and 7 deletions
@@ -256,3 +256,58 @@ def step_check_is_trigger(context: Context, rtype: str) -> None:
@then('"{rtype}" should not be a trigger type')
def step_check_not_trigger(context: Context, rtype: str) -> None:
assert is_trigger_type(rtype) is False
# ── Given steps for named configurations ─────────────────────
@given('the directory has a named devcontainer configuration "{name}"')
def step_add_named_dc(context: Context, name: str) -> None:
tmp = Path(context.tmp_path)
named_dir = tmp / ".devcontainer" / name
named_dir.mkdir(parents=True, exist_ok=True)
(named_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
@given('the directory has a named devcontainer configuration "{name}" with invalid JSON')
def step_add_named_dc_invalid(context: Context, name: str) -> None:
tmp = Path(context.tmp_path)
named_dir = tmp / ".devcontainer" / name
named_dir.mkdir(parents=True, exist_ok=True)
(named_dir / "devcontainer.json").write_text("not valid json {{", encoding="utf-8")
@given("the directory has an empty .devcontainer directory")
def step_add_empty_dc_dir(context: Context) -> None:
tmp = Path(context.tmp_path)
dc_dir = tmp / ".devcontainer"
dc_dir.mkdir(exist_ok=True)
# ── Then steps for named configurations ──────────────────────
@then('the first result should have config name "{name}"')
def step_check_first_result_config_name(context: Context, name: str) -> None:
assert len(context.discovery_results) > 0
result = context.discovery_results[0]
assert result.config_name == name, (
f"Expected config_name={name!r}, got {result.config_name!r}"
)
@then("the first result should have no config name")
def step_check_first_result_no_config_name(context: Context) -> None:
assert len(context.discovery_results) > 0
result = context.discovery_results[0]
assert result.config_name is None, (
f"Expected config_name=None, got {result.config_name!r}"
)
@then('the result config names should include "{name}"')
def step_check_result_config_names_include(context: Context, name: str) -> None:
names = {r.config_name for r in context.discovery_results}
assert name in names, (
f"Expected {name!r} in config names, got {names!r}"
)