Stage 3 of the rename. The repo now lives at meleki/feuerwerk, the runners carry feuerwerk labels and the WSL distro on melektaus-atx is called feuerwerk-ci. The code has to follow, or CI finds no runner and the suite no longer picks the GPU path. - ci.yml: workflow name feuerwerk, jobs on feuerwerk-ci (gate, stein2) and feuerwerk-wsl (suite, melektaus-atx). - GAME_MAKER_* variables are now FEUERWERK_*. This includes FEUERWERK_BROWSER=wsl, which the runner config on melektaus-atx sets. - The video worker runs as container feuerwerk-video with the image feuerwerk-video and works on /srv/feuerwerk. - e2e.sh starts the unit feuerwerk-e2e, the CI image keeps its paths under the new name. - README and repo links point to git.nockal.com/meleki/feuerwerk. The old URLs still redirect with 301. Both runners keep their old labels until the first CI run with this ci.yml is through. Then they go, together with the image tag game-maker-ci:latest and GAME_MAKER_BROWSER in the atx config. Tests: - Gate on melektaus-atx through the renamed distro: CHECK EXIT 0, 307 files, 4756 tests. - e2e 60 on melektaus-atx: ALL PASSED, 20 checks. Renderer is the RTX 4070 through D3D12, so FEUERWERK_BROWSER reaches the GPU path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
109 lines
3.9 KiB
Bash
Executable File
109 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
||
# Runs the editor e2e suite in a transient systemd unit, which outlives the calling shell.
|
||
# A plain background job is not enough: when the caller exits, its whole process group is
|
||
# killed and the run dies mid-suite with no closing line.
|
||
#
|
||
# Usage: ./e2e.sh start [GM_STEPS] e.g. ./e2e.sh start 1-12
|
||
# ./e2e.sh status live progress, or the result of the last run
|
||
#
|
||
# Logs land in logs/e2e-<date>_<time>.log (gitignored) and end in "=== E2E EXIT n ===".
|
||
|
||
set -u
|
||
|
||
UNIT=feuerwerk-e2e
|
||
DIR=$(dirname "$(readlink -f "$0")")
|
||
LOGDIR=$DIR/logs
|
||
|
||
running() { systemctl is-active --quiet "$UNIT.service"; }
|
||
rel() { echo "${1#"$DIR"/}"; }
|
||
# Newest by name, not by mtime: the timestamped names sort chronologically, and the glob demands
|
||
# that shape, so a hand-dropped file in logs/ cannot outsort the newest run.
|
||
latest_log() { ls -1 "$LOGDIR"/e2e-[0-9]*.log 2>/dev/null | tail -1; }
|
||
|
||
start() {
|
||
if running; then
|
||
echo "$UNIT.service is still running – ./e2e.sh status, or systemctl stop $UNIT.service" >&2
|
||
exit 1
|
||
fi
|
||
systemctl reset-failed "$UNIT.service" 2>/dev/null
|
||
|
||
mkdir -p "$LOGDIR"
|
||
local log="$LOGDIR/e2e-$(date '+%Y-%m-%d_%H%M%S').log"
|
||
local steps=()
|
||
if [ $# -gt 0 ]; then steps=(--setenv=GM_STEPS="$1"); fi
|
||
|
||
systemd-run --unit="$UNIT" --working-directory="$DIR" \
|
||
--property=StandardOutput=append:"$log" \
|
||
--property=StandardError=append:"$log" \
|
||
"${steps[@]}" \
|
||
bash -c 'node tools/seed-e2e.mjs && node tools/e2e.mjs; echo "=== E2E EXIT $? ==="' || exit 1
|
||
|
||
echo "$UNIT.service started${1:+ with GM_STEPS=$1}, logging to $(rel "$log")"
|
||
echo " status: ./e2e.sh status"
|
||
echo " follow: tail -f $(rel "$log")"
|
||
echo " stop: systemctl stop $UNIT.service"
|
||
}
|
||
|
||
# Wall-clock seconds as "2m 14s".
|
||
span() { printf '%dm %02ds' $(($1 / 60)) $(($1 % 60)); }
|
||
|
||
status() {
|
||
local log pass fail
|
||
log=$(latest_log)
|
||
if [ -z "$log" ]; then
|
||
running && echo "running – but logs/ is empty, follow: journalctl -u $UNIT.service -f" ||
|
||
echo "idle – no run logged yet, start one with ./e2e.sh start"
|
||
return
|
||
fi
|
||
pass=$(grep -c '^PASS' "$log")
|
||
fail=$(grep -c '^FAIL' "$log")
|
||
|
||
if running; then
|
||
local since=0 now
|
||
now=$(systemctl show "$UNIT.service" -p ActiveEnterTimestamp --value)
|
||
[ -n "$now" ] && since=$(($(date +%s) - $(date -d "$now" +%s)))
|
||
echo "running – $(span "$since") elapsed"
|
||
echo " log: $(rel "$log")"
|
||
echo " checks: $pass PASS, $fail FAIL"
|
||
local at
|
||
at=$(grep -E '^(PASS|FAIL)' "$log" | tail -1)
|
||
[ -n "$at" ] && echo " at: $at"
|
||
echo " follow: tail -f $(rel "$log")"
|
||
return
|
||
fi
|
||
|
||
# Started per the file name, ended when the file was last written to; an unparsable name
|
||
# costs the duration, not the line.
|
||
local name started epoch took verdict exit_line
|
||
name=$(basename "$log" .log)
|
||
started=$(echo "${name#e2e-}" | tr '_' ' ' | sed -E 's/(..)(..)(..)$/\1:\2:\3/')
|
||
epoch=$(date -d "$started" +%s 2>/dev/null)
|
||
took=""
|
||
[ -n "$epoch" ] && took=$(($(date -r "$log" +%s) - epoch))
|
||
verdict=$(grep -E '^(ALL PASSED|[0-9]+ FAILED|RUN DIED)' "$log" | tail -1)
|
||
exit_line=$(grep -E '^=== E2E EXIT' "$log" | tail -1 | sed -E 's/^=== (.*) ===$/\1/')
|
||
|
||
echo "idle – last run $started$([ -n "$took" ] && [ "$took" -ge 0 ] && echo ", $(span "$took")")"
|
||
echo " log: $(rel "$log")"
|
||
if [ -z "$exit_line" ]; then
|
||
# No closing line means the run was cut short. Reporting its PASS count as a result would
|
||
# claim a suite that never finished, so say so instead.
|
||
echo " result: ABORTED – no closing line, so this is not a result. Run it again."
|
||
else
|
||
echo " result: ${verdict:-no summary} ($exit_line)"
|
||
fi
|
||
echo " checks: $pass PASS, $fail FAIL"
|
||
[ "$fail" -gt 0 ] && grep -E '^FAIL' "$log" | head -20 | sed 's/^/ /'
|
||
return 0
|
||
}
|
||
|
||
case "${1:-}" in
|
||
start) shift; start "$@" ;;
|
||
status) status ;;
|
||
*)
|
||
echo "usage: ./e2e.sh start [GM_STEPS] e.g. ./e2e.sh start 1-12" >&2
|
||
echo " ./e2e.sh status" >&2
|
||
exit 2
|
||
;;
|
||
esac
|