396 lines
12 KiB
Bash
396 lines
12 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
readonly unavailable_status=86
|
||
|
|
|
||
|
|
rust_changed="${1:-true}"
|
||
|
|
playwright_shards="${PLAYWRIGHT_SHARDS:-8}"
|
||
|
|
playwright_concurrency="${PLAYWRIGHT_CONCURRENCY:-4}"
|
||
|
|
playwright_shared_server="${PLAYWRIGHT_SHARED_SERVER:-1}"
|
||
|
|
vitest_coverage_max_workers="${VITEST_COVERAGE_MAX_WORKERS:-${SIDECAR_VITEST_MAX_WORKERS:-2}}"
|
||
|
|
frontend_coverage_shards="${FRONTEND_COVERAGE_SHARDS:-${SIDECAR_FRONTEND_COVERAGE_SHARDS:-2}}"
|
||
|
|
cargo_build_jobs="${CARGO_BUILD_JOBS:-2}"
|
||
|
|
timeout_seconds="${SIDECAR_GATE_TIMEOUT:-1800}"
|
||
|
|
poll_interval="${SIDECAR_GATE_POLL_INTERVAL:-20}"
|
||
|
|
launch_timeout="${SIDECAR_GATE_LAUNCH_TIMEOUT:-45}"
|
||
|
|
launch_attempts="${SIDECAR_GATE_LAUNCH_ATTEMPTS:-3}"
|
||
|
|
remote_workdir="${SIDECAR_REMOTE_WORKDIR:-/home/user/tolaria}"
|
||
|
|
sidecar_prefix="${SIDECAR_NAME_PREFIX:-tolaria-hooks}"
|
||
|
|
frontend_name="${SIDECAR_FRONTEND_NAME:-${sidecar_prefix}-frontend-2}"
|
||
|
|
rust_name="${SIDECAR_RUST_NAME:-${sidecar_prefix}-rust}"
|
||
|
|
playwright_name="${SIDECAR_PLAYWRIGHT_NAME:-${sidecar_prefix}-playwright}"
|
||
|
|
state_dir="$(mktemp -d "${TMPDIR:-/tmp}/tolaria-sidecar-lanes.XXXXXX")"
|
||
|
|
lanes_file="${state_dir}/lanes.tsv"
|
||
|
|
|
||
|
|
: > "$lanes_file"
|
||
|
|
|
||
|
|
cleanup_state() {
|
||
|
|
rm -rf "$state_dir"
|
||
|
|
}
|
||
|
|
|
||
|
|
trap cleanup_state EXIT
|
||
|
|
|
||
|
|
resolve_chunk_bin() {
|
||
|
|
if [[ -n "${CHUNK_BIN:-}" && -x "$CHUNK_BIN" ]]; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
if command -v chunk >/dev/null 2>&1; then
|
||
|
|
CHUNK_BIN="$(command -v chunk)"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ -x "$HOME/.local/bin/chunk" ]]; then
|
||
|
|
CHUNK_BIN="$HOME/.local/bin/chunk"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
load_org_id() {
|
||
|
|
if [[ -n "${CIRCLECI_ORG_ID:-}" ]]; then
|
||
|
|
printf '%s\n' "$CIRCLECI_ORG_ID"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
node -e "const fs=require('node:fs'); const c=JSON.parse(fs.readFileSync('.chunk/config.json','utf8')); if (c.orgID) process.stdout.write(c.orgID)"
|
||
|
|
}
|
||
|
|
|
||
|
|
sidecar_id_by_name() {
|
||
|
|
local name="$1"
|
||
|
|
|
||
|
|
"$CHUNK_BIN" sidecar list 2>/dev/null | awk -v name="$name" '$1 == name { print $2; found=1; exit } END { exit found ? 0 : 1 }'
|
||
|
|
}
|
||
|
|
|
||
|
|
create_sidecar_from_snapshot() {
|
||
|
|
local name="$1"
|
||
|
|
|
||
|
|
if [[ -z "${SIDECAR_SNAPSHOT_ID:-}" ]]; then
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo " -> Creating ${name} from snapshot ${SIDECAR_SNAPSHOT_ID}" >&2
|
||
|
|
"$CHUNK_BIN" sidecar create --name "$name" --org-id "$org_id" --image "$SIDECAR_SNAPSHOT_ID" >/dev/null
|
||
|
|
}
|
||
|
|
|
||
|
|
ensure_sidecar() {
|
||
|
|
local name="$1"
|
||
|
|
local explicit_id="${2:-}"
|
||
|
|
local id
|
||
|
|
|
||
|
|
if [[ -n "$explicit_id" ]]; then
|
||
|
|
printf '%s\n' "$explicit_id"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
if id="$(sidecar_id_by_name "$name")"; then
|
||
|
|
printf '%s\n' "$id"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo " -> Sidecar ${name} not found; provisioning it..." >&2
|
||
|
|
if ! create_sidecar_from_snapshot "$name"; then
|
||
|
|
"$CHUNK_BIN" sidecar setup --name "$name" --org-id "$org_id" >/dev/null
|
||
|
|
fi
|
||
|
|
|
||
|
|
sidecar_id_by_name "$name"
|
||
|
|
}
|
||
|
|
|
||
|
|
sync_lane() {
|
||
|
|
local lane="$1"
|
||
|
|
local sidecar_id="$2"
|
||
|
|
local log_file="${state_dir}/sync-${lane}.log"
|
||
|
|
|
||
|
|
{
|
||
|
|
echo "[sidecar-sync] ${lane}: syncing to ${sidecar_id}"
|
||
|
|
if "$CHUNK_BIN" sidecar sync --sidecar-id "$sidecar_id" --workdir "$remote_workdir"; then
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "[sidecar-sync] ${lane}: sync failed, rerunning setup once"
|
||
|
|
"$CHUNK_BIN" sidecar setup --sidecar-id "$sidecar_id" --dir .
|
||
|
|
"$CHUNK_BIN" sidecar sync --sidecar-id "$sidecar_id" --workdir "$remote_workdir"
|
||
|
|
} >"$log_file" 2>&1
|
||
|
|
}
|
||
|
|
|
||
|
|
sync_all_lanes() {
|
||
|
|
local failures=0
|
||
|
|
local frontend_pid rust_pid playwright_pid
|
||
|
|
|
||
|
|
sync_lane frontend "$frontend_id" &
|
||
|
|
frontend_pid=$!
|
||
|
|
sync_lane rust "$rust_id" &
|
||
|
|
rust_pid=$!
|
||
|
|
sync_lane playwright "$playwright_id" &
|
||
|
|
playwright_pid=$!
|
||
|
|
|
||
|
|
for lane_pid in "$frontend_pid" "$rust_pid" "$playwright_pid"; do
|
||
|
|
if ! wait "$lane_pid"; then
|
||
|
|
failures=1
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
if [[ "$failures" != "0" ]]; then
|
||
|
|
echo "Chunk sidecar sync failed"
|
||
|
|
sed 's/^/ /' "${state_dir}"/sync-*.log 2>/dev/null || true
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
record_lane() {
|
||
|
|
local lane="$1"
|
||
|
|
local sidecar_id="$2"
|
||
|
|
local status_file="$3"
|
||
|
|
local log_file="$4"
|
||
|
|
local pid_file="$5"
|
||
|
|
|
||
|
|
printf '%s\t%s\t%s\t%s\t%s\n' "$lane" "$sidecar_id" "$status_file" "$log_file" "$pid_file" >> "$lanes_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
launch_lane() {
|
||
|
|
local lane="$1"
|
||
|
|
local sidecar_id="$2"
|
||
|
|
local run_id="tolaria-${lane}-$(date +%s)-$$"
|
||
|
|
local remote_status="/tmp/${run_id}.status"
|
||
|
|
local remote_log="/tmp/${run_id}.log"
|
||
|
|
local remote_pid="/tmp/${run_id}.pid"
|
||
|
|
local remote_launcher="/tmp/${run_id}.launcher.log"
|
||
|
|
local launch_output_file="${state_dir}/launch-${lane}.log"
|
||
|
|
local launch_attempt=1
|
||
|
|
|
||
|
|
while [[ "$launch_attempt" -le "$launch_attempts" ]]; do
|
||
|
|
local launch_status=0
|
||
|
|
local launch_pid
|
||
|
|
local launch_elapsed=0
|
||
|
|
|
||
|
|
echo " -> Launching ${lane} on ${sidecar_id} (attempt ${launch_attempt}/${launch_attempts})"
|
||
|
|
"$CHUNK_BIN" sidecar exec \
|
||
|
|
--sidecar-id "$sidecar_id" \
|
||
|
|
--command bash \
|
||
|
|
--args -lc \
|
||
|
|
--args "cd '$remote_workdir' && export RUST_CHANGED='$rust_changed' PLAYWRIGHT_SHARDS='$playwright_shards' PLAYWRIGHT_CONCURRENCY='$playwright_concurrency' PLAYWRIGHT_SHARED_SERVER='$playwright_shared_server' VITEST_COVERAGE_MAX_WORKERS='$vitest_coverage_max_workers' FRONTEND_COVERAGE_SHARDS='$frontend_coverage_shards' CARGO_BUILD_JOBS='$cargo_build_jobs' && rm -f '$remote_status' '$remote_log' '$remote_pid' '$remote_launcher' && nohup setsid -f bash -lc 'bash .chunk/run-sidecar-lane.sh $lane >\"$remote_log\" 2>&1; printf \"%s\\n\" \"\$?\" >\"$remote_status\"' </dev/null >'$remote_launcher' 2>&1" \
|
||
|
|
>"$launch_output_file" 2>&1 &
|
||
|
|
launch_pid=$!
|
||
|
|
|
||
|
|
while kill -0 "$launch_pid" 2>/dev/null; do
|
||
|
|
if [[ "$launch_elapsed" -ge "$launch_timeout" ]]; then
|
||
|
|
launch_status=124
|
||
|
|
kill "$launch_pid" 2>/dev/null || true
|
||
|
|
sleep 1
|
||
|
|
kill -KILL "$launch_pid" 2>/dev/null || true
|
||
|
|
wait "$launch_pid" 2>/dev/null || true
|
||
|
|
break
|
||
|
|
fi
|
||
|
|
sleep 1
|
||
|
|
launch_elapsed=$((launch_elapsed + 1))
|
||
|
|
done
|
||
|
|
|
||
|
|
if [[ "$launch_status" == "0" ]]; then
|
||
|
|
wait "$launch_pid" || launch_status=$?
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ "$launch_status" == "0" ]]; then
|
||
|
|
record_lane "$lane" "$sidecar_id" "$remote_status" "$remote_log" "$remote_pid"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
cat "$launch_output_file"
|
||
|
|
if launch_state="$(poll_lane_status "$sidecar_id" "$remote_status" "$remote_log" "$remote_pid" 2>/dev/null)"; then
|
||
|
|
launch_state="$(printf '%s\n' "$launch_state" | tail -1 | tr -d '[:space:]')"
|
||
|
|
if [[ "$launch_state" == "running" || "$launch_state" == "0" || "$launch_state" =~ ^[1-9][0-9]*$ ]]; then
|
||
|
|
echo " -> ${lane} appears to have started despite launch status ${launch_status}"
|
||
|
|
record_lane "$lane" "$sidecar_id" "$remote_status" "$remote_log" "$remote_pid"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
echo "Chunk sidecar launch failed for ${lane} with status ${launch_status}"
|
||
|
|
if [[ "$launch_attempt" -lt "$launch_attempts" ]]; then
|
||
|
|
sleep 5
|
||
|
|
fi
|
||
|
|
launch_attempt=$((launch_attempt + 1))
|
||
|
|
done
|
||
|
|
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
fetch_remote_log_tail() {
|
||
|
|
local lane="$1"
|
||
|
|
local sidecar_id="$2"
|
||
|
|
local log_file="$3"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "----- ${lane} log tail -----"
|
||
|
|
"$CHUNK_BIN" sidecar exec \
|
||
|
|
--sidecar-id "$sidecar_id" \
|
||
|
|
--command bash \
|
||
|
|
--args -lc \
|
||
|
|
--args "tail -180 '$log_file' 2>/dev/null || true" 2>&1 || true
|
||
|
|
}
|
||
|
|
|
||
|
|
stop_lane() {
|
||
|
|
local sidecar_id="$1"
|
||
|
|
local pid_file="$2"
|
||
|
|
local log_file="$3"
|
||
|
|
|
||
|
|
"$CHUNK_BIN" sidecar exec \
|
||
|
|
--sidecar-id "$sidecar_id" \
|
||
|
|
--command bash \
|
||
|
|
--args -lc \
|
||
|
|
--args "if [ -s '$pid_file' ]; then pid=\$(cat '$pid_file'); kill -TERM -\"\$pid\" 2>/dev/null || kill -TERM \"\$pid\" 2>/dev/null || true; sleep 3; kill -KILL -\"\$pid\" 2>/dev/null || kill -KILL \"\$pid\" 2>/dev/null || true; fi; ps -eo pid=,command= | awk -v target='$log_file' '\$0 ~ target && \$0 !~ /awk/ { print \$1 }' | xargs -r kill -TERM 2>/dev/null || true" \
|
||
|
|
>/dev/null 2>&1 || true
|
||
|
|
}
|
||
|
|
|
||
|
|
stop_all_lanes() {
|
||
|
|
while IFS=$'\t' read -r _lane sidecar_id _status_file log_file pid_file; do
|
||
|
|
stop_lane "$sidecar_id" "$pid_file" "$log_file"
|
||
|
|
done < "$lanes_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
poll_lane_status() {
|
||
|
|
local sidecar_id="$1"
|
||
|
|
local status_file="$2"
|
||
|
|
local log_file="$3"
|
||
|
|
local pid_file="$4"
|
||
|
|
|
||
|
|
"$CHUNK_BIN" sidecar exec \
|
||
|
|
--sidecar-id "$sidecar_id" \
|
||
|
|
--command bash \
|
||
|
|
--args -lc \
|
||
|
|
--args "if [ -f '$status_file' ]; then cat '$status_file'; elif test -s '$pid_file' && kill -0 \"\$(cat '$pid_file')\" 2>/dev/null; then echo running; elif ps -eo pid=,command= | awk -v target='$log_file' '\$0 ~ target && \$0 !~ /awk/ { found=1 } END { exit found ? 0 : 1 }'; then echo running; else echo missing; fi" 2>&1
|
||
|
|
}
|
||
|
|
|
||
|
|
poll_lanes() {
|
||
|
|
local started_at="$1"
|
||
|
|
local last_heartbeat=0
|
||
|
|
local completed_file="${state_dir}/completed"
|
||
|
|
local failed=0
|
||
|
|
|
||
|
|
: > "$completed_file"
|
||
|
|
|
||
|
|
while true; do
|
||
|
|
local all_done=1
|
||
|
|
|
||
|
|
while IFS=$'\t' read -r lane sidecar_id status_file log_file pid_file; do
|
||
|
|
if grep -qx "$lane" "$completed_file"; then
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
local poll_status=0
|
||
|
|
local poll_output
|
||
|
|
local status
|
||
|
|
|
||
|
|
poll_output="$(poll_lane_status "$sidecar_id" "$status_file" "$log_file" "$pid_file")" || poll_status=$?
|
||
|
|
if [[ "$poll_status" != "0" ]]; then
|
||
|
|
echo " -> ${lane} status poll failed: $(printf '%s\n' "$poll_output" | head -1)"
|
||
|
|
all_done=0
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
status="$(printf '%s\n' "$poll_output" | tail -1 | tr -d '[:space:]')"
|
||
|
|
case "$status" in
|
||
|
|
0)
|
||
|
|
echo "$lane" >> "$completed_file"
|
||
|
|
echo " -> ${lane} passed"
|
||
|
|
fetch_remote_log_tail "$lane" "$sidecar_id" "$log_file"
|
||
|
|
;;
|
||
|
|
running|"")
|
||
|
|
all_done=0
|
||
|
|
;;
|
||
|
|
missing)
|
||
|
|
echo " -> ${lane} stopped before writing a status file"
|
||
|
|
fetch_remote_log_tail "$lane" "$sidecar_id" "$log_file"
|
||
|
|
failed=1
|
||
|
|
echo "$lane" >> "$completed_file"
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo " -> ${lane} failed with status ${status}"
|
||
|
|
fetch_remote_log_tail "$lane" "$sidecar_id" "$log_file"
|
||
|
|
failed=1
|
||
|
|
echo "$lane" >> "$completed_file"
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done < "$lanes_file"
|
||
|
|
|
||
|
|
if [[ "$failed" != "0" ]]; then
|
||
|
|
stop_all_lanes
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ "$all_done" == "1" ]]; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
local elapsed
|
||
|
|
elapsed=$(($(date +%s) - started_at))
|
||
|
|
if [[ "$elapsed" -ge "$timeout_seconds" ]]; then
|
||
|
|
echo "Chunk sidecar lanes timed out after ${elapsed}s"
|
||
|
|
stop_all_lanes
|
||
|
|
return 124
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ $((elapsed - last_heartbeat)) -ge 60 ]]; then
|
||
|
|
echo " -> Sidecar lanes still running (${elapsed}s elapsed)"
|
||
|
|
last_heartbeat="$elapsed"
|
||
|
|
fi
|
||
|
|
|
||
|
|
sleep "$poll_interval"
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
if ! resolve_chunk_bin; then
|
||
|
|
echo "Chunk CLI not found"
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
|
||
|
|
org_id="$(load_org_id)"
|
||
|
|
if [[ -z "$org_id" ]]; then
|
||
|
|
echo "Chunk org id not found"
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Chunk sidecar lanes:"
|
||
|
|
echo " frontend: ${frontend_name}"
|
||
|
|
echo " rust: ${rust_name}"
|
||
|
|
echo " playwright: ${playwright_name}"
|
||
|
|
if [[ -n "${SIDECAR_FRONTEND_ID:-}${SIDECAR_RUST_ID:-}${SIDECAR_PLAYWRIGHT_ID:-}" ]]; then
|
||
|
|
echo " explicit sidecar IDs are set for one or more lanes"
|
||
|
|
fi
|
||
|
|
echo " vitest workers=${vitest_coverage_max_workers}; frontend coverage shards=${frontend_coverage_shards}; playwright shards=${playwright_shards}; concurrency=${playwright_concurrency}; cargo jobs=${cargo_build_jobs}"
|
||
|
|
|
||
|
|
if ! frontend_id="$(ensure_sidecar "$frontend_name" "${SIDECAR_FRONTEND_ID:-}")"; then
|
||
|
|
echo "Chunk frontend sidecar unavailable"
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
if ! rust_id="$(ensure_sidecar "$rust_name" "${SIDECAR_RUST_ID:-}")"; then
|
||
|
|
echo "Chunk rust sidecar unavailable"
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
if ! playwright_id="$(ensure_sidecar "$playwright_name" "${SIDECAR_PLAYWRIGHT_ID:-}")"; then
|
||
|
|
echo "Chunk Playwright sidecar unavailable"
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Syncing worktree to sidecar lanes..."
|
||
|
|
if ! sync_all_lanes; then
|
||
|
|
exit "$unavailable_status"
|
||
|
|
fi
|
||
|
|
|
||
|
|
started_at=$(date +%s)
|
||
|
|
echo "Launching sidecar lanes..."
|
||
|
|
launch_lane frontend "$frontend_id"
|
||
|
|
if [[ "$rust_changed" == "true" ]]; then
|
||
|
|
launch_lane rust "$rust_id"
|
||
|
|
else
|
||
|
|
echo " -> Rust lane skipped because RUST_CHANGED=false"
|
||
|
|
fi
|
||
|
|
launch_lane playwright "$playwright_id"
|
||
|
|
|
||
|
|
if ! poll_lanes "$started_at"; then
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
elapsed=$(($(date +%s) - started_at))
|
||
|
|
echo "Chunk sidecar lanes passed in ${elapsed}s"
|
||
|
|
exit 0
|