From d4e4f9da9b5619fd19bda7b0f8a2e15a6b96c09b Mon Sep 17 00:00:00 2001 From: Poyraz Avsever Date: Sun, 14 Jun 2026 11:43:16 +0300 Subject: [PATCH] refactor: enhance installer script with improved TTY handling and input methods --- README.md | 2 +- install.sh | 47 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe910ba..c9ea8a1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Neta is designed for self-hosting. It supports two Docker deployment modes: You can install Neta using the interactive setup script: ```bash -curl -sL https://raw.githubusercontent.com/poyrazavsever/neta/main/install.sh | bash +curl -fsSL https://raw.githubusercontent.com/poyrazavsever/neta/main/install.sh | bash ``` The installer asks for the deployment mode, writes a `.env` file, validates Docker Compose configuration, and starts the application. In full-stack mode it generates Supabase JWT secrets and applies Neta migrations automatically through the `neta-migrations` service. diff --git a/install.sh b/install.sh index 2265c88..44ec58a 100644 --- a/install.sh +++ b/install.sh @@ -7,6 +7,8 @@ set -euo pipefail REPO_URL="${NETA_REPO_URL:-https://github.com/poyrazavsever/neta.git}" TARGET_DIR="${NETA_TARGET_DIR:-neta-os}" INSTALL_MODE="${NETA_INSTALL_MODE:-}" +APPLY_MIGRATIONS="${NETA_APPLY_MIGRATIONS:-}" +TTY_PATH="${NETA_TTY_PATH:-/dev/tty}" info() { printf "\n%s\n" "$1" @@ -21,6 +23,35 @@ require_command() { command -v "$1" >/dev/null 2>&1 || fail "$1 is required." } +ensure_tty() { + if ! ( : < "$TTY_PATH" ) 2>/dev/null || ! ( : > "$TTY_PATH" ) 2>/dev/null; then + fail "Interactive input requires a TTY. Run with a terminal attached or provide the required NETA_* environment variables." + fi +} + +read_from_tty() { + local label="$1" + local value + + ensure_tty + + printf "%s" "$label" > "$TTY_PATH" + IFS= read -r value < "$TTY_PATH" || fail "Input cancelled." + printf "%s" "$value" +} + +read_secret_from_tty() { + local label="$1" + local value + + ensure_tty + + printf "%s" "$label" > "$TTY_PATH" + IFS= read -r -s value < "$TTY_PATH" || fail "Input cancelled." + printf "\n" > "$TTY_PATH" + printf "%s" "$value" +} + compose_cmd() { if docker compose version >/dev/null 2>&1; then echo "docker compose" @@ -42,7 +73,7 @@ prompt_required() { fi while true; do - read -r -p "$label: " value + value="$(read_from_tty "$label: ")" if [ -n "$value" ]; then printf -v "$var_name" "%s" "$value" export "$var_name" @@ -63,7 +94,7 @@ prompt_optional() { return fi - read -r -p "$label [$default_value]: " value + value="$(read_from_tty "$label [$default_value]: ")" printf -v "$var_name" "%s" "${value:-$default_value}" export "$var_name" } @@ -79,8 +110,7 @@ prompt_secret_required() { fi while true; do - read -r -s -p "$label: " value - echo + value="$(read_secret_from_tty "$label: ")" if [ -n "$value" ]; then printf -v "$var_name" "%s" "$value" export "$var_name" @@ -112,7 +142,7 @@ choose_install_mode() { echo " 2) app-only Neta app connected to an existing Supabase project" while true; do - read -r -p "Install mode [full-stack]: " answer + answer="$(read_from_tty "Install mode [full-stack]: ")" case "${answer:-full-stack}" in 1|full|full-stack|bundled) INSTALL_MODE="full-stack" @@ -246,7 +276,12 @@ main() { info "Wrote .env" if [ "$INSTALL_MODE" = "app-only" ]; then - read -r -p "Apply Neta database migrations now? Requires a direct Postgres DATABASE_URL. [y/N]: " apply_migrations + local apply_migrations="$APPLY_MIGRATIONS" + + if [ -z "$apply_migrations" ]; then + apply_migrations="$(read_from_tty "Apply Neta database migrations now? Requires a direct Postgres DATABASE_URL. [y/N]: ")" + fi + if [ "$apply_migrations" = "y" ] || [ "$apply_migrations" = "Y" ]; then prompt_secret_required DATABASE_URL "Postgres DATABASE_URL" DATABASE_URL="$DATABASE_URL" sh ./scripts/apply-migrations.sh