mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-12 13:47:01 +00:00
web-app-openwebui 0,1,2 was killed by the 350-minute action timeout with no failed task anywhere in its 1.33M log lines. The sweep admitted fedora at remaining=10372s after debian took 10028s, then never looked again: the budget was only checked between distros. Its internal deadline passed 594 seconds before GitHub killed the job - the run knew it was over time for ten minutes and had no way to act. Worst-case runtime was deadline plus the full duration of the last admitted distro, unbounded. The trigger was a single stalled task (803s for a no-op that takes 1.3s warm, and a fully cold transaction 8 minutes later cost 24s), but that only decided which job hit the wall. The wall itself is structural. remaining is already computed at the top of the loop, so wrapping the call in timeout adds no new state. rc 124 becomes 'skipped' rather than 'failed', because a job that stops at its budget is the documented intent, not a defect - test_distro_budget_fits_timeout.py already asserts this invariant, which until now was simply untrue. Two conditions narrow it, and both matter: dur >= remaining - 5 keeps an inner timeout that fires early as a failure. Verified: timeout returns 124 only for its own kill and passes inner exit codes through unchanged (0 for success, 3 for an inner exit 3). passed > 0 is the one that protects the test result. Without it, a job whose very first distro overruns would report 'skipped' and exit 0 - green while having verified nothing. That would have been a silent false pass across all 243 jobs of a run. The guard was executed over the full truth table, including passed=0. -k 60 is deliberate: one.sh execs into dedicated.sh and its ansible and docker children can outlive SIGTERM. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
170 lines
5.0 KiB
Bash
Executable File
170 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# SPOT: run one command once per distro, in random order, under a shared time
|
|
# budget. A distro the budget drops is reported as skipped, not as a failure;
|
|
# only the per-distro command failing fails the run. Every outcome lands in a
|
|
# job-summary table in execution order.
|
|
#
|
|
# Param:
|
|
# $@ command + args to run once per distro
|
|
# INFINITO_DISTROS space-separated distro list
|
|
# INFINITO_CI_DISTRO_BUDGET_SECONDS wall-clock budget for the whole run
|
|
# GITHUB_STEP_SUMMARY optional; the markdown table is appended there
|
|
#
|
|
# Exports per iteration:
|
|
# INFINITO_DISTRO the distro under test
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
cd "${REPO_ROOT}"
|
|
|
|
# shellcheck source=scripts/meta/env/load.sh
|
|
source "scripts/meta/env/load.sh"
|
|
|
|
: "${INFINITO_DISTROS:?INFINITO_DISTROS is required (e.g. 'arch debian ubuntu fedora centos')}"
|
|
: "${INFINITO_CI_DISTRO_BUDGET_SECONDS:?INFINITO_CI_DISTRO_BUDGET_SECONDS is required (declared in default.env)}"
|
|
|
|
if (($# == 0)); then
|
|
echo "[ERROR] a per-distro command is required" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if ! [[ "${INFINITO_CI_DISTRO_BUDGET_SECONDS}" =~ ^[0-9]+$ ]]; then
|
|
echo "[ERROR] INFINITO_CI_DISTRO_BUDGET_SECONDS must be an integer (seconds), got: '${INFINITO_CI_DISTRO_BUDGET_SECONDS}'" >&2
|
|
exit 2
|
|
fi
|
|
|
|
read -r -a distro_arr <<<"${INFINITO_DISTROS}"
|
|
mapfile -t distro_arr < <(printf '%s\n' "${distro_arr[@]}" | shuf)
|
|
echo "=== Distro execution order: ${distro_arr[*]} ==="
|
|
|
|
global_start="$(date +%s)"
|
|
deadline="$((global_start + INFINITO_CI_DISTRO_BUDGET_SECONDS))"
|
|
echo "=== Global time budget: ${INFINITO_CI_DISTRO_BUDGET_SECONDS}s (deadline epoch=${deadline}) ==="
|
|
|
|
max_seen=0
|
|
skipped=0
|
|
ran=0
|
|
passed=0
|
|
durations=()
|
|
statuses=()
|
|
seconds=()
|
|
notes=()
|
|
|
|
for _ in "${distro_arr[@]}"; do
|
|
statuses+=("skipped")
|
|
seconds+=("")
|
|
notes+=("not reached within the budget")
|
|
done
|
|
|
|
# Renders the per-distro outcome to stdout and, when GITHUB_STEP_SUMMARY is set,
|
|
# appends the same run as a markdown table in execution order.
|
|
render_summary() {
|
|
local i distro icon
|
|
|
|
echo
|
|
echo "=== Summary ==="
|
|
echo "ran=${ran} skipped=${skipped}"
|
|
echo "total_runtime=$(($(date +%s) - global_start))s max_seen_duration=${max_seen}s"
|
|
echo "budget=${INFINITO_CI_DISTRO_BUDGET_SECONDS}s remaining=$((deadline - $(date +%s)))s"
|
|
echo "per-distro:"
|
|
for line in "${durations[@]}"; do
|
|
echo " - ${line}"
|
|
done
|
|
|
|
[[ -n "${GITHUB_STEP_SUMMARY:-}" ]] || return 0
|
|
|
|
{
|
|
echo "## 🐧 Distro coverage"
|
|
echo
|
|
echo "| # | Distro | Status | Duration | Note |"
|
|
echo "|--:|---|:--:|--:|---|"
|
|
for i in "${!distro_arr[@]}"; do
|
|
distro="${distro_arr[$i]}"
|
|
case "${statuses[$i]}" in
|
|
passed) icon="✅ passed" ;;
|
|
failed) icon="❌ failed" ;;
|
|
*) icon="🟦 skipped" ;;
|
|
esac
|
|
# shellcheck disable=SC2016 # backticks are markdown, not expansion
|
|
printf '| %s | `%s` | %s | %s | %s |\n' \
|
|
"$((i + 1))" "${distro}" "${icon}" \
|
|
"${seconds[$i]:+${seconds[$i]}s}" "${notes[$i]}"
|
|
done
|
|
echo
|
|
echo "${ran}/${#distro_arr[@]} ran, ${skipped} skipped, budget ${INFINITO_CI_DISTRO_BUDGET_SECONDS}s, order randomised per run."
|
|
echo
|
|
} >>"${GITHUB_STEP_SUMMARY}"
|
|
}
|
|
|
|
for i in "${!distro_arr[@]}"; do
|
|
distro="${distro_arr[$i]}"
|
|
remaining="$((deadline - $(date +%s)))"
|
|
|
|
if ((remaining <= 0)); then
|
|
echo "[WARN] Global budget exhausted (remaining=${remaining}s). Stopping further distro runs."
|
|
skipped=$((skipped + ${#distro_arr[@]} - i))
|
|
notes[i]="budget exhausted"
|
|
break
|
|
fi
|
|
|
|
if ((max_seen > 0 && remaining < max_seen)); then
|
|
echo "[WARN] Skipping distro=${distro}: remaining=${remaining}s < max_seen=${max_seen}s (fast-fail heuristic)"
|
|
skipped=$((skipped + 1))
|
|
notes[i]="remaining ${remaining}s < slowest run ${max_seen}s"
|
|
continue
|
|
fi
|
|
|
|
echo "=== Running distro=${distro}: ${1} ==="
|
|
echo ">>> Time budget: remaining=${remaining}s max_seen=${max_seen}s"
|
|
|
|
export INFINITO_DISTRO="${distro}"
|
|
source "scripts/meta/env/load.sh"
|
|
|
|
distro_start="$(date +%s)"
|
|
|
|
set +e
|
|
timeout -k 60 "${remaining}s" "$@"
|
|
rc=$?
|
|
set -e
|
|
|
|
dur="$(($(date +%s) - distro_start))"
|
|
durations+=("${distro}=${dur}s")
|
|
ran=$((ran + 1))
|
|
seconds[i]="${dur}"
|
|
notes[i]=""
|
|
|
|
if ((dur > max_seen)); then
|
|
max_seen="$dur"
|
|
fi
|
|
|
|
echo ">>> Duration: distro=${distro} took ${dur}s (max_seen=${max_seen}s)"
|
|
|
|
if [[ $rc -eq 124 ]] && ((dur >= remaining - 5)) && ((passed > 0)); then
|
|
statuses[i]="skipped"
|
|
notes[i]="budget exhausted mid-run after ${dur}s"
|
|
skipped=$((skipped + 1))
|
|
ran=$((ran - 1))
|
|
echo "[WARN] Budget exhausted while distro=${distro} was running; stopping."
|
|
break
|
|
fi
|
|
|
|
if [[ $rc -ne 0 ]]; then
|
|
statuses[i]="failed"
|
|
notes[i]="rc=${rc}"
|
|
skipped=$((skipped + ${#distro_arr[@]} - i - 1))
|
|
for ((j = i + 1; j < ${#distro_arr[@]}; j++)); do
|
|
notes[j]="not run: aborted after distro=${distro} failed"
|
|
done
|
|
echo "[ERROR] Run failed for distro=${distro} (rc=${rc})" >&2
|
|
render_summary
|
|
exit "$rc"
|
|
fi
|
|
|
|
statuses[i]="passed"
|
|
passed=$((passed + 1))
|
|
done
|
|
|
|
render_summary
|