Files
kevinveenbirkenbachandClaude Opus 5 111b1bbcdf feat(ci): pin deploy axes from a selection token
A whitelist or priority entry was a role name and nothing else, so a run
could not ask for "this variant, on swarm, behind the onion". It either
covered everything the role offers or waited for the rotation to come
around to the combination that broke.

An entry is now a token that may carry the axes, either as
web-app-x#0,2@swarm+tor or as the job label pasted straight back out of
the run that failed. What a token leaves open still belongs to the line
it stands in: priority covers every remaining combination in one sweep,
whitelist keeps the rotation and its position in the matrix. A pin the
row cannot take aborts the matrix rather than dropping the row, because
a dropped row reports a green run for a combination that never ran.

The onion state spells out as +clearnet, not as a -tor suffix: svc-net-tor
would otherwise read as role svc-net on the clearnet.

Chunk membership still follows the discovery ranking, which is what
decides who makes the budget cut, but the rows inside a chunk are sorted
by display name, variant, mode and onion state, so a chunk's job list
reads like the plan table instead of like the sweep's rotation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 03:38:42 +02:00

75 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Resolve the effective INFINITO_WHITELIST that the discover step should pass to
# scripts/github/resolve/output_apps.sh, and write it to GITHUB_OUTPUT.
#
# Inputs via env:
# INPUT_WHITELIST caller-provided whitelist (space-separated), as role ids
# or as the display names utils.roles.display renders. A
# token may pin deploy axes onto the role
# (`#variant`, `@mode`, `+tor`, see
# utils.github.variant.selection); the pins are passed
# through untouched, and only the name in front of them is
# the directory this checks for.
# Three cases, in this order of precedence:
# * "__ALL__" (sentinel, case-insensitive): force
# full deploy across the workflow's scope. Skips
# the diff logic and emits an empty whitelist.
# * any other non-empty value: used verbatim.
# * empty: trigger the diff-based derivation.
#
# Output (GITHUB_OUTPUT):
# whitelist=<value> effective whitelist. An empty string means
# "no restriction; deploy everything in scope".
#
# Diff logic (only when INPUT_WHITELIST is empty):
# 1. Run scripts/meta/resolve/diff/affected_roles.sh.
# 2. If it returns "__ALL__", emit empty whitelist.
# 3. Otherwise emit the resolved (transitively expanded) role list.
set -euo pipefail
: "${GITHUB_OUTPUT:?Missing GITHUB_OUTPUT}"
# shellcheck source=scripts/meta/env/python.sh
source scripts/meta/env/python.sh
input="${INPUT_WHITELIST:-}"
input_trimmed="${input//[[:space:]]/}"
shopt -s nocasematch
if [[ "${input_trimmed}" == "__ALL__" ]]; then
shopt -u nocasematch
printf 'whitelist=\n' >>"${GITHUB_OUTPUT}"
echo "Caller forced full deploy via '__ALL__' sentinel."
exit 0
fi
shopt -u nocasematch
if [[ -n "${input_trimmed}" ]]; then
input="$("${PYTHON}" -m utils.roles.display "${input}")"
unknown=()
for role in ${input}; do
[[ -d "roles/${role%%[#@+]*}" ]] || unknown+=("${role}")
done
if ((${#unknown[@]})); then
echo "[ERROR] Unknown role id(s) in the caller-supplied whitelist: ${unknown[*]}" >&2
echo "[ERROR] They match no directory under roles/, so they would be dropped silently and never deployed." >&2
exit 1
fi
printf 'whitelist=%s\n' "${input}" >>"${GITHUB_OUTPUT}"
echo "Using caller-supplied whitelist: ${input}"
exit 0
fi
resolved="$(./scripts/meta/resolve/diff/affected_roles.sh)"
if [[ "${resolved}" == "__ALL__" ]]; then
printf 'whitelist=\n' >>"${GITHUB_OUTPUT}"
echo "Diff vs origin/main implies full deploy (no whitelist)."
exit 0
fi
printf 'whitelist=%s\n' "${resolved}" >>"${GITHUB_OUTPUT}"
echo "Diff-derived whitelist: ${resolved}"