chore: import upstream snapshot with attribution
This commit is contained in:
+398
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fail fast on any error or undefined variable.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if command -v git >/dev/null 2>&1; then
|
||||
REPO_ROOT="$(git -C "${SCRIPT_DIR}" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
fi
|
||||
REPO_ROOT="${REPO_ROOT:-$(cd "${SCRIPT_DIR}/../../../.." && pwd)}"
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
LOG_DIR="$(mktemp -d "${TMPDIR:-/tmp}/code-change-verification.XXXXXX")"
|
||||
STATUS_PIPE="${LOG_DIR}/status.fifo"
|
||||
HEARTBEAT_INTERVAL_SECONDS="${CODE_CHANGE_VERIFICATION_HEARTBEAT_SECONDS:-10}"
|
||||
declare -a STEP_LAUNCHER=()
|
||||
declare -a STEP_PIDS=()
|
||||
declare -a STEP_NAMES=()
|
||||
declare -a STEP_LOGS=()
|
||||
declare -a STEP_STARTS=()
|
||||
RUNNING_STEPS=0
|
||||
EXIT_STATUS=0
|
||||
|
||||
resolve_executable_path() {
|
||||
local name="$1"
|
||||
type -P "${name}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
configure_step_launcher() {
|
||||
local perl_path=""
|
||||
local python_path=""
|
||||
local uv_path=""
|
||||
|
||||
perl_path="$(resolve_executable_path perl)"
|
||||
if [ -n "${perl_path}" ]; then
|
||||
STEP_LAUNCHER=("${perl_path}" -MPOSIX=setsid -e 'setsid() or die $!; exec @ARGV')
|
||||
return 0
|
||||
fi
|
||||
|
||||
python_path="$(resolve_executable_path python3)"
|
||||
if [ -z "${python_path}" ]; then
|
||||
python_path="$(resolve_executable_path python)"
|
||||
fi
|
||||
if [ -n "${python_path}" ]; then
|
||||
STEP_LAUNCHER=("${python_path}" -c 'import os, sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])')
|
||||
return 0
|
||||
fi
|
||||
|
||||
uv_path="$(resolve_executable_path uv)"
|
||||
if [ -n "${uv_path}" ]; then
|
||||
STEP_LAUNCHER=("${uv_path}" run --no-sync python -c 'import os, sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])')
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "code-change-verification: perl, python3, python, or uv is required to manage parallel step process groups." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
configure_step_launcher
|
||||
|
||||
mkfifo "${STATUS_PIPE}"
|
||||
exec 3<> "${STATUS_PIPE}"
|
||||
|
||||
cleanup() {
|
||||
local trap_status="$?"
|
||||
local status="${EXIT_STATUS}"
|
||||
|
||||
if [ "${status}" -eq 0 ]; then
|
||||
status="${trap_status}"
|
||||
fi
|
||||
|
||||
if [ "${#STEP_PIDS[@]}" -gt 0 ]; then
|
||||
stop_running_steps
|
||||
fi
|
||||
|
||||
exec 3>&- 3<&- || true
|
||||
rm -rf "${LOG_DIR}"
|
||||
exit "${status}"
|
||||
}
|
||||
|
||||
on_interrupt() {
|
||||
EXIT_STATUS=130
|
||||
exit 130
|
||||
}
|
||||
|
||||
on_terminate() {
|
||||
EXIT_STATUS=143
|
||||
exit 143
|
||||
}
|
||||
|
||||
stop_running_steps() {
|
||||
local pid=""
|
||||
|
||||
if [ "${#STEP_PIDS[@]}" -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
for pid in "${STEP_PIDS[@]}"; do
|
||||
if [ -n "${pid}" ]; then
|
||||
kill -TERM -- "-${pid}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
sleep 1
|
||||
|
||||
for pid in "${STEP_PIDS[@]}"; do
|
||||
if [ -n "${pid}" ]; then
|
||||
# A process group can remain alive after its leader exits, so escalate by group id unconditionally.
|
||||
kill -KILL -- "-${pid}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
for pid in "${STEP_PIDS[@]}"; do
|
||||
if [ -n "${pid}" ]; then
|
||||
wait "${pid}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
STEP_PIDS=()
|
||||
STEP_NAMES=()
|
||||
STEP_LOGS=()
|
||||
STEP_STARTS=()
|
||||
RUNNING_STEPS=0
|
||||
}
|
||||
|
||||
find_step_index() {
|
||||
local target_name="$1"
|
||||
local idx=""
|
||||
|
||||
for idx in "${!STEP_NAMES[@]}"; do
|
||||
if [ "${STEP_NAMES[$idx]}" = "${target_name}" ]; then
|
||||
echo "${idx}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
clear_step() {
|
||||
local idx="$1"
|
||||
|
||||
STEP_PIDS[$idx]=""
|
||||
STEP_NAMES[$idx]=""
|
||||
STEP_LOGS[$idx]=""
|
||||
STEP_STARTS[$idx]=""
|
||||
RUNNING_STEPS=$((RUNNING_STEPS - 1))
|
||||
}
|
||||
|
||||
step_pid_is_alive() {
|
||||
local pid="$1"
|
||||
local state=""
|
||||
|
||||
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
state="$(ps -o stat= -p "${pid}" 2>/dev/null | tr -d '[:space:]')"
|
||||
case "${state}" in
|
||||
Z*|z*|"")
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
print_heartbeat() {
|
||||
local now
|
||||
local idx=""
|
||||
local name=""
|
||||
local start_time=""
|
||||
local elapsed=""
|
||||
local running=""
|
||||
|
||||
now=$(date +%s)
|
||||
|
||||
for idx in "${!STEP_NAMES[@]}"; do
|
||||
name="${STEP_NAMES[$idx]}"
|
||||
start_time="${STEP_STARTS[$idx]}"
|
||||
|
||||
if [ -z "${name}" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
elapsed=$((now - start_time))
|
||||
if [ -n "${running}" ]; then
|
||||
running="${running}, "
|
||||
fi
|
||||
running="${running}${name} (${elapsed}s)"
|
||||
done
|
||||
|
||||
if [ -n "${running}" ]; then
|
||||
echo "code-change-verification: still running: ${running}."
|
||||
fi
|
||||
}
|
||||
|
||||
start_step() {
|
||||
local name="$1"
|
||||
shift
|
||||
local log_file="${LOG_DIR}/${name}.log"
|
||||
|
||||
echo "Running make ${name}..."
|
||||
: > "${log_file}"
|
||||
# Start each step in its own process group so fail-fast cleanup can stop pytest worker trees too.
|
||||
"${STEP_LAUNCHER[@]}" \
|
||||
bash -c '
|
||||
step_name="$1"
|
||||
log_file="$2"
|
||||
status_pipe="$3"
|
||||
shift 3
|
||||
|
||||
if "$@" >"$log_file" 2>&1; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
|
||||
printf "%s\t%s\n" "$step_name" "$status" >"$status_pipe"
|
||||
exit "$status"
|
||||
' \
|
||||
bash "${name}" "${log_file}" "${STATUS_PIPE}" "$@" &
|
||||
|
||||
STEP_PIDS+=("$!")
|
||||
STEP_NAMES+=("${name}")
|
||||
STEP_LOGS+=("${log_file}")
|
||||
STEP_STARTS+=("$(date +%s)")
|
||||
RUNNING_STEPS=$((RUNNING_STEPS + 1))
|
||||
}
|
||||
|
||||
finish_step() {
|
||||
local name="$1"
|
||||
local status="$2"
|
||||
local idx=""
|
||||
local pid=""
|
||||
local log_file=""
|
||||
local start_time=""
|
||||
local now
|
||||
|
||||
idx="$(find_step_index "${name}")"
|
||||
pid="${STEP_PIDS[$idx]}"
|
||||
log_file="${STEP_LOGS[$idx]}"
|
||||
start_time="${STEP_STARTS[$idx]}"
|
||||
|
||||
now=$(date +%s)
|
||||
wait "${pid}" 2>/dev/null || true
|
||||
|
||||
if [ "${status}" -eq 0 ]; then
|
||||
clear_step "${idx}"
|
||||
echo "make ${name} passed in $((now - start_time))s."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "code-change-verification: make ${name} failed with exit code ${status} after $((now - start_time))s." >&2
|
||||
echo "--- ${name} log (last 80 lines) ---" >&2
|
||||
tail -n 80 "${log_file}" >&2 || true
|
||||
stop_running_steps
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
check_for_missing_reporters() {
|
||||
local idx=""
|
||||
local pid=""
|
||||
local name=""
|
||||
local log_file=""
|
||||
local start_time=""
|
||||
local now
|
||||
local step_status=0
|
||||
|
||||
for idx in "${!STEP_PIDS[@]}"; do
|
||||
pid="${STEP_PIDS[$idx]}"
|
||||
if [ -z "${pid}" ] || step_pid_is_alive "${pid}"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if try_finish_step_from_status_pipe 1; then
|
||||
if [ "${STATUS_PIPE_DRAINED}" -eq 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
step_status=$?
|
||||
return "${step_status}"
|
||||
fi
|
||||
|
||||
name="${STEP_NAMES[$idx]}"
|
||||
log_file="${STEP_LOGS[$idx]}"
|
||||
start_time="${STEP_STARTS[$idx]}"
|
||||
now=$(date +%s)
|
||||
set +e
|
||||
wait "${pid}" 2>/dev/null
|
||||
step_status=$?
|
||||
set -e
|
||||
|
||||
if [ "${step_status}" -eq 0 ]; then
|
||||
finish_step "${name}" 0
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "code-change-verification: make ${name} exited before reporting completion status after $((now - start_time))s." >&2
|
||||
echo "--- ${name} log (last 80 lines) ---" >&2
|
||||
tail -n 80 "${log_file}" >&2 || true
|
||||
stop_running_steps
|
||||
return "${step_status}"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
STATUS_PIPE_DRAINED=0
|
||||
|
||||
try_finish_step_from_status_pipe() {
|
||||
local timeout="$1"
|
||||
local name=""
|
||||
local status=""
|
||||
local step_status=0
|
||||
|
||||
STATUS_PIPE_DRAINED=0
|
||||
if ! IFS=$'\t' read -r -t "${timeout}" name status <&3; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
STATUS_PIPE_DRAINED=1
|
||||
finish_step "${name}" "${status}"
|
||||
step_status=$?
|
||||
if [ "${step_status}" -ne 0 ]; then
|
||||
return "${step_status}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_parallel_steps() {
|
||||
local name=""
|
||||
local status=""
|
||||
local step_status=""
|
||||
local next_heartbeat_at
|
||||
local now
|
||||
|
||||
next_heartbeat_at=$(( $(date +%s) + HEARTBEAT_INTERVAL_SECONDS ))
|
||||
|
||||
while [ "${RUNNING_STEPS}" -gt 0 ]; do
|
||||
if try_finish_step_from_status_pipe 1; then
|
||||
if [ "${STATUS_PIPE_DRAINED}" -eq 1 ]; then
|
||||
continue
|
||||
fi
|
||||
else
|
||||
step_status=$?
|
||||
if [ "${step_status}" -ne 0 ]; then
|
||||
return "${step_status}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
check_for_missing_reporters
|
||||
step_status=$?
|
||||
if [ "${step_status}" -ne 0 ]; then
|
||||
return "${step_status}"
|
||||
fi
|
||||
|
||||
now=$(date +%s)
|
||||
if [ "${now}" -ge "${next_heartbeat_at}" ]; then
|
||||
print_heartbeat
|
||||
next_heartbeat_at=$((now + HEARTBEAT_INTERVAL_SECONDS))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap on_interrupt INT
|
||||
trap on_terminate TERM
|
||||
|
||||
echo "Running make format..."
|
||||
set +e
|
||||
make format
|
||||
EXIT_STATUS=$?
|
||||
set -e
|
||||
|
||||
if [ "${EXIT_STATUS}" -ne 0 ]; then
|
||||
exit "${EXIT_STATUS}"
|
||||
fi
|
||||
|
||||
echo "Running make lint, make typecheck, and make tests in parallel..."
|
||||
start_step "lint" make lint
|
||||
start_step "typecheck" make typecheck
|
||||
start_step "tests" make tests
|
||||
set +e
|
||||
wait_for_parallel_steps
|
||||
EXIT_STATUS=$?
|
||||
set -e
|
||||
|
||||
if [ "${EXIT_STATUS}" -ne 0 ]; then
|
||||
exit "${EXIT_STATUS}"
|
||||
fi
|
||||
|
||||
trap - EXIT INT TERM
|
||||
exec 3>&- 3<&-
|
||||
rm -rf "${LOG_DIR}"
|
||||
echo "code-change-verification: all commands passed."
|
||||
Reference in New Issue
Block a user