Files
browser/scripts/local-debug/start.sh
T

356 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# shellcheck source=scripts/local-debug/env.sh
source "$ROOT_DIR/scripts/local-debug/env.sh"
mkdir -p "$LOCAL_DEBUG_LOG_DIR" "$LOCAL_DEBUG_PID_DIR" "$PLATFORM_DATA_DIR" "$PLATFORM_LOG_DIR" "$RUN_WORKSPACE_ROOT" "$RUN_SPOOL_ROOT" "$RUN_BUILD_BUCKET_ROOT"
mkdir -p "$GOCACHE"
local_debug_prepare_run_lifecycle_templates
local_debug_prepare_distribution_builder
managed_pid_running() {
local pid_file="$1"
if [[ ! -f "$pid_file" ]]; then
return 1
fi
local pid
pid="$(cat "$pid_file")"
[[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null
}
pid_is_descendant_of() {
local pid="$1"
local ancestor="$2"
local parent
while [[ "$pid" =~ ^[0-9]+$ && "$pid" -gt 1 ]]; do
if [[ "$pid" == "$ancestor" ]]; then
return 0
fi
parent="$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d '[:space:]')"
[[ -n "$parent" && "$parent" != "$pid" ]] || return 1
pid="$parent"
done
return 1
}
port_listener_pid() {
local port="$1"
lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true
}
listener_owned_by_local_debug() {
local pid="$1"
local details
details="$(lsof -nP -p "$pid" 2>/dev/null || true)"
[[ "$details" == *"$ROOT_DIR"* || "$details" == *"$LOCAL_DEBUG_ROOT"* ]]
}
wait_for_pid_exit() {
local pid="$1"
for _ in $(seq 1 20); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.25
done
return 1
}
hash_stream() {
shasum -a 256 | awk '{print $1}'
}
tracked_tree_fingerprint() {
local repo_dir="$1"
shift
(
cd "$repo_dir"
{
git ls-files -z -- "$@"
git ls-files -z --others --exclude-standard -- "$@"
} | sort -z | while IFS= read -r -d '' file; do
[[ -f "$file" ]] || continue
shasum -a 256 "$file"
done
git diff --binary -- "$@"
) | hash_stream
}
file_fingerprint() {
local file="$1"
if [[ ! -f "$file" ]]; then
printf 'missing:%s\n' "$file" | hash_stream
return 0
fi
shasum -a 256 "$file" | awk '{print $1}'
}
combined_fingerprint() {
printf '%s\n' "$@" | hash_stream
}
descendant_pids() {
local pid="$1"
local child
while IFS= read -r child; do
[[ -n "$child" ]] || continue
printf '%s\n' "$child"
descendant_pids "$child"
done < <(pgrep -P "$pid" 2>/dev/null || true)
}
stop_pid_tree() {
local name="$1"
local root_pid="$2"
local pids
local pid
local forced=0
[[ "$root_pid" =~ ^[0-9]+$ ]] || return 0
pids="$(descendant_pids "$root_pid"; printf '%s\n' "$root_pid")"
for pid in $pids; do
kill "$pid" 2>/dev/null || true
done
for pid in $pids; do
if ! wait_for_pid_exit "$pid"; then
forced=1
fi
done
if [[ "$forced" -eq 1 ]]; then
printf '%s process tree rooted at pid %s did not stop after SIGTERM; sending SIGKILL\n' "$name" "$root_pid"
for pid in $pids; do
kill -9 "$pid" 2>/dev/null || true
done
for pid in $pids; do
wait_for_pid_exit "$pid" || true
done
fi
}
stop_orphaned_run_bootstrap() {
local pid_file="$LOCAL_DEBUG_PID_DIR/run.pid"
local pid
if managed_pid_running "$pid_file"; then
return 0
fi
while IFS= read -r pid; do
[[ "$pid" =~ ^[0-9]+$ ]] || continue
[[ "$pid" != "$$" ]] || continue
if kill -0 "$pid" 2>/dev/null; then
printf 'stopping stale run bootstrap pid %s before start\n' "$pid"
stop_pid_tree "run bootstrap" "$pid"
fi
done < <(pgrep -f "$RUN_BOOTSTRAP_BIN" 2>/dev/null || true)
}
ensure_port_available() {
local name="$1"
local port="$2"
local pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid"
local listener_pid
listener_pid="$(port_listener_pid "$port")"
if [[ -z "$listener_pid" ]]; then
return 0
fi
if managed_pid_running "$pid_file"; then
local managed_pid
managed_pid="$(cat "$pid_file")"
if pid_is_descendant_of "$listener_pid" "$managed_pid"; then
return 0
fi
fi
if listener_owned_by_local_debug "$listener_pid"; then
printf 'stopping stale %s listener pid %s on port %s\n' "$name" "$listener_pid" "$port"
kill "$listener_pid" 2>/dev/null || true
if ! wait_for_pid_exit "$listener_pid"; then
printf 'stale %s listener pid %s did not stop after SIGTERM; sending SIGKILL\n' "$name" "$listener_pid"
kill -9 "$listener_pid" 2>/dev/null || true
wait_for_pid_exit "$listener_pid" || true
fi
rm -f "$pid_file"
return 0
fi
printf '%s port %s is already used by pid %s; choose another LOCAL_DEBUG_*_PORT or stop the existing listener\n' "$name" "$port" "$listener_pid" >&2
return 1
}
start_service() {
local name="$1"
local service_dir="$2"
local fingerprint="$3"
local pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid"
local fingerprint_file="$LOCAL_DEBUG_PID_DIR/$name.fingerprint"
shift 3
if [[ -f "$pid_file" ]]; then
local existing_pid
existing_pid="$(cat "$pid_file")"
if kill -0 "$existing_pid" 2>/dev/null; then
local previous_fingerprint=""
if [[ -n "$fingerprint" && -f "$fingerprint_file" ]]; then
previous_fingerprint="$(cat "$fingerprint_file")"
fi
if [[ -n "$fingerprint" && "$previous_fingerprint" != "$fingerprint" ]]; then
printf '%s source or environment changed; restarting pid %s\n' "$name" "$existing_pid"
stop_pid_tree "$name" "$existing_pid"
rm -f "$pid_file"
else
printf '%s already running with pid %s\n' "$name" "$existing_pid"
return 0
fi
else
rm -f "$pid_file" "$fingerprint_file"
fi
fi
printf 'starting %s\n' "$name"
(
cd "$service_dir"
exec nohup "$@" >"$LOCAL_DEBUG_LOG_DIR/$name.log" 2>&1
) >/dev/null 2>&1 &
printf '%s' "$!" >"$pid_file"
if [[ -n "$fingerprint" ]]; then
printf '%s' "$fingerprint" >"$fingerprint_file"
else
rm -f "$fingerprint_file"
fi
printf '%s pid %s log %s\n' "$name" "$(cat "$pid_file")" "$LOCAL_DEBUG_LOG_DIR/$name.log"
}
wait_for_url() {
local name="$1"
local url="$2"
local attempts="${3:-30}"
printf 'waiting for %s at %s\n' "$name" "$url"
for _ in $(seq 1 "$attempts"); do
if curl -fsS "$url" >/dev/null 2>&1; then
printf '%s is ready\n' "$name"
return 0
fi
sleep 1
done
printf '%s did not become ready at %s\n' "$name" "$url" >&2
return 1
}
printf 'local debug root: %s\n' "$LOCAL_DEBUG_ROOT"
printf 'platform: %s\n' "$(local_debug_platform_url)"
printf 'platform_web: %s\n' "$(local_debug_web_url)"
printf 'run source: %s\n' "$RUN_SOURCE_DIR"
printf 'run build bucket: %s\n' "$RUN_BUILD_BUCKET_ROOT"
printf 'run source checkout: %s\n' "$RUN_SOURCE_DIR"
printf 'run bootstrap binary: %s\n' "$RUN_BOOTSTRAP_BIN"
printf 'platform builder image: %s\n' "$PLATFORM_BUILDER_IMAGE"
printf 'platform builder workspace: %s\n' "$PLATFORM_BUILDER_WORKSPACE_DIR"
printf 'platform builder cache: %s\n' "$PLATFORM_BUILDER_CACHE_DIR"
printf 'platform log: %s\n' "$LOCAL_DEBUG_LOG_DIR/platform.log"
printf 'run log: %s\n' "$LOCAL_DEBUG_LOG_DIR/run.log"
printf 'platform_web log: %s\n' "$LOCAL_DEBUG_LOG_DIR/platform_web.log"
platform_fingerprint="$(combined_fingerprint \
"$(tracked_tree_fingerprint "$ROOT_DIR" platform scripts/dev-start.sh scripts/local-debug)" \
"PLATFORM_ADDR=$PLATFORM_ADDR" \
"PLATFORM_STORAGE_BACKEND=$PLATFORM_STORAGE_BACKEND" \
"PLATFORM_METADATA_PATH=$PLATFORM_METADATA_PATH" \
"PLATFORM_LOG_BODY_BACKEND=$PLATFORM_LOG_BODY_BACKEND" \
"PLATFORM_RUN_RELEASE_URL=$PLATFORM_RUN_RELEASE_URL" \
"PLATFORM_BUILDER_IMAGE=$PLATFORM_BUILDER_IMAGE" \
"PLATFORM_BUILDER_SOURCE_DIR=$PLATFORM_BUILDER_SOURCE_DIR" \
"PLATFORM_BUILDER_SOURCE_REVISION=$PLATFORM_BUILDER_SOURCE_REVISION")"
web_fingerprint="$(combined_fingerprint \
"$(tracked_tree_fingerprint "$ROOT_DIR" platform_web package.json package-lock.json scripts/dev-start.sh scripts/local-debug)" \
"PLATFORM_API_PROXY=$PLATFORM_API_PROXY" \
"VITE_PLATFORM_API_BASE_URL=$VITE_PLATFORM_API_BASE_URL" \
"VITE_ENABLE_LOCAL_AUTH_FALLBACK=$VITE_ENABLE_LOCAL_AUTH_FALLBACK")"
ensure_port_available platform "$LOCAL_DEBUG_PLATFORM_PORT"
ensure_port_available platform_web "$LOCAL_DEBUG_WEB_PORT"
start_service platform "$ROOT_DIR/platform" "$platform_fingerprint" env \
GOCACHE="$GOCACHE" \
PLATFORM_ADDR="$PLATFORM_ADDR" \
PLATFORM_STORAGE_BACKEND="$PLATFORM_STORAGE_BACKEND" \
PLATFORM_DATA_DIR="$PLATFORM_DATA_DIR" \
PLATFORM_METADATA_PATH="$PLATFORM_METADATA_PATH" \
PLATFORM_LOG_BODY_BACKEND="$PLATFORM_LOG_BODY_BACKEND" \
PLATFORM_LOG_DIR="$PLATFORM_LOG_DIR" \
PLATFORM_ARTIFACT_DIR="$PLATFORM_ARTIFACT_DIR" \
PLATFORM_BOOTSTRAP_ADMIN_EMAIL="$PLATFORM_BOOTSTRAP_ADMIN_EMAIL" \
PLATFORM_BOOTSTRAP_ADMIN_PASSWORD="$PLATFORM_BOOTSTRAP_ADMIN_PASSWORD" \
PLATFORM_SECRET_ENVELOPE_KEY="$PLATFORM_SECRET_ENVELOPE_KEY" \
PLATFORM_RUN_RELEASE_URL="$PLATFORM_RUN_RELEASE_URL" \
PLATFORM_BUILDER_DOCKER_BINARY="$PLATFORM_BUILDER_DOCKER_BINARY" \
PLATFORM_BUILDER_IMAGE="$PLATFORM_BUILDER_IMAGE" \
PLATFORM_BUILDER_SOURCE_DIR="$PLATFORM_BUILDER_SOURCE_DIR" \
PLATFORM_BUILDER_SOURCE_REPOSITORY="$PLATFORM_BUILDER_SOURCE_REPOSITORY" \
PLATFORM_BUILDER_SOURCE_REVISION="$PLATFORM_BUILDER_SOURCE_REVISION" \
PLATFORM_BUILDER_WORKSPACE_DIR="$PLATFORM_BUILDER_WORKSPACE_DIR" \
PLATFORM_BUILDER_CACHE_DIR="$PLATFORM_BUILDER_CACHE_DIR" \
PLATFORM_BUILDER_TIMEOUT_SECONDS="$PLATFORM_BUILDER_TIMEOUT_SECONDS" \
go run ./cmd/platform
wait_for_url platform "$(local_debug_platform_url)/healthz"
start_service platform_web "$ROOT_DIR" "$web_fingerprint" env \
PLATFORM_API_PROXY="$PLATFORM_API_PROXY" \
VITE_PLATFORM_API_BASE_URL="$VITE_PLATFORM_API_BASE_URL" \
VITE_ENABLE_LOCAL_AUTH_FALLBACK="$VITE_ENABLE_LOCAL_AUTH_FALLBACK" \
npm --prefix platform_web run dev -- --host "0.0.0.0" --port "$LOCAL_DEBUG_WEB_PORT"
wait_for_url platform_web "$(local_debug_web_url)"
stop_orphaned_run_bootstrap
local_debug_build_bootstrap_run
run_fingerprint="$(combined_fingerprint \
"$(tracked_tree_fingerprint "$RUN_SOURCE_DIR" .)" \
"$(file_fingerprint "$RUN_BOOTSTRAP_BIN")" \
"RUN_MODE=$RUN_MODE" \
"RUN_PLATFORM_URL=$RUN_PLATFORM_URL" \
"RUN_ENDPOINT_ID=$RUN_ENDPOINT_ID" \
"RUN_DISPLAY_NAME=$RUN_DISPLAY_NAME" \
"RUN_VERSION=$RUN_VERSION" \
"RUN_WORKSPACE_ROOT=$RUN_WORKSPACE_ROOT" \
"RUN_BUILD_SOURCE_ROOT=$RUN_BUILD_SOURCE_ROOT" \
"RUN_SPOOL_ROOT=$RUN_SPOOL_ROOT" \
"RUN_MAX_JOBS=$RUN_MAX_JOBS" \
"RUN_HEARTBEAT_INTERVAL_MS=$RUN_HEARTBEAT_INTERVAL_MS" \
"RUN_POLL_INTERVAL_MS=$RUN_POLL_INTERVAL_MS" \
"RUN_RETRY_BACKOFF_MS=$RUN_RETRY_BACKOFF_MS")"
start_service run "$(dirname "$RUN_BOOTSTRAP_BIN")" "$run_fingerprint" env \
GOCACHE="$GOCACHE" \
RUN_MODE="$RUN_MODE" \
RUN_PLATFORM_URL="$RUN_PLATFORM_URL" \
RUN_ENDPOINT_ID="$RUN_ENDPOINT_ID" \
RUN_DISPLAY_NAME="$RUN_DISPLAY_NAME" \
RUN_VERSION="$RUN_VERSION" \
RUN_REGISTRATION_TOKEN="$RUN_REGISTRATION_TOKEN" \
RUN_WORKSPACE_ROOT="$RUN_WORKSPACE_ROOT" \
RUN_BUILD_SOURCE_ROOT="$RUN_BUILD_SOURCE_ROOT" \
RUN_SPOOL_ROOT="$RUN_SPOOL_ROOT" \
RUN_MAX_JOBS="$RUN_MAX_JOBS" \
RUN_HEARTBEAT_INTERVAL_MS="$RUN_HEARTBEAT_INTERVAL_MS" \
RUN_POLL_INTERVAL_MS="$RUN_POLL_INTERVAL_MS" \
RUN_RETRY_BACKOFF_MS="$RUN_RETRY_BACKOFF_MS" \
"$RUN_BOOTSTRAP_BIN"
cat <<EOF
Local debug stack requested.
Login with:
account: operator.local@example.test
password: operator-local
Run smoke verification:
scripts/dev-smoke.sh
Open local console:
$(local_debug_web_url)
EOF