mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
ruff format reflows long calls and normalises quotes, isort reorders the imports of the documentation lint package, SIM117 folds nested `with` statements into one parenthesised block, TC003 moves typing-only imports under `if TYPE_CHECKING`, the JavaScript linter replaces a `var`, and the YAML formatter rewraps five role files. None of it changes behaviour. One entry does more than that: the formatter had reflowed two calls in test_builder.py so their `# nocheck: cache-read` markers landed on the closing parenthesis, where they suppress nothing and the cache-read lint would have reported two real violations. Both calls are hoisted into a variable so the marker sits on the reading line again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
109 lines
4.0 KiB
Python
109 lines
4.0 KiB
Python
"""Which deploy row replays the role's README instructions, and in what mode.
|
|
|
|
The instructions test used to be a job of its own that picked one random role
|
|
per run. Riding along on a deploy row instead costs no extra runner and covers
|
|
every role the sweep touches. The row it rides on is the role's smallest
|
|
variant -- the one enabling the fewest services -- because that is the shape
|
|
the README's Production block describes: the bare role, with the optional
|
|
providers switched off.
|
|
|
|
The replay is a second deploy on the same runner, so the row it picks must be
|
|
one the guide can actually deploy: the role has to be invokable, because that
|
|
is what makes the documentation build give it a Production block, and the
|
|
guide's own mode (``compose`` for a role that ships a stack, ``host`` for one
|
|
installed onto the machine) has to be a mode the role runs and tests.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from functools import cache
|
|
from typing import TYPE_CHECKING
|
|
|
|
from utils.cache.files import PROJECT_ROOT
|
|
from utils.roles.deploy import role_has_stack
|
|
from utils.roles.meta_lookup import get_role_mode_enabled, get_role_test_skips
|
|
from utils.roles.validation.invokable import _get_invokable_paths, _is_role_invokable
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Collection, Mapping, Sequence
|
|
from typing import Any
|
|
|
|
|
|
def _enabled_services(variant: Mapping[str, Any]) -> int:
|
|
services = variant.get("services") or {}
|
|
return sum(
|
|
1
|
|
for config in services.values()
|
|
if isinstance(config, dict) and config.get("enabled")
|
|
)
|
|
|
|
|
|
def smallest_variant(
|
|
variants: Sequence[Mapping[str, Any]], allowed: Collection[str] | None = None
|
|
) -> str:
|
|
"""The variant that enables the fewest services, as the matrix writes it;
|
|
the lowest index wins a tie, and ``''`` means none qualifies.
|
|
|
|
Args:
|
|
variants: the role's rendered variant configs, in index order.
|
|
allowed: the variants this sweep actually deploys. A variant the sweep
|
|
cuts as redundant coverage never reaches a runner, so hanging the
|
|
replay on it would silently drop the role from the instructions
|
|
test; the next-smallest deployed variant carries it instead.
|
|
"""
|
|
candidates = [
|
|
index
|
|
for index in range(len(variants))
|
|
if allowed is None or str(index) in allowed
|
|
]
|
|
if not candidates:
|
|
return ""
|
|
return str(
|
|
min(candidates, key=lambda index: (_enabled_services(variants[index]), index))
|
|
)
|
|
|
|
|
|
@cache
|
|
def guide_deployable(app: str) -> str:
|
|
"""The mode the guide would replay *app* in, or ``''`` when it cannot.
|
|
|
|
Independent of the variant, and cached: the answer is a property of the
|
|
role's files, and :func:`utils.github.variant.axes.assign` asks it once per
|
|
row of every chunk.
|
|
"""
|
|
role_dir = PROJECT_ROOT / "roles" / app
|
|
if not _is_role_invokable(app, _get_invokable_paths()):
|
|
return ""
|
|
mode = "compose" if role_has_stack(role_dir) else "host"
|
|
if mode in get_role_test_skips(role_dir, role_name=app):
|
|
return ""
|
|
if not get_role_mode_enabled(role_dir, mode=mode, role_name=app):
|
|
return ""
|
|
return mode
|
|
|
|
|
|
def guide_variant(
|
|
app: str,
|
|
variants_per_app: Mapping[str, Sequence[Mapping[str, Any]]] | None,
|
|
allowed: Collection[str],
|
|
) -> tuple[str, str]:
|
|
"""The variant of *app* that carries the replay, and the mode it runs in.
|
|
|
|
``('', '')`` when the role has no instructions to replay or this sweep
|
|
deploys no variant that could carry them.
|
|
|
|
Args:
|
|
app: the role.
|
|
variants_per_app: rendered variant configs per app; without them no
|
|
variant can be told to be the smallest one.
|
|
allowed: the variants this sweep deploys, as the matrix writes them.
|
|
"""
|
|
variants = (variants_per_app or {}).get(app)
|
|
if not variants:
|
|
return "", ""
|
|
mode = guide_deployable(app)
|
|
if not mode:
|
|
return "", ""
|
|
variant = smallest_variant(variants, allowed)
|
|
return (variant, mode) if variant else ("", "")
|