feat(ci): replay a role's README instructions on its smallest deploy row

The instructions test held a job of its own that picked one random role per
run, so a README's Production block was proven roughly once per 265 roles.
The replay now rides on a deploy row instead: no extra runner, and every role
the sweep touches gets its documented one-command deploy exercised.

The row is the role's smallest variant -- fewest services enabled -- because
that is the shape the README describes, and it is chosen among the variants
the sweep actually deploys, so a row cut as redundant coverage cannot silently
drop the role from the test. A role qualifies only when its README carries a
Production block and the guide's own mode is one the role runs and tests.

The marked entry carries the guide mode in `instructions` and a 📖 in its job
title, between the variant number and a priority ⭐. The guide mode is
independent of the mode the rotation gave the row: a swarm row still replays
in compose. The step runs last in the deploy job, after the swarm teardown,
because the replay deploys the role a second time and needs the runner's
ports.

call-test-instructions.yml, cli.meta.ci.guide_select and the `instructions`
dispatch input go with it. `redundant()` moves next to the marker so the
chunker, the plan table and the marker share one definition.

The plan table trades its 📊 Weight column for 📖 Instructions; nothing read
the weight any more, so it leaves the matrix entries too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-08-31 16:57:04 +02:00
1 parent bfde5876ba
commit 7fd495cdfc
22 files changed
+330 -303

No files matched your search

