migrate/podman-to-docker (#92)

podman to docker

Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/92
This commit is contained in:
johba 2025-11-08 14:08:46 +01:00
parent c2720c35a5
commit 5d71753086
13 changed files with 211 additions and 100 deletions

View file

@ -4,17 +4,33 @@ set -euo pipefail
cd "$(dirname "$0")/.."
# Timeout constants (in seconds)
readonly ANVIL_TIMEOUT=30 # Anvil starts fast
readonly POSTGRES_TIMEOUT=20 # Database init is quick
readonly BOOTSTRAP_TIMEOUT=60 # Contract deployment + seeding
readonly PONDER_TIMEOUT=90 # Must index bootstrap events
readonly WEBAPP_TIMEOUT=90 # npm install + Vite startup
readonly CADDY_TIMEOUT=10 # Proxy starts instantly
readonly ANVIL_TIMEOUT=60 # Anvil starts fast (increased for first-time setup)
readonly POSTGRES_TIMEOUT=30 # Database init is quick
readonly BOOTSTRAP_TIMEOUT=120 # Contract deployment + seeding
readonly PONDER_TIMEOUT=120 # Must index bootstrap events
readonly WEBAPP_TIMEOUT=120 # npm install + Vite startup
readonly CADDY_TIMEOUT=20 # Proxy starts instantly
readonly POLL_INTERVAL=2 # Check health every N seconds
PID_FILE=/tmp/kraiken-watcher.pid
PROJECT_NAME=${COMPOSE_PROJECT_NAME:-$(basename "$PWD")}
# Detect container runtime
if command -v docker compose &> /dev/null; then
COMPOSE_CMD="docker compose"
RUNTIME_CMD="docker"
elif command -v docker-compose &> /dev/null; then
COMPOSE_CMD="docker-compose"
RUNTIME_CMD="docker"
else
echo "Error: docker/docker-compose not found. Please install Docker."
echo ""
echo "Installation instructions:"
echo " Linux: https://docs.docker.com/engine/install/"
echo " Mac: brew install colima docker docker-compose && colima start"
exit 1
fi
container_name() {
local service="$1"
echo "${PROJECT_NAME}_${service}_1"
@ -28,13 +44,13 @@ cleanup_existing() {
# Remove PID file
rm -f "$PID_FILE"
# Kill zombie podman processes
pkill -9 -f "podman wait.*${PROJECT_NAME}_" 2>/dev/null || true
# Kill zombie container processes
pkill -9 -f "${RUNTIME_CMD} wait.*${PROJECT_NAME}_" 2>/dev/null || true
# Remove any existing containers (suppress errors if they don't exist)
echo " Cleaning up existing containers..."
podman ps -a --filter "label=com.docker.compose.project=${PROJECT_NAME}" --format "{{.Names}}" 2>/dev/null | \
xargs -r podman rm -f 2>&1 | grep -v "Error.*no container" || true
${RUNTIME_CMD} ps -a --filter "label=com.docker.compose.project=${PROJECT_NAME}" --format "{{.Names}}" 2>/dev/null | \
xargs -r ${RUNTIME_CMD} rm -f 2>&1 | grep -v "Error.*no container" || true
}
# Wait for container to be healthy (via healthcheck)
@ -45,7 +61,10 @@ wait_for_healthy() {
local start_time=$(date +%s)
for i in $(seq 1 "$max_attempts"); do
if podman healthcheck run "$container" &>/dev/null; then
# Docker doesn't have a standalone healthcheck command, check via inspect
local health_status
health_status=$(${RUNTIME_CMD} inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "unknown")
if [[ "$health_status" == "healthy" ]]; then
local elapsed=$(($(date +%s) - start_time))
echo "$container ready (${elapsed}s)"
return 0
@ -66,7 +85,7 @@ wait_for_exited() {
for i in $(seq 1 "$max_attempts"); do
local status
status=$(podman inspect "$container" --format='{{.State.Status}}' 2>/dev/null || echo "unknown")
status=$(${RUNTIME_CMD} inspect "$container" --format='{{.State.Status}}' 2>/dev/null || echo "unknown")
if [[ "$status" == "exited" ]]; then
local elapsed=$(($(date +%s) - start_time))
echo "$container completed (${elapsed}s)"
@ -97,32 +116,32 @@ start_stack() {
# Phase 1: Start base services (no dependencies)
echo " Starting anvil & postgres..."
podman-compose up -d anvil postgres 2>&1 | grep -v "STEP\|Copying\|Writing\|Getting\|fetch\|Installing\|Executing" || true
${COMPOSE_CMD} up -d anvil postgres 2>&1 | grep -v "STEP\|Copying\|Writing\|Getting\|fetch\|Installing\|Executing" || true
wait_for_healthy "$(container_name anvil)" "$ANVIL_TIMEOUT" || exit 1
wait_for_healthy "$(container_name postgres)" "$POSTGRES_TIMEOUT" || exit 1
# Phase 2: Start bootstrap (depends on anvil & postgres healthy)
echo " Starting bootstrap..."
podman-compose up -d bootstrap >/dev/null 2>&1
${COMPOSE_CMD} up -d bootstrap >/dev/null 2>&1
wait_for_exited "$(container_name bootstrap)" "$BOOTSTRAP_TIMEOUT" || exit 1
# Phase 3: Start ponder (depends on bootstrap completed)
echo " Starting ponder..."
podman-compose up -d ponder >/dev/null 2>&1
${COMPOSE_CMD} up -d ponder >/dev/null 2>&1
wait_for_healthy "$(container_name ponder)" "$PONDER_TIMEOUT" || exit 1
# Phase 4: Start frontend services (depend on ponder healthy)
echo " Starting webapp, landing, txn-bot..."
podman-compose up -d webapp landing txn-bot >/dev/null 2>&1
${COMPOSE_CMD} up -d webapp landing txn-bot >/dev/null 2>&1
wait_for_healthy "$(container_name webapp)" "$WEBAPP_TIMEOUT" || exit 1
# Phase 5: Start caddy (depends on frontend services)
echo " Starting caddy..."
podman-compose up -d caddy >/dev/null 2>&1
${COMPOSE_CMD} up -d caddy >/dev/null 2>&1
wait_for_healthy "$(container_name caddy)" "$CADDY_TIMEOUT" || exit 1
@ -142,7 +161,7 @@ start_stack() {
stop_stack() {
cleanup_existing
podman-compose down
${COMPOSE_CMD} down
echo "[ok] Stack stopped"
}
@ -151,7 +170,7 @@ check_health() {
local services=(anvil postgres ponder webapp landing txn-bot caddy)
for service in "${services[@]}"; do
local container
container=$(podman ps --all \
container=$(${RUNTIME_CMD} ps --all \
--filter "label=com.docker.compose.project=${PROJECT_NAME}" \
--filter "label=com.docker.compose.service=${service}" \
--format '{{.Names}}' | head -n1)
@ -161,7 +180,9 @@ check_health() {
continue
fi
if podman healthcheck run "$container" &>/dev/null; then
local health_status
health_status=$(${RUNTIME_CMD} inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "unknown")
if [[ "$health_status" == "healthy" ]]; then
echo " [ok] $service"
else
echo " [!!] $service"
@ -174,12 +195,12 @@ restart_light() {
echo " Preserving Anvil state (contracts remain deployed)"
local webapp_container txnbot_container
webapp_container=$(podman ps --all \
webapp_container=$(${RUNTIME_CMD} 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 \
txnbot_container=$(${RUNTIME_CMD} ps --all \
--filter "label=com.docker.compose.project=${PROJECT_NAME}" \
--filter "label=com.docker.compose.service=txn-bot" \
--format '{{.Names}}' | head -n1)
@ -192,8 +213,8 @@ restart_light() {
local start_time=$(date +%s)
echo " Restarting containers..."
podman restart "$webapp_container" >/dev/null
[[ -n "$txnbot_container" ]] && podman restart "$txnbot_container" >/dev/null
${RUNTIME_CMD} restart "$webapp_container" >/dev/null
[[ -n "$txnbot_container" ]] && ${RUNTIME_CMD} restart "$txnbot_container" >/dev/null
echo " Waiting for webapp to be ready..."
local max_attempts=30

View file

@ -10,8 +10,8 @@ if ! command -v inotifywait >/dev/null 2>&1; then
exit 1
fi
if ! command -v podman >/dev/null 2>&1; then
echo "Error: podman not found on PATH." >&2
if ! command -v docker >/dev/null 2>&1; then
echo "Error: docker not found on PATH." >&2
exit 1
fi
@ -20,7 +20,7 @@ cd "$ROOT_DIR"
restart_services() {
local missing=0
local running
mapfile -t running < <(podman ps --format '{{.Names}}')
mapfile -t running < <(docker ps --format '{{.Names}}')
for service in "${SERVICES[@]}"; do
local found=1
@ -37,7 +37,7 @@ restart_services() {
continue
fi
if ! podman restart "$service" >/dev/null; then
if ! docker restart "$service" >/dev/null; then
echo "Warning: failed to restart $service" >&2
missing=1
fi