utils.github.variant package

Submodules

utils.github.variant.axes module

Assign the deploy axes to CI matrix rows.

Every row of the CI matrix is one role#variant selection. Four axes are decided here, all as a deterministic rotation over the row’s position in the global discovery order and the sweep number – never at random, so a red job can be reproduced by re-running the same sweep, and so consecutive sweeps cover the combinations instead of sampling them:

  • mode – which of compose/swarm/host the row deploys in, drawn from the modes the role actually offers. In practice that is at most two: swarm requires the role to ship its own stack, host requires it not to. (position + sweep) % len(offered) therefore flips a row between its two modes on consecutive sweeps.

  • tor – whether the row deploys behind the node onion. Driven by sweep // 2 so it does NOT flip in lockstep with the mode.

  • distro – which declared distribution the row deploys on. Per row rather than per run, so one sweep proves every distribution instead of proving one and claiming nothing about the other four, and a red row still names the exact distribution it died on.

  • filesystem – which kind the row’s docker data root runs on. The two read the row’s position like an odometer, the distro as the low digit, so consecutive rows walk every pairing rather than a diagonal through it. Turning both on the position directly would cover only n of the n x m pairs whenever the pools are the same length, and no sweep would unlock it.

A row’s position is its index in the uncapped discovery order, not its index inside a chunk, so slicing the list into chunks never changes what a row is assigned. A priority row, which deploys several mode/tor combinations at once, walks the distro and filesystem rotations across those combinations rather than repeating one pair.

class utils.github.variant.axes.Label(mode: str, name: str, variant: str, tor: bool, distro: str = '', filesystem: str = '', vpn: bool = False)

Bases: NamedTuple

One deploy job title, taken apart.

distro: str

Alias for field number 4

filesystem: str

Alias for field number 5

mode: str

Alias for field number 0

name: str

Alias for field number 1

tor: bool

Alias for field number 3

variant: str

Alias for field number 2

vpn: bool

Alias for field number 6

utils.github.variant.axes.artifact_slug(mode: str, app: str, variant: str, tor: bool, distro: str = '', filesystem: str = '', vpn: bool = False) → str

What identifies one deploy job’s artifacts.

