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/hostthe 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 // 2so 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
nof then x mpairs 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:
NamedTupleOne 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) andpriority, 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 –
enforcedonions every capable row,disablednone,exclusivedrops the rows that cannot take one,autorotates.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_filesystemand 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 –
enforcedmeshes every swarm row,disablednone,autorotates.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
idand thecoveredid 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 combinationcombinations()allows, so the roles a run is told to prove are proven everywhere at once rather than sampled over four sweeps.disablecarries 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
--modesinput, the onion states by its--torinput and the two pools by--distrosand--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:
Nonewhen the title carries no deploy row.nameis the display name, returned unresolved – callers decode it throughutils.roles.display, which is what knows the role tree.tor,distroandfilesystemmatter 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:
NamedTupleOne 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:
Nonewhen the title carries no deploy row.nameis the display name, returned unresolved – callers decode it throughutils.roles.display, which is what knows the role tree.tor,distroandfilesystemmatter 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 autoopens 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:
NamedTupleOne selection token, taken apart.
variantsempty andmode/tor/distro/filesystemNoneeach 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_distroandpin_filesystemforutils.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+toris 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@swarmasks 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-xcovers every row of that role andweb-app-x#2@swarmonly 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.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 meansauto.- Parameters:
raw – explicit value;
Nonereads 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;
Nonefor a role declaring none.variants_per_app – rendered variant configs per app;
Nonefalls back to the role’s basemeta/services.yml.
- Returns:
Falsewhen the covered variant pinsservices.tor.enabledto a literal false,Trueotherwise. 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 gatetruein variant 0 andfalsein 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
torservice,Noneif 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 underauto– a priority row is not sampled, it covers both states in the same sweep. An explicitenforced/exclusive/disabledis 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 // 2so 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;
Nonereads the environment.- Returns:
one of
VPN_MODES,autofor 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¶