Files
computer-playbook/utils/github/variant/instructions.py
T
kevinveenbirkenbachandClaude Opus 5.5 91022ad37a fix(ci): let only the primary app's declared variant bring the guide host
Every role's base services carry `docs.enabled: "{{ 'web-app-docs' in
group_names }}"`, and the static closure counts any "in group_names" flag
as possibly true (is_explicit_truth). A round therefore pulled web-app-docs
in through any member whose active variant did not pin docs off, and a
round hands every role its index or clamps it to variant 0, where 84 roles
declare docs on. The README replay and everything it drags in (dashboard,
matomo, keycloak, prometheus with its MCP partners) landed in 365 of 469
rounds instead of one per role, which ran CI run 40625679270 out of
resources: Listmonk#0 memory, OpenProject#0 I/O in the DR drill,
Nextcloud#0/#2 budget (4310-4539 s in the replay each, 60 s over),
BookWyrm#0 drill disk, Matrix#1/#3 pids in the replay; none of those
primaries declares docs in the failing variant.

The guide host now stays on only where the primary app's own variant
declares it (declares_guide, the predicate instructions.py already used);
every other role has it switched off (guide_off), in the planner's round
overrides and in the swarm include set. The host_vars bake drops a docs
edge whenever the round does not deploy web-app-docs. Measured for
listmonk, openproject, translate and mariadb: web-app-docs is deployed in
exactly each primary's declared guide round, swarm and planner agree in
all 12 rounds, and rounds shed up to ten apps (mariadb#1 19 -> 9).

test_guide_rides_the_primary_only pins that on real role data (it fails
with the rule disabled); test_variant_pin_required leaves the guide host
out of its clone comparison and no longer counts the docs key as a
pairing edge, because the replay no longer follows a partner's index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:49:51 +01:00

72 lines
2.5 KiB
Python

"""Which matrix entry replays its role's README instructions.
The replay used to be a job of its own, picking one random role per run. It
rides on a deploy row instead, marked with ``instructions`` (the mode the
replay runs in). The job title says nothing about it: the row is an ordinary
deploy of its variant, and the guide host is one more service that variant
pulls in.
Which row carries it is declared, not computed: the role's ``meta/variants.yml``
switches the ``docs`` service on in exactly one variant, and the deploy pulls
the guide host in through that service edge. This module only reads that
declaration back out and turns it into matrix entries.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from utils.roles.guide import declares_guide, guide_deployable
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence
from typing import Any
def redundant(entry: Mapping[str, str]) -> bool:
"""Whether the sweep drops this row as coverage an earlier row already has."""
if entry["priority"] == "true":
return False
return entry["clone"] == "true" or entry["covered"] != "0"
def _carries_guide(
app: str,
variant: str,
variants_per_app: Mapping[str, Sequence[Mapping[str, Any]]] | None,
) -> bool:
"""Whether *app*'s *variant* switches the guide host on."""
variants = (variants_per_app or {}).get(app) or []
if not variant.isdigit() or int(variant) >= len(variants):
return False
return declares_guide((variants[int(variant)] or {}).get("services") or {})
def mark(
entries: list[dict[str, str]],
variants_per_app: Mapping[str, Sequence[Mapping[str, Any]]] | None,
) -> list[dict[str, str]]:
"""Hand one entry per role the replay.
Only the first entry of the chosen variant: a priority row runs every mode
and network mode it offers, and replaying the same README block in each of
them proves nothing the first replay did not.
The row has to run in the guide's own mode. The replay is a second deploy
on the same runner, so hanging it on a swarm row would ask that runner to
bring a compose stack up beside a swarm one.
"""
marked: set[str] = set()
for entry in entries:
app = entry["apps"]
if app in marked or redundant(entry):
continue
mode = guide_deployable(app)
if not mode or entry["mode"] != mode:
continue
if not _carries_guide(app, entry["variant"], variants_per_app):
continue
entry["instructions"] = mode
marked.add(app)
return entries