diff --git a/tests/rig/selection.py b/tests/rig/selection.py index a1daac001a..e67945d0dd 100644 --- a/tests/rig/selection.py +++ b/tests/rig/selection.py @@ -7,6 +7,10 @@ The literal ``all`` expands to every group in the manifest. When the result is empty, callers should treat that as "do nothing" rather than silently running everything — the CLI surfaces a clear error. + +With ``--tier``, dep expansion never reaches outside that tier: each tier runs as +its own CI leg, so a cross-tier ``depends_on`` would otherwise re-run the same +group in every leg that transitively depends on it. """ from __future__ import annotations @@ -66,7 +70,18 @@ def resolve( if changed_only: selected.update(_groups_for_changed_paths(manifest, base=changed_base)) - return manifest.expand(sorted(selected)) if selected else [] + if not selected: + return [] + + requested = sorted(selected) + expanded = manifest.expand(requested) + if tier is not None: + # A tier run never pulls in another tier's groups. Tiers run as separate + # legs, so a cross-tier `depends_on` would re-run the same group once per + # leg; the owning tier's leg covers it. Explicitly requested groups stay. + keep = set(requested) + expanded = [n for n in expanded if n in keep or manifest.get(n).tier == tier] + return expanded def _groups_for_changed_paths(manifest: GroupManifest, *, base: str) -> set[str]: diff --git a/tests/rig/tests/test_selection.py b/tests/rig/tests/test_selection.py index d0ee7ef410..468a66667d 100644 --- a/tests/rig/tests/test_selection.py +++ b/tests/rig/tests/test_selection.py @@ -76,3 +76,51 @@ def test_tier_only_selects_that_tier_and_deps(tmp_path: Path) -> None: manifest = load_groups(_manifest(tmp_path)) ordered = resolve(manifest, positional=[], tier="unit") assert set(ordered) == {"unit-a", "unit-b"} + + +def _cross_tier_manifest(tmp_path: Path) -> Path: + p = tmp_path / "groups.yaml" + p.write_text( + """ + version: 1 + groups: + unit-a: + tier: unit + paths: [x] + optional: true + e2e-smoke: + tier: e2e + paths: [x] + requires_platform: true + optional: true + e2e-cross: + tier: e2e + paths: [x] + requires_platform: true + depends_on: [e2e-smoke, unit-a] + optional: true + """ + ) + return p + + +def test_tier_run_does_not_pull_in_other_tiers(tmp_path: Path) -> None: + """Each tier is its own CI leg; a cross-tier dep must not re-run there.""" + manifest = load_groups(_cross_tier_manifest(tmp_path)) + ordered = resolve(manifest, positional=[], tier="e2e") + assert set(ordered) == {"e2e-smoke", "e2e-cross"} + # Intra-tier ordering still holds. + assert ordered.index("e2e-smoke") < ordered.index("e2e-cross") + + +def test_explicitly_named_group_survives_tier_filter(tmp_path: Path) -> None: + """The filter only drops dep-expanded groups, never an explicit request.""" + manifest = load_groups(_cross_tier_manifest(tmp_path)) + ordered = resolve(manifest, positional=["unit-a"], tier="e2e") + assert set(ordered) == {"unit-a", "e2e-smoke", "e2e-cross"} + + +def test_cross_tier_dep_still_expands_without_tier_filter(tmp_path: Path) -> None: + manifest = load_groups(_cross_tier_manifest(tmp_path)) + ordered = resolve(manifest, positional=["e2e-cross"]) + assert set(ordered) == {"unit-a", "e2e-smoke", "e2e-cross"}