feat: Forgejo-native CI — a ci-box tenant and a forgejo-runner command family #110

Merged
andres merged 9 commits from build/109-forgejo-ci-box into main 2026-07-28 19:58:37 +00:00
3 changed files with 33 additions and 3 deletions
Showing only changes of commit ab29ee78fb - Show all commits

View file

@ -347,7 +347,10 @@ if [ "$NEED_DOWNLOAD" -eq 1 ]; then
mv -f "$BIN.rig-new" "$BIN"
log "installed ${BIN}"
fi
INSTALLED_VER="$("$BIN" --version 2>/dev/null | head -n1)"
# `|| true` so the refusal BELOW is the one that fires. Under set -euo pipefail
# a bare `VAR="$(cmd | head)"` dies at the assignment when cmd exits non-zero,
# which is precisely the case this line exists to diagnose — see the lib.
INSTALLED_VER="$("$BIN" --version 2>/dev/null | head -n1 || true)"
[ -n "$INSTALLED_VER" ] || die "${BIN} does not answer --version — the download landed but cannot run"
# The converge actually took — asserted, not assumed. A pin that silently did
# not land is exactly the failure --version exists to make impossible.

View file

@ -59,9 +59,21 @@ forgejo_runner_secure() {
# reports, empty when it cannot answer. `forgejo-runner --version` prints
# "forgejo-runner version v12.13.2"; the leading v is stripped so this compares
# against a --version argument, which has its own v stripped at parse.
#
# `|| true` for json_field's reason, which bites harder here. Callers run under
# `set -euo pipefail`, where a pipeline whose FIRST stage exits non-zero fails
# the whole pipeline — and `PRESENT_VER="$(runner_version_of "$BIN")"` is an
# assignment, so the script dies AT THAT LINE, with no message. "Empty when it
# cannot answer" is only true if this says so out loud.
#
# That is not a hypothetical shape: the binary is `[ -x ]` but unrunnable
# exactly when a ci-box's template preinstall landed a truncated or wrong-arch
# download — the one path this command family exists for. Without this, install
# exits 1 in silence and the refusal written for that case ("the download
# landed but cannot run") is unreachable code.
runner_version_of() {
"$1" --version 2>/dev/null | head -n1 \
| sed -nE 's/.*[Vv]ersion[[:space:]]+v?([0-9][0-9A-Za-z.+-]*).*/\1/p'
| sed -nE 's/.*[Vv]ersion[[:space:]]+v?([0-9][0-9A-Za-z.+-]*).*/\1/p' || true
}
# runner_download_decision <have-binary yes|no> <present-ver> <wanted-ver>

View file

@ -3389,13 +3389,28 @@ check "forgejo-runner: install routes through the shared checksum policy" 0 "fet
# binary at mint — so a bare presence check would make --version dead on the
# exact path this command exists for.
FRLIB="$ROOT/commands/lib/forgejo-runner-config.sh"
vparse() { bash -c ". '$FRLIB'; runner_version_of '$1'"; }
# Driven under the CALLER'S shell options, and that is half the test. Every
# forgejo-runner-* command is `set -euo pipefail`; a reader that only returns
# empty in a permissive shell has not kept its contract where it is used.
vparse() { bash -c "set -euo pipefail; . '$FRLIB'; runner_version_of \"\$1\"" _ "$1"; }
VSTUB="$(mktemp -d)"
printf '#!/bin/sh\necho "forgejo-runner version v12.13.2"\n' > "$VSTUB/fr"; chmod +x "$VSTUB/fr"
printf '#!/bin/sh\necho "garbage"\n' > "$VSTUB/bad"; chmod +x "$VSTUB/bad"
# `[ -x ]` yes, runnable no: a truncated or wrong-arch download, which is what a
# ci-box template preinstall leaves behind when it half-lands. Distinct from
# `bad` — that one EXITS 0 and merely says nothing parseable, so it never
# exercised the pipeline-failure path at all.
printf '#!/bin/sh\nexit 1\n' > "$VSTUB/dead"; chmod +x "$VSTUB/dead"
check "forgejo-runner: the version reader strips the leading v" 0 "12.13.2" vparse "$VSTUB/fr"
check "forgejo-runner: an unreadable version yields empty, not garbage" 0 "" vparse "$VSTUB/bad"
check "forgejo-runner: a binary that cannot RUN yields empty, not a silent set -e death" 0 "" \
vparse "$VSTUB/dead"
rm -rf "$VSTUB"
# The install site's own read of --version needs the same guard, or its refusal
# is unreachable: the assignment dies before the test below it can fire. Pinned
# by grep because reaching that line for real needs root and a downloaded binary.
check "forgejo-runner: the --version read cannot die ahead of its own refusal" 0 "|| true" \
grep -o 'head -n1 || true' "$FR"
# The decision itself, driven — this is the case review !110 caught, and a
# grep could not have caught it. Each row is a real lifecycle situation.