Built here rather than as a workflow expression so the matrix entry and every consumer read the same string: a priority role runs the same mode and variant twice, once behind the onion and once not, and two jobs uploading under one name is an artifact conflict, not an overwrite. The distro and filesystem are in it for the same reason – a selection may name one row on two distros (role#0%debian role#0%fedora), and those are two deploys of one mode, variant and onion state.

utils.github.variant.axes.assign(rows: Sequence[Mapping[str, Any]], *, sweep: int, tor_mode: str, distros: Sequence[str], filesystems: Sequence[str], vpn_mode: str = 'auto', variants_per_app: Mapping[str, Sequence[Mapping[str, Any]]] | None = None) → list[dict[str, str]]

Turn ordered discovery rows into CI matrix entries.

Parameters:
  • rows – discovery rows, each carrying name, variant, modes (the offered subset) and priority, in global query order. pin_mode/pin_tor/pin_distro/pin_filesystem, when a selection token set them, pin that axis: the priority line then covers only the combinations that match, and the regular line takes the pin instead of its rotation.

  • sweep – sweep number driving every rotation.

  • tor_mode – enforced onions every capable row, disabled none, exclusive drops the rows that cannot take one, auto rotates.

  • distros – the distributions this run may draw from (resolve_pool()).

  • filesystems – the docker data-root kinds this run may draw from. A pool of exactly one is a human naming it: the entry then carries enforce_filesystem and a host that cannot serve it fails instead of substituting one. A wider pool leaves the kind a preference, because the matrix narrows every row to one anyway.

  • vpn_mode – enforced meshes every swarm row, disabled none, auto rotates.

  • variants_per_app – rendered variant configs per app, so a variant that switches the tor gate off is never counted capable.

Returns:

one entry per (row, mode, tor) the run deploys, each stamped with the distro and filesystem its rotation drew, carrying the row’s discovery id and the covered id of the earlier row that already embeds it (0: nothing does), so a reader of the plan can tell a redundant row from a unique one without a second query. A regular row yields exactly one. A priority row yields every combination combinations() allows, so the roles a run is told to prove are proven everywhere at once rather than sampled over four sweeps. disable carries the provider tokens the deploy drill switches off; a row without tor disables the provider so no dependency edge can pull it back into the closure. The provider’s own rows therefore never take the clearnet state: disabling tor there would strip the app under test out of its own deploy.

utils.github.variant.axes.check_pins(app: str, variant: str, offered: Sequence[str], *, pin_mode: str | None, pin_tor: bool | None, pin_distro: str | None, pin_filesystem: str | None, capable: bool, tor_mode: str, distros: Sequence[str], filesystems: Sequence[str]) → None

Prove the row can take what the selection token pinned on it.

The offered modes are already narrowed by the run’s --modes input, the onion states by its --tor input and the two pools by --distros and --filesystem, so this catches a pin that fights the role, the variant, or the run’s own axes with one check each.

utils.github.variant.axes.parse_label(name: str) → Label | None

Take a deploy job title apart.

The inverse of what assign() builds, kept next to it so the two cannot drift: consumers that hand-rolled their own regex over raw role ids silently matched nothing once job titles carried display names, and every failure went unreported.

Parameters:

name – the job title, with or without a reusable-workflow caller path in front of it.

Returns:

None when the title carries no deploy row. name is the display name, returned unresolved – callers decode it through utils.roles.display, which is what knows the role tree. tor, distro and filesystem matter because a priority role runs the same mode and variant several times over, and only the glyphs tell those jobs apart – a retrigger built from the title alone would otherwise replay a different combination than the one that failed.

utils.github.variant.axes.pick_mode(offered: Sequence[str], position: int, sweep: int) → str

The deploy mode a row runs in this sweep.

Parameters:
  • offered – modes the role supports, in a stable order.

  • position – the row’s index in the global discovery order.

  • sweep – sweep number.

Raises:

ValueError – offered is empty – a row the query returned always has at least one mode, so an empty list is a bug upstream, not a case to paper over with a fallback mode.

utils.github.variant.axes.resolve_sweep(raw: str | None = None) → int

Sweep number from INFINITO_CI_SWEEP; it drives both rotations.

utils.github.variant.axes.sort_key(entry: Mapping[str, str]) → tuple[Any, ...]

Where one entry sorts inside its chunk: display name, then variant, then deploy mode, then onion state.

The chunk split itself is not sorted – it follows the discovery ranking, which is what decides who makes the budget cut. This only orders the jobs a chunk already holds, so a chunk’s job list reads like the plan table instead of like the sweep’s rotation.

utils.github.variant.instructions module

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: the role’s smallest variant among the rows this sweep actually deploys, marked with instructions (the mode the replay runs in) and a 📖 in the job title. utils.roles.guide decides which variant that is; this module is what turns the decision into matrix entries.

utils.github.variant.instructions.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, and give its label the 📖.

Only the first entry of the chosen variant: a priority row runs every mode and onion state it offers, and replaying the same README block in each of them proves nothing the first replay did not. The glyph goes between the variant number and the ⭐ of a priority row, with no space between the two: they are one marker block, not two axes.

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. Only rows in that mode count as deployed here either, so the variant search cannot settle on one that this sweep runs in swarm alone.

utils.github.variant.instructions.redundant(entry: Mapping[str, str]) → bool

Whether the sweep drops this row as coverage an earlier row already has.

utils.github.variant.label module

utils.github.variant.label.LABEL_RE = re.compile('^.*(?P<mode>🐳|🐝|💻)️?(?P<tor>🧅)?(?:🌐|🏠)?️?(?P<vpn>🚇)?️?(?:🛣️)?️?(?P<distro>🏹|🌀|🟠|🎩|🟣)?️?(?P<filesystem>🦓|🦋|🐘)?️?[🧅🌐🚇🛣️⭐📖🏠️\\s]*(?P<name>.+?)(?:\\#(?P<variant>[0-9,]+))?[🧅🌐🚇🛣️⭐📖🏠️\\s]*$')

it anchors on the LAST mode glyph. A reusable-workflow caller path can carry a mode glyph of its own (z / 💻 Host / 💻 sys-front-proxy), and matching the first one would swallow the caller name into the role.

Type:

The leading .* is greedy on purpose

class utils.github.variant.label.Label(mode: str, name: str, variant: str, tor: bool, distro: str = '', filesystem: str = '', vpn: bool = False)

Bases: NamedTuple

One deploy job title, taken apart.

distro: str

Alias for field number 4

filesystem: str

Alias for field number 5

mode: str

Alias for field number 0

name: str

Alias for field number 1

tor: bool

Alias for field number 3

variant: str

Alias for field number 2

vpn: bool

Alias for field number 6

utils.github.variant.label.parse_label(name: str) → Label | None

Take a deploy job title apart.

The inverse of what assign() builds, kept next to it so the two cannot drift: consumers that hand-rolled their own regex over raw role ids silently matched nothing once job titles carried display names, and every failure went unreported.

Parameters:

name – the job title, with or without a reusable-workflow caller path in front of it.

Returns:

None when the title carries no deploy row. name is the display name, returned unresolved – callers decode it through utils.roles.display, which is what knows the role tree. tor, distro and filesystem matter because a priority role runs the same mode and variant several times over, and only the glyphs tell those jobs apart – a retrigger built from the title alone would otherwise replay a different combination than the one that failed.

utils.github.variant.pools module

What the deploy axes may draw from, and how they draw.

Separate from 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.

utils.github.variant.pools.resolve_distros(raw: str | None) → tuple[str, ...]

The distributions this run spreads its rows over; empty means all.

utils.github.variant.pools.resolve_filesystems(raw: str | None) → tuple[str, ...]

The docker data-root kinds this run spreads its rows over; empty means all.

utils.github.variant.pools.resolve_pool(raw: str | None, declared: Sequence[str], axis: str) → tuple[str, ...]

The values one axis may draw from this run.

Parameters:
  • 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.

utils.github.variant.pools.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.

utils.github.variant.selection module

Read the deploy axes an operator pins in whitelist and priority.

A selection token names a role and MAY narrow the axes that role’s rows would otherwise be assigned: which variants run, which deploy mode, whether the row goes behind the node onion, which distribution it deploys on and which filesystem its docker data root runs on. What a token leaves open stays open, exactly as an unpinned run leaves it: the priority line covers every mode and onion state the row can take, and the sweep rotation picks the distro and the filesystem on both lines.

Two spellings are accepted, so an operator can either type the token or paste back the job title of the run they want repeated:

  • the job label CI emits (🐳🧅🌀🦓网络应用·Nextcloud#2) – the glyphs carry mode, onion state, distro and filesystem, the # shard the variants;

  • an ASCII form (web-app-nextcloud#0,2@swarm+tor%debian/zfs&vpn).

The onion state spells out as +tor/+clearnet rather than as a -tor suffix: a role id may itself end in -tor (svc-net-tor), and a suffix that eats the tail of a role name selects a different role in silence.

Everything a token pins is checked against what the row can actually do (utils.github.variant.axes.assign()) and against the run’s own mode and tor inputs. A contradiction aborts the matrix instead of quietly deploying something else or nothing at all.

class utils.github.variant.selection.Pin(app: str, variants: tuple[int, ...] = (), mode: str | None = None, tor: bool | None = None, distro: str | None = None, filesystem: str | None = None, vpn: bool | None = None)

Bases: NamedTuple

One selection token, taken apart.

variants empty and mode/tor/distro/filesystem None each mean “not pinned”: that axis keeps whatever the line it stands in would assign.

app: str

Alias for field number 0

property axes: tuple[str | None, bool | None, str | None, str | None, bool | None]

What the token narrows, as the key two tokens are equal under.

distro: str | None

Alias for field number 4

filesystem: str | None

Alias for field number 5

mode: str | None

Alias for field number 2

property pinned: bool

Whether the token narrows anything at all beyond the role name.

tor: bool | None

Alias for field number 3

variants: tuple[int, ...]

Alias for field number 1

vpn: bool | None

Alias for field number 6

utils.github.variant.selection.apply(rows: Sequence[Mapping[str, Any]], pins: Sequence[Pin]) → list[dict[str, Any]]

Keep the rows the pins select and stamp the pinned axes onto them.

Parameters:
  • rows – discovery rows of one line, in query order.

  • pins – that line’s selection tokens; empty means “no selection”, and every row passes through untouched.

Returns:

one entry per (row, pin) the selection asks for, each carrying pin_mode, pin_tor, pin_distro and pin_filesystem for utils.github.variant.axes.assign() to honour. Order is the query’s – a selection narrows what runs, it never re-ranks it.

A row several tokens name is emitted once per token, which is the whole point of the axes: role#1@compose+tor role#1@swarm+tor is one variant that failed in two modes, and it has to come back as two deploys. Two tokens narrowing a row the same way collapse into one, so a duplicate in the input cannot become two jobs racing for one artifact name.

A bare role name loses to any token that narrows the same row: writing role role#1@swarm asks for a specific deploy, not for that deploy plus a rotation-picked one on top.

Raises:

SystemExit – a token pins a variant of a role the query DID return, and no row carries that variant. Silently deploying nothing is how a mistyped variant index turns into a green run that tested nothing. A token whose role the query returned nothing for is let through instead: the diff-derived whitelist legitimately names roles the run’s mode and lifecycle envelope filters out, and since that resolver now pins the variants a change actually reaches, such a token would otherwise abort every chunk of the run. Role names are checked against roles/ before the run starts (scripts/github/resolve/effective_whitelist.sh), so a typo is still caught – one layer earlier.

utils.github.variant.selection.covers(pin: Pin, entry: Mapping[str, Any]) → bool

Whether pin names this matrix entry.

An axis the token leaves open matches anything, so web-app-x covers every row of that role and web-app-x#2@swarm only its swarm deploy of variant 2. Reads the strings a matrix entry carries, not a discovery row’s native types.

utils.github.variant.selection.describe(pin: Pin) → str

The token as an operator would have written it, for error messages.

utils.github.variant.selection.names(pins: Iterable[Pin]) → str

The role ids the pins select, deduplicated, in the order given – what the discovery query filters on. The axes are applied afterwards, on the rows the query returned.

utils.github.variant.selection.parse(token: str) → Pin

One selection token as a Pin.

Parameters:

token – label form or ASCII form; a bare role id or display name pins nothing and selects every row of that role.

Raises:

SystemExit – the token is unparsable, or names a mode or onion state that does not exist. A typo must abort the run rather than narrow it to nothing.

utils.github.variant.selection.parse_list(tokens: str) → list[Pin]

Every token of a space-separated whitelist/priority input.

utils.github.variant.tor module

The onion axis of the CI deploy matrix: what it may do, and to which rows.

Separate from utils.github.variant.axes because it answers a different question. axes decides which combination a row takes this sweep; this module decides which onion states exist for that row at all – read off the role’s meta/services.yml, off the covered variant, off the deploy mode, and off the run’s own tor input.

utils.github.variant.tor.combinations(offered: Sequence[str], *, capable: bool, tor_mode: str) → list[tuple[str, bool]]

Every (mode, tor) pair a priority row is deployed in.

Priority rows are the ones a run must not sample: they cover the whole cross-product of the modes their role offers and the onion states each of those modes can take, in one sweep, instead of walking it over four.

utils.github.variant.tor.resolve_tor_mode(raw: str | None = None) → str

Tor axis mode from INFINITO_TOR; unknown or empty means auto.

Parameters:

raw – explicit value; None reads the environment.

Returns:

one of TOR_MODES.

utils.github.variant.tor.tor_capable(app: str, variant: int | None = None, variants_per_app: Mapping[str, Sequence[Mapping[str, Any]]] | None = None) → bool

Whether a matrix row may be deployed behind the node onion.

Parameters:
  • app – application id.

  • variant – the row’s variant index; None for a role declaring none.

  • variants_per_app – rendered variant configs per app; None falls back to the role’s base meta/services.yml.

Returns:

False when the covered variant pins services.tor.enabled to a literal false, True otherwise. A reactive Jinja flag counts as capable: it is the role saying “onion when the node is dark”. Consulting the variant matters – roles pin the gate true in variant 0 and false in the rest, so reading only the base config claims an onion for rounds that deliberately run without one.

utils.github.variant.tor.tor_provider() → str | None

Application id of the role providing the tor service, None if the registry names none. Resolved rather than hardcoded so renaming the provider role cannot leave the matrix pointing at a dead id.

utils.github.variant.tor.tor_states(mode: str, *, capable: bool, tor_mode: str) → list[bool]

The onion states one mode is worth running for a priority row.

Parameters:
  • mode – the deploy mode the row runs in.

  • capable – whether the row’s variant may take an onion at all.

  • tor_mode – the run’s tor axis.

Returns:

[True, False] on a tor-carrying mode under auto – a priority row is not sampled, it covers both states in the same sweep. An explicit enforced/exclusive/disabled is an operator narrowing and still wins, and a mode that carries no onion axis at all (host) only ever yields the clearnet state.

utils.github.variant.tor.wants_tor(position: int, sweep: int) → bool

Whether a capable row takes the onion this sweep. Halved on sweep // 2 so it does not flip in lockstep with the mode rotation.

utils.github.variant.vpn module

utils.github.variant.vpn.pinned_vpn_states(mode: str, *, pin: bool | None = None, vpn_mode: str = 'auto') → list[bool]

The mesh states a priority row takes, global axis ahead of the pin.

Parameters:
  • mode – the deploy mode of the row.

  • pin – the state the row asked for, or None for every state.

  • vpn_mode – the run’s axis mode.

utils.github.variant.vpn.resolve_vpn_mode(raw: str | None = None) → str

Mesh axis mode from INFINITO_VPN.

Parameters:

raw – explicit value; None reads the environment.

Returns:

one of VPN_MODES, auto for an unknown or empty value.

utils.github.variant.vpn.rotated_vpn(mode: str, *, position: int, sweep: int, pin: bool | None = None, vpn_mode: str = 'auto') → bool
utils.github.variant.vpn.vpn_states(mode: str, *, vpn_mode: str = 'auto') → list[bool]
utils.github.variant.vpn.wants_vpn(position: int, sweep: int) → bool

Module contents