ci: add deploy script and simplify CI/CD workflow
Some checks failed
Deploy to Production / Deploy to 192.168.1.190 (push) Has been cancelled
Some checks failed
Deploy to Production / Deploy to 192.168.1.190 (push) Has been cancelled
- scripts/deploy.sh: automated deployment with interactive env setup (--env-only, --build, --full) - Supports OpenRouter, OpenAI, Ollama, DeepSeek providers - Auto-generates NEXTAUTH_SECRET and postgres password - Waits for healthchecks, initializes DB, shows status - CI/CD workflow simplified to call deploy.sh --build Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
295
scripts/deploy.sh
Executable file
295
scripts/deploy.sh
Executable file
@@ -0,0 +1,295 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# Memento - Deploy Script
|
||||
# ============================================================
|
||||
# Usage: ./scripts/deploy.sh [--env-only | --build | --full]
|
||||
#
|
||||
# --env-only : Generate .env.docker interactively
|
||||
# --build : Build and start containers (default)
|
||||
# --full : env + build + first-time setup
|
||||
# ============================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
ENV_FILE="$PROJECT_DIR/.env.docker"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Generate .env.docker
|
||||
# -----------------------------------------------------------
|
||||
generate_env() {
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
warn "$ENV_FILE already exists."
|
||||
read -rp "Overwrite? [y/N] " confirm
|
||||
[[ "$confirm" != "y" && "$confirm" != "Y" ]] && return 0
|
||||
fi
|
||||
|
||||
info "Generating $ENV_FILE ..."
|
||||
|
||||
# Auto-generate NEXTAUTH_SECRET
|
||||
local secret
|
||||
secret=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64)
|
||||
|
||||
# Auto-generate secure postgres password
|
||||
local pg_pass
|
||||
pg_pass=$(openssl rand -hex 16 2>/dev/null || head -c 16 /dev/urandom | hexdump -v -e '/1 "%02x"')
|
||||
|
||||
# Ask for URL
|
||||
local url="http://192.168.1.190"
|
||||
read -rp "NEXTAUTH_URL [$url]: " input_url
|
||||
[ -n "$input_url" ] && url="$input_url"
|
||||
|
||||
# Ask for AI provider
|
||||
echo ""
|
||||
echo "AI Provider:"
|
||||
echo " 1) OpenRouter (recommended, works out of the box)"
|
||||
echo " 2) Ollama (requires ollama container)"
|
||||
echo " 3) OpenAI"
|
||||
echo " 4) DeepSeek"
|
||||
echo " 5) Skip AI configuration"
|
||||
read -rp "Choice [1]: " ai_choice
|
||||
ai_choice="${ai_choice:-1}"
|
||||
|
||||
local ai_tags_provider="openrouter"
|
||||
local ai_tags_model="google/gemma-3-27b-it"
|
||||
local ai_embed_provider="openrouter"
|
||||
local ai_embed_model="text-embedding-3-small"
|
||||
local ai_chat_provider="openrouter"
|
||||
local ai_chat_model="google/gemma-3-27b-it"
|
||||
local custom_api_key=""
|
||||
local custom_base_url=""
|
||||
|
||||
case "$ai_choice" in
|
||||
1)
|
||||
read -rp "OpenRouter API Key: " custom_api_key
|
||||
[ -z "$custom_api_key" ] && error "API key is required"
|
||||
custom_base_url="https://openrouter.ai/api/v1"
|
||||
;;
|
||||
2)
|
||||
ai_tags_provider="ollama"
|
||||
ai_tags_model="granite4:latest"
|
||||
ai_embed_provider="ollama"
|
||||
ai_embed_model="embeddinggemma:latest"
|
||||
ai_chat_provider="ollama"
|
||||
ai_chat_model="granite4:latest"
|
||||
custom_base_url="http://ollama:11434"
|
||||
;;
|
||||
3)
|
||||
ai_tags_provider="openai"
|
||||
ai_tags_model="gpt-4o-mini"
|
||||
ai_embed_provider="openai"
|
||||
ai_embed_model="text-embedding-3-small"
|
||||
ai_chat_provider="openai"
|
||||
ai_chat_model="gpt-4o-mini"
|
||||
read -rp "OpenAI API Key: " custom_api_key
|
||||
[ -z "$custom_api_key" ] && error "API key is required"
|
||||
;;
|
||||
4)
|
||||
ai_tags_provider="deepseek"
|
||||
ai_tags_model="deepseek-chat"
|
||||
ai_embed_provider="openrouter"
|
||||
ai_embed_model="text-embedding-3-small"
|
||||
ai_chat_provider="deepseek"
|
||||
ai_chat_model="deepseek-chat"
|
||||
read -rp "DeepSeek API Key: " custom_api_key
|
||||
[ -z "$custom_api_key" ] && error "API key is required"
|
||||
custom_base_url="https://api.deepseek.com/v1"
|
||||
;;
|
||||
5)
|
||||
info "Skipping AI configuration"
|
||||
;;
|
||||
*)
|
||||
error "Invalid choice"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Ask for email provider
|
||||
echo ""
|
||||
echo "Email provider:"
|
||||
echo " 1) Resend"
|
||||
echo " 2) SMTP"
|
||||
echo " 3) Skip"
|
||||
read -rp "Choice [3]: " email_choice
|
||||
email_choice="${email_choice:-3}"
|
||||
|
||||
local resend_key=""
|
||||
local smtp_config=""
|
||||
|
||||
case "$email_choice" in
|
||||
1)
|
||||
read -rp "Resend API Key: " resend_key
|
||||
;;
|
||||
2)
|
||||
local smtp_host smtp_port smtp_user smtp_pass smtp_from
|
||||
read -rp "SMTP Host: " smtp_host
|
||||
read -rp "SMTP Port [587]: " smtp_port; smtp_port="${smtp_port:-587}"
|
||||
read -rp "SMTP User: " smtp_user
|
||||
read -rp "SMTP Password: " smtp_pass
|
||||
read -rp "SMTP From: " smtp_from
|
||||
smtp_config="SMTP_HOST=\"$smtp_host\"
|
||||
SMTP_PORT=\"$smtp_port\"
|
||||
SMTP_USER=\"$smtp_user\"
|
||||
SMTP_PASS=\"$smtp_pass\"
|
||||
SMTP_FROM=\"$smtp_from\""
|
||||
;;
|
||||
esac
|
||||
|
||||
# Write .env.docker
|
||||
cat > "$ENV_FILE" << ENVEOF
|
||||
# =============================================================================
|
||||
# Memento - Docker Environment (auto-generated)
|
||||
# =============================================================================
|
||||
NEXTAUTH_URL="${url}"
|
||||
NEXTAUTH_SECRET="${secret}"
|
||||
ALLOW_REGISTRATION="true"
|
||||
|
||||
# PostgreSQL
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=memento
|
||||
POSTGRES_USER=memento
|
||||
POSTGRES_PASSWORD="${pg_pass}"
|
||||
|
||||
# MCP Server
|
||||
MCP_MODE="sse"
|
||||
MCP_PORT="3001"
|
||||
|
||||
# AI - Tags
|
||||
AI_PROVIDER_TAGS=${ai_tags_provider}
|
||||
AI_MODEL_TAGS="${ai_tags_model}"
|
||||
|
||||
# AI - Embeddings
|
||||
AI_PROVIDER_EMBEDDING=${ai_embed_provider}
|
||||
AI_MODEL_EMBEDDING="${ai_embed_model}"
|
||||
|
||||
# AI - Chat
|
||||
AI_PROVIDER_CHAT=${ai_chat_provider}
|
||||
AI_MODEL_CHAT="${ai_chat_model}"
|
||||
ENVEOF
|
||||
|
||||
# Add API key config if applicable
|
||||
if [ -n "$custom_api_key" ]; then
|
||||
case "$ai_choice" in
|
||||
1)
|
||||
echo "CUSTOM_OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
|
||||
echo "CUSTOM_OPENAI_BASE_URL=\"${custom_base_url}\"" >> "$ENV_FILE"
|
||||
;;
|
||||
3)
|
||||
echo "OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
|
||||
;;
|
||||
4)
|
||||
echo "CUSTOM_OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
|
||||
echo "CUSTOM_OPENAI_BASE_URL=\"${custom_base_url}\"" >> "$ENV_FILE"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Add Ollama URL if using ollama
|
||||
if [ "$ai_choice" = "2" ]; then
|
||||
echo "OLLAMA_BASE_URL=\"http://ollama:11434\"" >> "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# Add email config
|
||||
if [ -n "$resend_key" ]; then
|
||||
echo "RESEND_API_KEY=\"${resend_key}\"" >> "$ENV_FILE"
|
||||
fi
|
||||
if [ -n "$smtp_config" ]; then
|
||||
echo "" >> "$ENV_FILE"
|
||||
echo "$smtp_config" >> "$ENV_FILE"
|
||||
fi
|
||||
|
||||
ok "$ENV_FILE created"
|
||||
echo ""
|
||||
info "Contents:"
|
||||
grep -v "KEY\|PASSWORD\|SECRET" "$ENV_FILE" | sed 's/^/ /'
|
||||
echo " (sensitive values hidden)"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Build and deploy
|
||||
# -----------------------------------------------------------
|
||||
deploy() {
|
||||
[ -f "$ENV_FILE" ] || error "$ENV_FILE not found. Run with --env-only first."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
info "Pulling latest code..."
|
||||
git pull origin main 2>/dev/null || warn "Could not pull (maybe already up to date or no git)"
|
||||
|
||||
info "Building containers..."
|
||||
docker compose build --parallel 2>&1
|
||||
|
||||
info "Starting containers..."
|
||||
docker compose up -d 2>&1
|
||||
|
||||
info "Waiting for healthchecks..."
|
||||
local retries=0
|
||||
local max_retries=30
|
||||
while [ $retries -lt $max_retries ]; do
|
||||
local unhealthy
|
||||
unhealthy=$(docker compose ps --format '{{.Status}}' 2>/dev/null | grep -c -v "healthy\|Up" || true)
|
||||
if [ "$unhealthy" -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
retries=$((retries + 1))
|
||||
sleep 2
|
||||
printf "."
|
||||
done
|
||||
echo ""
|
||||
|
||||
info "Initializing database..."
|
||||
docker compose exec memento-note npx prisma db push --skip-generate 2>/dev/null || \
|
||||
warn "DB push failed (may already be synced)"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
docker compose ps
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
ok "Deploy complete!"
|
||||
info "App: $(grep NEXTAUTH_URL "$ENV_FILE" | cut -d= -f2 | tr -d '"')"
|
||||
|
||||
# Show admin setup hint if first time
|
||||
local user_count
|
||||
user_count=$(docker compose exec -T postgres psql -U memento -d memento -t -c 'SELECT COUNT(*) FROM "User"' 2>/dev/null | tr -d ' ' || echo "0")
|
||||
if [ "$user_count" = "0" ]; then
|
||||
echo ""
|
||||
warn "No users found. Register at $(grep NEXTAUTH_URL "$ENV_FILE" | cut -d= -f2 | tr -d '"')/register"
|
||||
warn "Then run: docker compose exec memento-note npx tsx scripts/grant-all-admins.ts"
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Main
|
||||
# -----------------------------------------------------------
|
||||
ACTION="${1:---build}"
|
||||
|
||||
case "$ACTION" in
|
||||
--env-only)
|
||||
generate_env
|
||||
;;
|
||||
--build)
|
||||
deploy
|
||||
;;
|
||||
--full)
|
||||
generate_env
|
||||
echo ""
|
||||
deploy
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [--env-only | --build | --full]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user