#!/usr/bin/env bash set -euo pipefail ensure_posix_root() { local project_root=${1:?project root is required} local posix_root=${2:-"$project_root/POSIX"} mkdir -p "$posix_root" } confirm_destructive_action() { local prompt=${1:-Are you sure you want to continue?} local response_input="" if [[ "${JUST_YES:-0}" == "1" ]]; then return 0 fi if [[ -t 0 ]]; then printf '%s [y/N] ' "$prompt" IFS= read -r response_input elif [[ -r /dev/tty ]]; then printf '%s [y/N] ' "$prompt" > /dev/tty IFS= read -r response_input < /dev/tty else printf 'Refusing destructive action without interactive confirmation. Re-run with JUST_YES=1 to continue.\n' >&2 exit 1 fi case "$response_input" in y|Y|yes|YES|Yes) return 0 ;; *) printf 'Aborted.\n' >&2 exit 1 ;; esac } run_compose_api_migrations() { local compose_file=${1:?compose file is required} local service_name=${2:-api} local max_attempts=${3:-20} local sleep_seconds=${4:-2} local attempt=1 local output="" while (( attempt <= max_attempts )); do if output=$(docker compose -f "$compose_file" exec -T "$service_name" sh -lc 'go run ./cmd/migrate up' 2>&1); then printf '%s\n' "$output" return 0 fi if (( attempt == max_attempts )); then printf 'Failed to apply database migrations automatically after %d attempts.\n' "$max_attempts" >&2 printf '%s\n' "$output" >&2 return 1 fi sleep "$sleep_seconds" attempt=$((attempt + 1)) done }