#!/usr/bin/env bash # One general waiter, so no agent hand-rolls a poll loop. Same contract as # `ygg-verify.sh wait`: one call blocks at most 270 s (WAIT_MAX can only lower # that), checks locally every 2 s (no model turn), and exits: # 0 done; prints a bounded tail (at most 60 lines) of the log when one is given # 99 still going; prints `RUNNING s (...)`: call it again # 98 the thing waited on is gone with no completion evidence # 3 the done regex is not a valid ERE (usage error, nothing was waited on) # 2 bad usage # Call it with a Bash tool `timeout` of at least 280000 ms. # # Forms: # wait-on.sh pid [] process gone: prints `GONE (no completion evidence)`, exit 0 # wait-on.sh log done when a log line matches the ERE # wait-on.sh file done when the file exists and is non-empty # `log` accepts WAIT_PID=: if that process dies and the regex never matched # (checked once more after the death), exit 98. Without WAIT_PID a log that does # not exist after 30 s is exit 98 (never started). `pid` cannot tell a finished # process from one that never existed or crashed: exit 0 there means only "gone", # never "succeeded". Prefer `log ` with WAIT_PID whenever the job # writes a done line. # # Log paths: every job gets a fresh log path, never one a previous job wrote. The # waiter greps the whole file, so an old done line reads as success at once. An # offset cannot fix it: truncating writers (`>`, Unity -logFile) rewrite below it. # # Known limitations (Soul pass 1, PR #7; not fixed): # F5 WAIT_PID naming a launcher that exits while its child keeps writing gives a # false 98; pass the pid that writes the log, or none. # F6 `file` is done at the first byte; a writer that is still writing is not # waited out. Have the job write the file last (rename into place). # F7 `pid ` prints a tail only; the `log` form greps the # whole log each 2 s, which is slow on multi-GB logs. # # PIDs: on Git Bash a Windows PID (PowerShell Start-Process .Id, a task manager # number) is seen by `tasklist`, but `kill -0` does not know it; Git Bash's own # `$!` is known to `kill -0` and also to `tasklist`'s WINPID only by luck. Alive # means either check says so. Linux uses `kill -0`. set -uo pipefail mode=${1:-}; cap=${WAIT_MAX:-270}; [ "$cap" -le 270 ] || cap=270 t0=$(date +%s) usage() { echo "usage: wait-on.sh pid [] | log | file " >&2; exit 2; } alive() { local p=$1 case "$p" in ''|*[!0-9]*) return 1 ;; esac kill -0 "$p" 2>/dev/null && return 0 case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) tasklist //FI "PID eq $p" //NH 2>/dev/null | tr -d '\r' | grep -qE "[[:space:]]$p[[:space:]]" && return 0 ;; esac return 1 } tail60() { [ -n "${1:-}" ] && [ -f "$1" ] && tail -n 60 "$1" | cut -c1-400; return 0; } running() { echo "RUNNING $(( $(date +%s) - t0 ))s ($1)"; exit 99; } elapsed() { echo $(( $(date +%s) - t0 )); } case "$mode" in pid) pid=${2:-}; log=${3:-}; [ -n "$pid" ] || usage while :; do if ! alive "$pid"; then echo "GONE (no completion evidence)"; tail60 "$log"; exit 0; fi [ "$(elapsed)" -lt "$cap" ] || running "pid $pid alive; last log line: $(tail -n 1 "$log" 2>/dev/null | cut -c1-160)" sleep 2 done ;; log) log=${2:-}; re=${3:-}; [ -n "$log" ] && [ -n "$re" ] || usage wp=${WAIT_PID:-} rc=0; printf '' | grep -Eq -- "$re" 2>/dev/null || rc=$? [ "$rc" -le 1 ] || { echo "wait-on: usage - /$re/ is not a valid extended regex" >&2; exit 3; } while :; do if [ -f "$log" ] && grep -Eq -- "$re" "$log" 2>/dev/null; then tail60 "$log"; exit 0; fi if [ -n "$wp" ] && ! alive "$wp"; then sleep 1 if [ -f "$log" ] && grep -Eq -- "$re" "$log" 2>/dev/null; then tail60 "$log"; exit 0; fi echo "wait-on: DEAD - pid $wp is gone and /$re/ never matched in $log"; tail60 "$log"; exit 98 fi if [ -z "$wp" ] && [ ! -f "$log" ] && [ "$(elapsed)" -ge 30 ]; then echo "wait-on: NOT STARTED - no $log after 30 s"; exit 98 fi [ "$(elapsed)" -lt "$cap" ] || running "no /$re/ yet; last line: $(tail -n 1 "$log" 2>/dev/null | cut -c1-160)" sleep 2 done ;; file) path=${2:-}; [ -n "$path" ] || usage while :; do [ -s "$path" ] && exit 0 [ "$(elapsed)" -lt "$cap" ] || running "$path not there yet" sleep 2 done ;; *) usage ;; esac