- applyCommands sit on ObjectEditCommand with a revert hook instead of five copies of the same preamble and undo - actionIf replaces six identical Reset/Apply menu blocks, addNameField the three name rows, assetLink the two asset links - requireStructuralObject replaces ten two-line preambles and drops the duplicate holder lookup - __gm.objectsOf had no caller; the SMB remnant in e2e.sh is gone Proven: check.sh 797 tests, full e2e 633 checks, smokes 24/19/20 against the same bundle. Co-Authored-By: Claude Fable 5.1 <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=game-maker-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
|