@@ -395,7 +395,6 @@ class TestConfigFromTitle(unittest.TestCase):
"filesystem": "ext4",
"chunk_gate": "false",
"workspace": "true",
"instructions": "false",
}
self.assertEqual(runs.config_from_title(render(dispatched)), dispatched)
@@ -1,53 +0,0 @@
from __future__ import annotations
import textwrap
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
from cli.meta.ci import guide_select
from utils.cache.files import PROJECT_ROOT
from utils.roles.mapping import ROLE_FILE_META_TESTS
from utils.roles.meta_lookup import MetaServicesShapeError, get_role_test_skips
class GuideModeTests(unittest.TestCase):
def test_role_without_stack_deploys_in_host_mode(self):
self.assertEqual(
guide_select._guide_mode(PROJECT_ROOT / "roles" / "svc-storage-nfs-client"),
"host",
)
def test_role_with_stack_deploys_in_compose_mode(self):
self.assertEqual(
guide_select._guide_mode(PROJECT_ROOT / "roles" / "web-app-yourls"),
"compose",
)
class TestableRolesTests(unittest.TestCase):
def test_pool_excludes_roles_whose_guide_mode_is_skipped(self):
pool = guide_select._testable_roles()
self.assertNotIn("svc-storage-nfs-client", pool)
self.assertTrue(pool)
def test_malformed_tests_yml_fails_selection_loudly(self):
with TemporaryDirectory() as tmp:
role_dir = Path(tmp) / "web-app-broken"
tests_path = role_dir / ROLE_FILE_META_TESTS
tests_path.parent.mkdir(parents=True)
tests_path.write_text(
textwrap.dedent(
"""
---
skip: not-a-list
"""
),
encoding="utf-8",
)
with self.assertRaises(MetaServicesShapeError):
get_role_test_skips(role_dir, role_name=role_dir.name)
if __name__ == "__main__":
unittest.main()
+12 -1
View File
@@ -15,6 +15,7 @@ def _entry(
identifier: str = "0",
covered: str = "0",
clone: bool = False,
instructions: str = "",
) -> dict:
return {
"apps": app,
@@ -23,7 +24,7 @@ def _entry(
"tor": "true" if variant == "0" else "false",
"disable": "",
"priority": "true" if priority else "false",
"weight": "42",
"instructions": instructions,
"id": identifier,
"covered": covered,
"clone": "true" if clone else "false",
@@ -68,6 +69,16 @@ class TestCells(unittest.TestCase):
self.assertEqual(rows[0][clone], "")
self.assertEqual(rows[-1][clone], to_emoji("clone"))
def test_the_instructions_column_names_the_mode_the_replay_runs_in(self) -> None:
entries = [
*_ENTRIES,
_entry("web-app-d", "0", "swarm", instructions="compose"),
]
rows = plan.cells(entries, [_PRIORITY, _REGULAR])
column = plan._COLUMNS.index("instructions")
self.assertEqual(rows[0][column], "")
self.assertEqual(rows[-1][column], to_emoji("compose"))
def test_the_mode_is_rendered_as_its_glyph(self) -> None:
rows = plan.cells(_ENTRIES, [_PRIORITY, _REGULAR])
mode = plan._COLUMNS.index("mode")
@@ -128,7 +128,6 @@ class DeclaredTemplateTests(unittest.TestCase):
dispatched = {
"chunk_gate": "true",
"workspace": "true",
"instructions": "false",
"filesystem": "ext4",
"distros": "debian arch",
"lifecycles": "stable",
@@ -0,0 +1,77 @@
import unittest
from typing import ClassVar
from unittest import mock
from utils.cache.applications import get_variants
from utils.roles.guide import guide_deployable, guide_variant, smallest_variant
def _variant(*enabled: str) -> dict:
return {"services": {name: {"enabled": True} for name in enabled}}
class TestSmallestVariant(unittest.TestCase):
def test_the_variant_with_the_fewest_enabled_services_wins(self) -> None:
variants = [_variant("a", "b", "c"), _variant("a"), _variant("a", "b")]
self.assertEqual(smallest_variant(variants), "1")
def test_a_tie_goes_to_the_lower_index(self) -> None:
self.assertEqual(smallest_variant([_variant("a"), _variant("b")]), "0")
def test_a_disabled_service_does_not_count(self) -> None:
variants = [
{"services": {"a": {"enabled": True}, "b": {"enabled": False}}},
_variant("a", "b"),
]
self.assertEqual(smallest_variant(variants), "0")
def test_a_variant_without_services_counts_as_empty(self) -> None:
self.assertEqual(smallest_variant([_variant("a"), {}]), "1")
def test_a_variant_the_sweep_cuts_hands_the_replay_on(self) -> None:
"""A row no sweep deploys would drop the role from the test entirely."""
variants = [_variant("a"), _variant("a", "b"), _variant("a", "b", "c")]
self.assertEqual(smallest_variant(variants, {"1", "2"}), "1")
def test_a_role_with_no_deployed_variant_carries_nothing(self) -> None:
self.assertEqual(smallest_variant([_variant("a")], set()), "")
class TestGuideVariant(unittest.TestCase):
_VARIANTS: ClassVar[dict] = {"web-app-x": [_variant("a", "b"), _variant("a")]}
_ALL: ClassVar[set] = {"0", "1"}
def test_the_smallest_deployed_variant_carries_the_replay(self) -> None:
with mock.patch("utils.roles.guide.guide_deployable", return_value="compose"):
self.assertEqual(
guide_variant("web-app-x", self._VARIANTS, self._ALL), ("1", "compose")
)
def test_a_role_the_guide_cannot_deploy_carries_nothing(self) -> None:
with mock.patch("utils.roles.guide.guide_deployable", return_value=""):
self.assertEqual(
guide_variant("web-app-x", self._VARIANTS, self._ALL), ("", "")
)
def test_without_variant_data_no_row_is_the_smallest(self) -> None:
self.assertEqual(guide_variant("web-app-x", None, self._ALL), ("", ""))
self.assertEqual(guide_variant("web-app-x", {}, self._ALL), ("", ""))
class TestGuideDeployable(unittest.TestCase):
"""Against the real roles, which is what the marker is computed over."""
def test_a_role_without_a_readme_production_block_is_excluded(self) -> None:
self.assertEqual(guide_deployable("does-not-exist"), "")
def test_the_mode_follows_whether_the_role_ships_a_stack(self) -> None:
self.assertEqual(guide_deployable("web-app-nextcloud"), "compose")
def test_the_replay_reaches_more_roles_than_the_single_random_pick(self) -> None:
"""The job it replaced tested exactly one role per run."""
marked = [app for app in get_variants() if guide_deployable(app)]
self.assertGreater(len(marked), 100)
if __name__ == "__main__":
unittest.main()