Files
computer-playbook/utils/github/variant/pools.py
kevinveenbirkenbachandClaude Opus 5 66bde6af40 feat(ci): spread distro and filesystem per row
A run-wide draw proved one distribution and said nothing about the other four, while reporting green. Both are matrix-row axes now, assigned by the same deterministic rotation mode and tor already use, so one sweep exercises every distribution and every filesystem.

The two read the row's position like an odometer, the distro as the low digit. Turning both on the position directly covers only n of the n x m pairs whenever the pools are the same length, and no sweep unlocks it.

Both are part of a row's identity: glyphs in the job label, columns in the plan table, shards in the artifact name. Both are selectable through %distro and /filesystem, and a retrigger replays the distro off a failed title. It leaves the filesystem out, because a title states the kind the matrix assigned and a deploy may have fallen back from it.

A row's filesystem is a preference unless a human names it, on the run or in a token. Reading the matrix's own narrowing as a demand would fail rows for every transient condition the applying step is built to tolerate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 14:23:29 +02:00

65 lines
2.2 KiB
Python

"""What the deploy axes may draw from, and how they draw.
Separate from :mod:`utils.github.variant.axes` because it answers a narrower
question: ``axes`` decides which value a given row takes, this module owns the
value sets themselves, the narrowing a run may apply to them, and the rotation
that walks them.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from utils.distros import distro_names
if TYPE_CHECKING:
from collections.abc import Sequence
DISTROS = distro_names()
FILESYSTEMS = ("zfs", "btrfs", "ext4")
def resolve_pool(
raw: str | None, declared: Sequence[str], axis: str
) -> tuple[str, ...]:
"""The values one axis may draw from this run.
Args:
raw: the run's input, space- or comma-separated. Empty means the whole
declared set, the same way ``--modes auto`` opens the mode axis.
declared: every value the axis knows, in declaration order.
axis: what to call the axis in the refusal.
Raises:
SystemExit: a token naming no declared value. A typo has to abort the
run rather than narrow it to a set nobody asked for.
"""
tokens = (raw or "").replace(",", " ").split()
if not tokens:
return tuple(declared)
unknown = [token for token in tokens if token not in declared]
if unknown:
raise SystemExit(
f"unknown {axis}(s): {' '.join(unknown)}; expected {', '.join(declared)}"
)
return tuple(value for value in declared if value in tokens)
def resolve_distros(raw: str | None) -> tuple[str, ...]:
"""The distributions this run spreads its rows over; empty means all."""
return resolve_pool(raw, DISTROS, "distro")
def resolve_filesystems(raw: str | None) -> tuple[str, ...]:
"""The docker data-root kinds this run spreads its rows over; empty means
all."""
return resolve_pool(raw, FILESYSTEMS, "filesystem")
def rotate(pool: Sequence[str], position: int, sweep: int) -> str:
"""The value a row draws from *pool* this sweep: a plain rotation over the
row's global position, so consecutive rows spread across the pool and
consecutive sweeps move every row on to the next value."""
return pool[(position + sweep) % len(pool)]