Skip to content

config

config

The consumer's declarative surface — tests/containers/matrix.yml.

A consuming repo declares what its matrix is (image prefix, Containerfile flavor, slot selection, capability contract, test phases); the engine owns how a matrix runs. This module is the schema and its loader — parse, validate against the slot catalog, and hand the runner a frozen MatrixConfig.

Unknown keys and unknown slot names are hard errors: the file is small and hand-written, so a typo silently ignored would mean a slot or phase quietly not running.

SCOPES = ('unit', 'integ') module-attribute

MatrixConfigError

Bases: ValueError

A matrix.yml failed validation.

Phase(name, run=(), pytest=None, scope=None, expect_add=(), tolerate_failure=False) dataclass

One step of the in-container test flow.

A phase either runs shell commands (run) or a pytest invocation (pytest) — never both. Command phases abort the slot on failure (later phases depend on their effect, e.g. installed hooks); pytest phases record the failure and continue, so one run surfaces every failing suite.

Parameters:

Name Type Description Default
name str

Human-readable heading printed before the phase.

required
run tuple[str, ...]

Shell commands executed in order (command phase).

()
pytest str | None

Arguments to pytest (pytest phase).

None
scope str | None

Optional unit/integ tag; scope-filtered runs execute only pytest phases carrying the requested tag.

None
expect_add tuple[str, ...]

Capability names appended to TEROK_EXPECT after the phase succeeds (e.g. hooks once installed).

()
tolerate_failure bool

Run the commands best-effort (diagnostics).

False

name instance-attribute

run = () class-attribute instance-attribute

pytest = None class-attribute instance-attribute

scope = None class-attribute instance-attribute

expect_add = () class-attribute instance-attribute

tolerate_failure = False class-attribute instance-attribute

SlotConfig(extra_packages=(), skip_arches=(), skip_reason='', groups=None, expect=None, phases=None) dataclass

Per-slot choices a repo makes on top of the catalog facts.

Parameters:

Name Type Description Default
extra_packages tuple[str, ...]

Distro packages appended to the shared image's base set (the EXTRA_PACKAGES build arg).

()
skip_arches tuple[str, ...]

Host architectures (uname -m) the slot is skipped on, with skip_reason explaining why.

()
skip_reason str

Human-readable reason shown for the skip.

''
groups tuple[str, ...] | None

Override of the repo-level dependency groups.

None
expect tuple[str, ...] | None

Override of the repo-level capability contract.

None
phases tuple[Phase, ...] | None

Override of the repo-level phase list.

None

extra_packages = () class-attribute instance-attribute

skip_arches = () class-attribute instance-attribute

skip_reason = '' class-attribute instance-attribute

groups = None class-attribute instance-attribute

expect = None class-attribute instance-attribute

phases = None class-attribute instance-attribute

MatrixConfig(image_prefix, flavor, groups, expect, slots, phases, containers_dir, repo_root) dataclass

A repo's whole matrix declaration, resolved and validated.

Parameters:

Name Type Description Default
image_prefix str

Image/container name prefix and prune-label value.

required
flavor str

Shared Containerfile family (podman or dbus).

required
groups tuple[str, ...]

Dependency groups synced before testing.

required
expect tuple[str, ...]

TEROK_EXPECT capability contract; empty = not exported.

required
slots dict[str, SlotConfig]

Selected slots in declaration order.

required
phases tuple[Phase, ...]

Repo-level test flow.

required
containers_dir Path

Directory of the matrix.yml (fragments live here).

required
repo_root Path

Build context and bind-mounted source tree.

required

image_prefix instance-attribute

flavor instance-attribute

groups instance-attribute

expect instance-attribute

slots instance-attribute

phases instance-attribute

containers_dir instance-attribute

repo_root instance-attribute

slot_groups(name)

Dependency groups effective for slot name.

Source code in src/terok_util/matrix/config.py
def slot_groups(self, name: str) -> tuple[str, ...]:
    """Dependency groups effective for slot ``name``."""
    override = self.slots[name].groups
    return self.groups if override is None else override

slot_expect(name)

Capability contract effective for slot name.

Source code in src/terok_util/matrix/config.py
def slot_expect(self, name: str) -> tuple[str, ...]:
    """Capability contract effective for slot ``name``."""
    override = self.slots[name].expect
    return self.expect if override is None else override

slot_phases(name)

Test flow effective for slot name.

Source code in src/terok_util/matrix/config.py
def slot_phases(self, name: str) -> tuple[Phase, ...]:
    """Test flow effective for slot ``name``."""
    override = self.slots[name].phases
    return self.phases if override is None else override

load_config(path)

Load and validate a matrix.yml.

Parameters:

Name Type Description Default
path Path

The config file; its grandparent directory is taken as the repo root (<root>/tests/containers/matrix.yml).

required

Raises:

Type Description
MatrixConfigError

On unknown keys, unknown slots, or a phase that is neither a command phase nor a pytest phase.

Source code in src/terok_util/matrix/config.py
def load_config(path: Path) -> MatrixConfig:
    """Load and validate a ``matrix.yml``.

    Args:
        path: The config file; its grandparent directory is taken as the
            repo root (``<root>/tests/containers/matrix.yml``).

    Raises:
        MatrixConfigError: On unknown keys, unknown slots, or a phase that
            is neither a command phase nor a pytest phase.
    """
    raw = yaml_load(path.read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise MatrixConfigError(f"{path}: expected a YAML mapping at top level")

    data = _Section(dict(raw), where=str(path))
    flavor = data.string("flavor")
    if flavor not in FLAVORS:
        raise MatrixConfigError(f"{path}: unknown flavor '{flavor}'; known: {list(FLAVORS)}")
    config = MatrixConfig(
        image_prefix=data.string("image-prefix"),
        flavor=flavor,
        groups=data.strings("groups", default=("test",)),
        expect=data.strings("expect", default=()),
        slots=_parse_slots(data.mapping("slots"), where=str(path)),
        phases=_parse_phases(data.list_of_maps("phases"), where=str(path)),
        containers_dir=path.parent.resolve(),
        repo_root=path.parent.parent.parent.resolve(),
    )
    data.reject_leftovers()
    return config