mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Improve deployment script
This commit is contained in:
@@ -112,6 +112,7 @@ web_modules/
|
|||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
|
.env.prod
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
|
|||||||
+170
-56
@@ -19,7 +19,7 @@ BOLD='\033[1m'
|
|||||||
info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
||||||
success() { echo -e "${GREEN}✓${NC} $1"; }
|
success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||||
warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||||
error() { echo -e "${RED}✗${NC} $1"; exit 1; }
|
error() { echo -e "${RED}✗${NC} $1"; }
|
||||||
step() { echo -e "${CYAN}${BOLD}→${NC} ${BOLD}$1${NC}"; }
|
step() { echo -e "${CYAN}${BOLD}→${NC} ${BOLD}$1${NC}"; }
|
||||||
substep() {
|
substep() {
|
||||||
if [ "$2" = "-n" ]; then
|
if [ "$2" = "-n" ]; then
|
||||||
@@ -29,6 +29,44 @@ substep() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo -e "${MAGENTA}${BOLD}🚀 Deployment${NC}\n"
|
||||||
|
|
||||||
|
# Read password with asterisks
|
||||||
|
read_password() {
|
||||||
|
local password=""
|
||||||
|
local char
|
||||||
|
local old_stty
|
||||||
|
|
||||||
|
# Save current terminal settings
|
||||||
|
old_stty=$(stty -g 2>/dev/null)
|
||||||
|
|
||||||
|
# Disable echo
|
||||||
|
stty -echo 2>/dev/null
|
||||||
|
|
||||||
|
# Read characters one by one
|
||||||
|
while IFS= read -rs -n 1 char; do
|
||||||
|
# Check for Enter key (empty means Enter was pressed)
|
||||||
|
if [ -z "$char" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# Check for backspace/delete (ASCII 127)
|
||||||
|
if [ "$char" = $'\177' ] || [ "$char" = $'\b' ]; then
|
||||||
|
if [ ${#password} -gt 0 ]; then
|
||||||
|
password="${password%?}"
|
||||||
|
printf "\b \b" >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
password+="$char"
|
||||||
|
printf "*" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Restore terminal settings
|
||||||
|
stty "$old_stty" 2>/dev/null
|
||||||
|
echo "" >&2
|
||||||
|
echo "$password"
|
||||||
|
}
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
DEPLOYMENT_DIR="$PROJECT_ROOT/deployment"
|
DEPLOYMENT_DIR="$PROJECT_ROOT/deployment"
|
||||||
@@ -53,8 +91,9 @@ fi
|
|||||||
|
|
||||||
# Read server from environment variable (from .env), command line argument, or fallback
|
# Read server from environment variable (from .env), command line argument, or fallback
|
||||||
SERVER="${1:-${DEPLOYMENT_SERVER:-}}"
|
SERVER="${1:-${DEPLOYMENT_SERVER:-}}"
|
||||||
DEPLOY_PATH="${2:-${DEPLOY_PATH:-/home/denis0001-dev/actions-runner/_work/FromChat/FromChat}}"
|
REPO_NAME="FromChat"
|
||||||
PLATFORM="${3:-linux/arm64}"
|
DEPLOY_PATH="~/actions-runner/_work/$REPO_NAME/$REPO_NAME"
|
||||||
|
PLATFORM="linux/arm64"
|
||||||
|
|
||||||
# Check if server is provided
|
# Check if server is provided
|
||||||
if [ -z "$SERVER" ]; then
|
if [ -z "$SERVER" ]; then
|
||||||
@@ -68,13 +107,68 @@ if [ -z "$SERVER" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${MAGENTA}${BOLD}🚀 Deployment${NC}\n"
|
# ============================================================================
|
||||||
|
# SSH AUTHENTICATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
step "Authentication"
|
||||||
|
SSH_KEY_FILE="$HOME/.ssh/id_rsa"
|
||||||
|
|
||||||
|
# Ensure ssh-agent is running
|
||||||
|
if [ -z "$SSH_AUTH_SOCK" ]; then
|
||||||
|
eval "$(ssh-agent -s)" > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add SSH key to agent if not already loaded
|
||||||
|
if [ -f "$SSH_KEY_FILE" ]; then
|
||||||
|
# Check if key is already loaded
|
||||||
|
KEY_LOADED=false
|
||||||
|
if ssh-add -l > /dev/null 2>&1; then
|
||||||
|
# Check if this specific key is loaded by trying to match the public key
|
||||||
|
KEY_FINGERPRINT=$(ssh-keygen -lf "$SSH_KEY_FILE" 2>/dev/null | awk '{print $2}')
|
||||||
|
if [ -n "$KEY_FINGERPRINT" ] && ssh-add -l 2>/dev/null | grep -q "$KEY_FINGERPRINT"; then
|
||||||
|
KEY_LOADED=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$KEY_LOADED" = false ]; then
|
||||||
|
substep "Adding SSH key to agent..."
|
||||||
|
ssh-add "$SSH_KEY_FILE" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warning "SSH key not found at $SSH_KEY_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test SSH connection once to cache the key (this will prompt for passphrase if needed)
|
||||||
|
ssh -o ConnectTimeout=5 "$SERVER" "echo" > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SUDO AUTHENTICATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
SUDO_PASSWORD=""
|
||||||
|
while true; do
|
||||||
|
substep "Sudo password: " -n
|
||||||
|
SUDO_PASSWORD=$(read_password)
|
||||||
|
|
||||||
|
if [ -z "$SUDO_PASSWORD" ]; then
|
||||||
|
warning "No password provided - assuming passwordless sudo"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$SUDO_PASSWORD" | ssh "$SERVER" "sudo -S -v" > /dev/null 2>&1; then
|
||||||
|
export SUDO_PASSWORD
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo -n " " && error "Invalid password, please try again"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# BUILD PHASE
|
# BUILD PHASE
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
echo -e "${MAGENTA}${BOLD}🔨 Building Docker images${NC}"
|
echo -e "\n${MAGENTA}${BOLD}🔨 Building Docker images${NC}\n"
|
||||||
|
|
||||||
# Determine project name
|
# Determine project name
|
||||||
if [ -n "$SERVER" ]; then
|
if [ -n "$SERVER" ]; then
|
||||||
@@ -84,11 +178,48 @@ else
|
|||||||
PROJECT_NAME=$(basename "$DEPLOYMENT_DIR")
|
PROJECT_NAME=$(basename "$DEPLOYMENT_DIR")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if Docker daemon is running
|
||||||
|
check_docker_daemon() {
|
||||||
|
docker info > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start Docker Desktop
|
||||||
|
start_docker_desktop() {
|
||||||
|
substep "Starting Docker Desktop..."
|
||||||
|
if ! docker desktop start > /dev/null 2>&1; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for Docker to be ready (max 60 seconds)
|
||||||
|
substep "Waiting for Docker to start..." -n
|
||||||
|
local max_wait=60
|
||||||
|
local waited=0
|
||||||
|
while [ $waited -lt $max_wait ]; do
|
||||||
|
if check_docker_daemon; then
|
||||||
|
echo ""
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
waited=$((waited + 2))
|
||||||
|
echo -n "."
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# Check buildx
|
# Check buildx
|
||||||
if ! docker buildx version > /dev/null 2>&1; then
|
if ! docker buildx version > /dev/null 2>&1; then
|
||||||
error "Docker buildx not available. Install Docker Desktop."
|
error "Docker buildx not available. Install Docker Desktop."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check Docker daemon
|
||||||
|
if ! check_docker_daemon; then
|
||||||
|
warning "Docker daemon is not running"
|
||||||
|
if ! start_docker_desktop; then
|
||||||
|
error "Failed to start Docker Desktop. Please start it manually and try again."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Setup buildx builder
|
# Setup buildx builder
|
||||||
step "Setting up buildx builder"
|
step "Setting up buildx builder"
|
||||||
BUILDER_NAME="fromchat-builder"
|
BUILDER_NAME="fromchat-builder"
|
||||||
@@ -209,31 +340,6 @@ success "Build complete! ${#BUILT_IMAGES[@]} image(s) ready"
|
|||||||
|
|
||||||
echo -e "\n${MAGENTA}${BOLD}🚀 Deploying to ${SERVER}${NC}\n"
|
echo -e "\n${MAGENTA}${BOLD}🚀 Deploying to ${SERVER}${NC}\n"
|
||||||
|
|
||||||
# Ask for sudo password at the beginning
|
|
||||||
step "Authentication"
|
|
||||||
SUDO_PASSWORD=""
|
|
||||||
while true; do
|
|
||||||
substep "Sudo password: " -n
|
|
||||||
read -sp "" SUDO_PASSWORD
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ -z "$SUDO_PASSWORD" ]; then
|
|
||||||
warning "No password provided - assuming passwordless sudo"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
if echo "$SUDO_PASSWORD" | ssh "$SERVER" "sudo -S -v" > /dev/null 2>&1; then
|
|
||||||
export SUDO_PASSWORD
|
|
||||||
break
|
|
||||||
else
|
|
||||||
error "Invalid password, please try again"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check SSH connection (silent)
|
|
||||||
if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$SERVER" "echo" > /dev/null 2>&1; then
|
|
||||||
warning "SSH key auth not available, will prompt when needed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check docker pussh
|
# Check docker pussh
|
||||||
if ! docker pussh --help > /dev/null 2>&1; then
|
if ! docker pussh --help > /dev/null 2>&1; then
|
||||||
@@ -275,42 +381,51 @@ fi
|
|||||||
|
|
||||||
# Transfer files
|
# Transfer files
|
||||||
step "Transferring deployment files"
|
step "Transferring deployment files"
|
||||||
TEMP_DIR="/tmp/fromchat-deploy-$$"
|
|
||||||
ssh "$SERVER" "mkdir -p $TEMP_DIR" > /dev/null 2>&1
|
|
||||||
|
|
||||||
# Copy docker-compose.yml
|
# Ensure destination directory exists with proper permissions
|
||||||
if scp "$DEPLOYMENT_DIR/docker-compose.yml" "$SERVER:$TEMP_DIR/docker-compose.yml" > /dev/null 2>&1; then
|
|
||||||
if [ -n "$SUDO_PASSWORD" ]; then
|
if [ -n "$SUDO_PASSWORD" ]; then
|
||||||
ssh "$SERVER" bash << REMOTE_SUDO_SCRIPT > /dev/null 2>&1
|
ssh "$SERVER" bash << REMOTE_SUDO_SCRIPT > /dev/null 2>&1
|
||||||
set -e
|
set -e
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' mkdir -p $DEPLOY_PATH/deployment 2>/dev/null || true
|
echo '$SUDO_PASSWORD' | sudo -S -p '' mkdir -p $DEPLOY_PATH/deployment 2>/dev/null || true
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' cp $TEMP_DIR/docker-compose.yml $DEPLOY_PATH/deployment/ 2>/dev/null || true
|
echo '$SUDO_PASSWORD' | sudo -S -p '' chown -R \$(whoami):\$(whoami) $DEPLOY_PATH/deployment 2>/dev/null || true
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' chown \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/docker-compose.yml 2>/dev/null || true
|
|
||||||
REMOTE_SUDO_SCRIPT
|
REMOTE_SUDO_SCRIPT
|
||||||
else
|
else
|
||||||
ssh "$SERVER" "sudo mkdir -p $DEPLOY_PATH/deployment && sudo cp $TEMP_DIR/docker-compose.yml $DEPLOY_PATH/deployment/ && sudo chown \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/docker-compose.yml" > /dev/null 2>&1 || true
|
ssh "$SERVER" "sudo mkdir -p $DEPLOY_PATH/deployment && sudo chown -R \$(whoami):\$(whoami) $DEPLOY_PATH/deployment" > /dev/null 2>&1 || true
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy service file
|
# Copy deployment directory excluding gitignored files
|
||||||
scp "$DEPLOYMENT_DIR/fromchat.service" "$SERVER:$TEMP_DIR/fromchat.service" > /dev/null 2>&1 || {
|
cd "$PROJECT_ROOT"
|
||||||
error "Failed to copy fromchat.service"
|
substep "Copying deployment directory..."
|
||||||
}
|
|
||||||
|
|
||||||
if [ -n "$SUDO_PASSWORD" ]; then
|
# Generate exclude file for rsync using git ls-files to list ignored files
|
||||||
ssh "$SERVER" bash << REMOTE_SUDO_SCRIPT > /dev/null 2>&1
|
EXCLUDE_FILE="/tmp/fromchat-rsync-exclude-$$"
|
||||||
set -e
|
RSYNC_ERROR="/tmp/fromchat-rsync-error-$$"
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' mkdir -p $DEPLOY_PATH/deployment 2>/dev/null
|
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' cp $TEMP_DIR/fromchat.service $DEPLOY_PATH/deployment/ 2>/dev/null
|
# Get ignored files in deployment directory and convert to rsync exclude patterns
|
||||||
echo '$SUDO_PASSWORD' | sudo -S -p '' chown \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/fromchat.service 2>/dev/null
|
git ls-files --others --ignored --exclude-standard deployment/ 2>/dev/null | \
|
||||||
REMOTE_SUDO_SCRIPT
|
sed 's|^deployment/||' > "$EXCLUDE_FILE" || true
|
||||||
|
|
||||||
|
# Use rsync with native --exclude-from option
|
||||||
|
if rsync -avz --delete --exclude-from="$EXCLUDE_FILE" \
|
||||||
|
"$DEPLOYMENT_DIR/" \
|
||||||
|
"$SERVER:$DEPLOY_PATH/deployment/" > "$RSYNC_ERROR" 2>&1; then
|
||||||
|
rm -f "$EXCLUDE_FILE" "$RSYNC_ERROR"
|
||||||
else
|
else
|
||||||
ssh "$SERVER" "sudo mkdir -p $DEPLOY_PATH/deployment && sudo cp $TEMP_DIR/fromchat.service $DEPLOY_PATH/deployment/ && sudo chown \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/fromchat.service" > /dev/null 2>&1 || {
|
echo -e " ${RED}✗${NC} Rsync failed. Error output:"
|
||||||
error "Failed to copy fromchat.service"
|
cat "$RSYNC_ERROR" | sed 's/^/ /'
|
||||||
}
|
rm -f "$EXCLUDE_FILE" "$RSYNC_ERROR"
|
||||||
|
echo -n " " && error "Failed to copy deployment directory"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ssh "$SERVER" "rm -rf $TEMP_DIR" > /dev/null 2>&1 || true
|
# Copy .env.prod to .env on server (bypassing gitignore)
|
||||||
|
if [ -f "$DEPLOYMENT_DIR/.env.prod" ]; then
|
||||||
|
substep "Copying .env.prod to .env..."
|
||||||
|
if ! scp "$DEPLOYMENT_DIR/.env.prod" "$SERVER:$DEPLOY_PATH/deployment/.env" > /dev/null 2>&1; then
|
||||||
|
warning "Failed to copy .env.prod to .env"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warning ".env.prod not found in deployment directory"
|
||||||
|
fi
|
||||||
|
|
||||||
# Deploy on server
|
# Deploy on server
|
||||||
step "Deploying on server"
|
step "Deploying on server"
|
||||||
@@ -352,13 +467,12 @@ sudo_cmd systemctl daemon-reload
|
|||||||
sudo_cmd systemctl restart fromchat
|
sudo_cmd systemctl restart fromchat
|
||||||
|
|
||||||
sleep 3
|
sleep 3
|
||||||
if systemctl is-active --quiet fromchat; then
|
if ! systemctl is-active --quiet fromchat; then
|
||||||
echo "✅ Service started"
|
|
||||||
else
|
|
||||||
echo "❌ Service failed to start"
|
echo "❌ Service failed to start"
|
||||||
sudo_cmd journalctl --no-pager -xeu fromchat -n 30
|
sudo_cmd journalctl --no-pager -xeu fromchat -n 30
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
REMOTE_SCRIPT
|
REMOTE_SCRIPT
|
||||||
|
|
||||||
|
echo
|
||||||
success "Deployment complete!"
|
success "Deployment complete!"
|
||||||
|
|||||||
Reference in New Issue
Block a user