harb/scripts/dev.sh
johba 6cbb1781ce tax rate, version and compose (#70)
resolves #67

Co-authored-by: johba <johba@harb.eth>
Reviewed-on: https://codeberg.org/johba/harb/pulls/70
2025-10-07 19:26:08 +02:00

239 lines
6.5 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
PID_FILE=/tmp/kraiken-watcher.pid
PROJECT_NAME=${COMPOSE_PROJECT_NAME:-$(basename "$PWD")}
start_stack() {
if [[ -f "$PID_FILE" ]]; then
local existing_pid
existing_pid=$(cat "$PID_FILE")
if kill -0 "$existing_pid" 2>/dev/null; then
echo "Stopping existing kraiken-lib watcher ($existing_pid)..."
kill "$existing_pid" 2>/dev/null || true
wait "$existing_pid" 2>/dev/null || true
fi
rm -f "$PID_FILE"
fi
# Show branch if set
if [[ -n "${GIT_BRANCH:-}" ]]; then
echo "Branch: $GIT_BRANCH"
fi
echo "Building kraiken-lib..."
./scripts/build-kraiken-lib.sh
echo "Starting stack..."
# Start services in strict dependency order with explicit create+start
# This avoids podman dependency graph issues
# Create all containers first (without starting)
echo " Creating containers..."
podman-compose up --no-start 2>&1 | grep -v "STEP\|Copying\|Writing\|Getting\|fetch\|Installing\|Executing" || true
# Phase 1: Start base services (no dependencies)
echo " Starting anvil & postgres..."
podman-compose start anvil postgres >/dev/null 2>&1
# Wait for base services to be healthy
echo " Waiting for anvil & postgres..."
for i in {1..30}; do
anvil_healthy=$(podman healthcheck run harb_anvil_1 >/dev/null 2>&1 && echo "yes" || echo "no")
postgres_healthy=$(podman healthcheck run harb_postgres_1 >/dev/null 2>&1 && echo "yes" || echo "no")
if [[ "$anvil_healthy" == "yes" ]] && [[ "$postgres_healthy" == "yes" ]]; then
break
fi
sleep 2
done
# Phase 2: Start bootstrap (depends on anvil & postgres healthy)
echo " Starting bootstrap..."
podman-compose start bootstrap >/dev/null 2>&1
# Wait for bootstrap to complete
echo " Waiting for bootstrap..."
for i in {1..60}; do
bootstrap_status=$(podman inspect harb_bootstrap_1 --format='{{.State.Status}}')
if [[ "$bootstrap_status" == "exited" ]]; then
break
fi
sleep 2
done
# Phase 3: Start ponder (depends on bootstrap completed)
echo " Starting ponder..."
podman-compose start ponder >/dev/null 2>&1
# Wait for ponder to be healthy
echo " Waiting for ponder..."
for i in {1..60}; do
ponder_healthy=$(podman healthcheck run harb_ponder_1 >/dev/null 2>&1 && echo "yes" || echo "no")
if [[ "$ponder_healthy" == "yes" ]]; then
break
fi
sleep 2
done
# Phase 4: Start frontend services (depend on ponder healthy)
echo " Starting webapp, landing, txn-bot..."
podman-compose start webapp landing txn-bot >/dev/null 2>&1
# Phase 5: Start caddy (depends on frontend services)
sleep 5
echo " Starting caddy..."
podman-compose start caddy >/dev/null 2>&1
echo "Watching for kraiken-lib changes..."
./scripts/watch-kraiken-lib.sh &
echo $! > "$PID_FILE"
echo ""
echo "[ok] Stack started"
echo " Web App: http://localhost:8081/app/"
echo " GraphQL: http://localhost:8081/graphql"
}
stop_stack() {
if [[ -f "$PID_FILE" ]]; then
local watcher_pid
watcher_pid=$(cat "$PID_FILE")
if kill "$watcher_pid" 2>/dev/null; then
wait "$watcher_pid" 2>/dev/null || true
fi
rm -f "$PID_FILE"
fi
podman-compose down
echo "[ok] Stack stopped"
}
check_health() {
echo "Checking health..."
local services=(anvil postgres ponder webapp landing txn-bot caddy)
for service in "${services[@]}"; do
local container
container=$(podman ps --all \
--filter "label=com.docker.compose.project=${PROJECT_NAME}" \
--filter "label=com.docker.compose.service=${service}" \
--format '{{.Names}}' | head -n1)
if [[ -z "$container" ]]; then
echo " [??] $service (not created)"
continue
fi
if podman healthcheck run "$container" &>/dev/null; then
echo " [ok] $service"
else
echo " [!!] $service"
fi
done
}
restart_light() {
echo "Light restart: webapp + txn-bot only..."
echo " Preserving Anvil state (contracts remain deployed)"
local webapp_container txnbot_container
webapp_container=$(podman ps --all \
--filter "label=com.docker.compose.project=${PROJECT_NAME}" \
--filter "label=com.docker.compose.service=webapp" \
--format '{{.Names}}' | head -n1)
txnbot_container=$(podman ps --all \
--filter "label=com.docker.compose.project=${PROJECT_NAME}" \
--filter "label=com.docker.compose.service=txn-bot" \
--format '{{.Names}}' | head -n1)
if [[ -z "$webapp_container" ]]; then
echo "[!!] webapp container not found - run './scripts/dev.sh start' first"
exit 1
fi
local start_time=$(date +%s)
echo " Restarting containers..."
podman restart "$webapp_container" >/dev/null
[[ -n "$txnbot_container" ]] && podman restart "$txnbot_container" >/dev/null
echo " Waiting for webapp to be ready..."
local max_attempts=30
local attempt=0
while ((attempt < max_attempts)); do
if curl -s -f -o /dev/null http://localhost:5173/app/ 2>/dev/null; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "[ok] Light restart complete (~${duration}s)"
echo " Web App: http://localhost:8081/app/"
return 0
fi
sleep 2
((attempt++))
done
echo "[!!] Webapp failed to respond after ${max_attempts} attempts"
exit 1
}
restart_full() {
echo "Full restart: all containers + bootstrap..."
stop_stack
start_stack
echo "[ok] Full restart complete"
}
usage() {
cat <<EOF
Usage: $0 {start|stop|health|restart [--light|--full]}
Commands:
start Start all services (builds kraiken-lib, runs bootstrap)
stop Stop all services
health Check service health
restart Full restart (default: redeploys contracts)
restart --light Light restart (webapp + txnbot only, preserves state)
restart --full Full restart (same as 'restart')
Environment Variables:
GIT_BRANCH Branch to checkout in containers
Examples:
./scripts/dev.sh start
./scripts/dev.sh restart --light # Fast frontend iteration (~10-20s)
./scripts/dev.sh restart --full # Fresh contract deployment (~3-4min)
GIT_BRANCH=fix/something ./scripts/dev.sh start
./scripts/dev.sh health
EOF
exit 1
}
case "${1:-help}" in
start)
start_stack
;;
stop)
stop_stack
;;
health)
check_health
;;
restart)
case "${2:-}" in
--light)
restart_light
;;
--full|"")
restart_full
;;
*)
echo "Unknown restart mode: $2"
usage
;;
esac
;;
*)
usage
;;
esac