mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 11:05:05 +03:00
Test new production deployment
This commit is contained in:
@@ -3,17 +3,17 @@ name: Deploy to server
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
# Runs on pushes targeting the default branch
|
# Runs on pushes targeting the default branch
|
||||||
push:
|
# push:
|
||||||
branches: ["main"]
|
# branches: ["main"]
|
||||||
paths:
|
# paths:
|
||||||
- "backend/**"
|
# - "backend/**"
|
||||||
- "frontend/**"
|
# - "frontend/**"
|
||||||
- "deployment/**"
|
# - "deployment/**"
|
||||||
- "**/package.json"
|
# - "**/package.json"
|
||||||
- ".nvmrc"
|
# - ".nvmrc"
|
||||||
- ".github/workflows/deploy.yml"
|
# - ".github/workflows/deploy.yml"
|
||||||
- "!frontend/electron/**"
|
# - "!frontend/electron/**"
|
||||||
- "!**.d.ts"
|
# - "!**.d.ts"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||||
|
|||||||
Executable
+116
@@ -0,0 +1,116 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Post-push hook: opens deploy command in system's native terminal
|
||||||
|
# Cross-platform support: macOS, Linux, Windows, WSL
|
||||||
|
|
||||||
|
# Get the project root directory
|
||||||
|
PROJECT_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$PROJECT_ROOT" || exit 1
|
||||||
|
|
||||||
|
# Command to run in terminal (deploy.sh will load .env from project root)
|
||||||
|
COMMAND="npm run -s deploy"
|
||||||
|
|
||||||
|
# Detect OS and open appropriate terminal
|
||||||
|
detect_and_open_terminal() {
|
||||||
|
# Detect WSL
|
||||||
|
if [ -n "${WSL_DISTRO_NAME:-}" ] || [ -f /proc/version ] && grep -qi microsoft /proc/version 2>/dev/null; then
|
||||||
|
# WSL detected - try to open Windows Terminal, fallback to Linux terminals
|
||||||
|
if command -v wt.exe >/dev/null 2>&1; then
|
||||||
|
# Windows Terminal (preferred for WSL)
|
||||||
|
ESCAPED_PATH=$(echo "$PROJECT_ROOT" | sed "s/'/'\"'\"'/g")
|
||||||
|
ESCAPED_CMD=$(echo "$COMMAND" | sed "s/'/'\"'\"'/g")
|
||||||
|
wt.exe bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v wsl.exe >/dev/null 2>&1; then
|
||||||
|
# Fallback: use wsl.exe to open cmd
|
||||||
|
WINDOWS_PATH=$(wslpath -w "$PROJECT_ROOT" 2>/dev/null || echo "$PROJECT_ROOT")
|
||||||
|
cmd.exe /c "start cmd /k \"cd /d $WINDOWS_PATH && $COMMAND\""
|
||||||
|
else
|
||||||
|
# Fallback to Linux terminal
|
||||||
|
open_linux_terminal
|
||||||
|
fi
|
||||||
|
# macOS
|
||||||
|
elif [ "$(uname)" = "Darwin" ]; then
|
||||||
|
# macOS - use .command file with open command
|
||||||
|
# Clean up old script files and create a new one
|
||||||
|
rm -f /tmp/post-push-deploy-*.command 2>/dev/null
|
||||||
|
SCRIPT_FILE=$(mktemp /tmp/post-push-deploy-XXXXXX.command 2>/dev/null)
|
||||||
|
if [ -z "$SCRIPT_FILE" ] || [ ! -f "$SCRIPT_FILE" ]; then
|
||||||
|
# Fallback if mktemp fails
|
||||||
|
SCRIPT_FILE="/tmp/post-push-deploy-$$.command"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "#!/bin/bash"
|
||||||
|
echo "clear"
|
||||||
|
echo "cd '$PROJECT_ROOT'"
|
||||||
|
echo "export PS1=''"
|
||||||
|
echo "set +x"
|
||||||
|
# Export DEPLOYMENT_SERVER if it was set in the hook environment
|
||||||
|
if [ -n "$DEPLOYMENT_SERVER_VALUE" ]; then
|
||||||
|
echo "export DEPLOYMENT_SERVER='$DEPLOYMENT_SERVER_VALUE'"
|
||||||
|
fi
|
||||||
|
echo "$COMMAND"
|
||||||
|
echo "echo ''"
|
||||||
|
echo "echo 'Press Enter to close...'"
|
||||||
|
echo "read -r"
|
||||||
|
echo "osascript -e 'tell application \"Terminal\" to close front window' &"
|
||||||
|
} > "$SCRIPT_FILE"
|
||||||
|
chmod +x "$SCRIPT_FILE"
|
||||||
|
# Use open command to launch .command file - opens only one Terminal window
|
||||||
|
open "$SCRIPT_FILE"
|
||||||
|
# Windows (Git Bash or similar)
|
||||||
|
elif [ -n "${MSYSTEM:-}" ] || [ -n "${MINGW64:-}" ] || [ -n "${MINGW32:-}" ]; then
|
||||||
|
# Git Bash on Windows
|
||||||
|
if command -v wt.exe >/dev/null 2>&1; then
|
||||||
|
# Windows Terminal
|
||||||
|
ESCAPED_PATH=$(echo "$PROJECT_ROOT" | sed "s/'/'\"'\"'/g")
|
||||||
|
ESCAPED_CMD=$(echo "$COMMAND" | sed "s/'/'\"'\"'/g")
|
||||||
|
wt.exe bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v cmd.exe >/dev/null 2>&1; then
|
||||||
|
# Command Prompt - convert path to Windows format
|
||||||
|
WINDOWS_PATH=$(echo "$PROJECT_ROOT" | sed 's|^/\([a-z]\)|\1:|' | sed 's|/|\\|g' | sed 's|\\|\\\\|g')
|
||||||
|
cmd.exe /c "start cmd /k \"cd /d $WINDOWS_PATH && $COMMAND\""
|
||||||
|
else
|
||||||
|
# Fallback
|
||||||
|
ESCAPED_PATH=$(echo "$PROJECT_ROOT" | sed "s/'/'\"'\"'/g")
|
||||||
|
start "Deploy" bash -c "cd '$ESCAPED_PATH' && $COMMAND; exec bash"
|
||||||
|
fi
|
||||||
|
# Linux
|
||||||
|
else
|
||||||
|
open_linux_terminal
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
open_linux_terminal() {
|
||||||
|
# Escape path for use in shell commands
|
||||||
|
ESCAPED_PATH=$(echo "$PROJECT_ROOT" | sed "s/'/'\"'\"'/g")
|
||||||
|
|
||||||
|
ESCAPED_CMD=$(echo "$COMMAND" | sed "s/'/'\"'\"'/g")
|
||||||
|
# Try different Linux terminal emulators
|
||||||
|
if command -v gnome-terminal >/dev/null 2>&1; then
|
||||||
|
gnome-terminal -- bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v x-terminal-emulator >/dev/null 2>&1; then
|
||||||
|
x-terminal-emulator -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v konsole >/dev/null 2>&1; then
|
||||||
|
konsole -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v xterm >/dev/null 2>&1; then
|
||||||
|
xterm -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v alacritty >/dev/null 2>&1; then
|
||||||
|
alacritty -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v kitty >/dev/null 2>&1; then
|
||||||
|
kitty bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
elif command -v tilix >/dev/null 2>&1; then
|
||||||
|
tilix -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
else
|
||||||
|
# Last resort: try to find any terminal
|
||||||
|
TERMINAL=$(command -v x-terminal-emulator gnome-terminal konsole xterm alacritty kitty tilix 2>/dev/null | head -1)
|
||||||
|
if [ -n "$TERMINAL" ]; then
|
||||||
|
"$TERMINAL" -e bash -c "cd '$ESCAPED_PATH' && set +x && echo 'Post-push: Running deploy...' && $ESCAPED_CMD && echo '' && echo 'Deploy completed. Press Enter to close...' && read -r && exit"
|
||||||
|
else
|
||||||
|
echo "Could not find a terminal emulator. Please run manually: cd '$PROJECT_ROOT' && $COMMAND"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run in background so git push doesn't wait
|
||||||
|
# Add a small delay to ensure git push completes first
|
||||||
|
(sleep 0.5 && detect_and_open_terminal) &
|
||||||
|
|
||||||
Vendored
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
"**/__pycache__": true,
|
"**/__pycache__": true,
|
||||||
"**/package-lock.json": true,
|
"**/package-lock.json": true,
|
||||||
"**/*.module.scss.d.ts": true,
|
"**/*.module.scss.d.ts": true,
|
||||||
"**/.husky": true,
|
"**/.husky/_": true,
|
||||||
"**/.venv": true,
|
"**/.venv": true,
|
||||||
"**/node_modules": true
|
"**/node_modules": true
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+14
-1
@@ -33,7 +33,7 @@
|
|||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build"
|
||||||
},
|
},
|
||||||
"isBackground": true
|
"isBackground": true
|
||||||
},
|
},
|
||||||
@@ -87,6 +87,19 @@
|
|||||||
"focus": false,
|
"focus": false,
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Pre-commit checks",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm run frontend:typecheck",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": true,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"problemMatcher": "$tsc"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+4
-2
@@ -27,9 +27,11 @@
|
|||||||
"preview": "cd deployment && docker compose up --build --watch",
|
"preview": "cd deployment && docker compose up --build --watch",
|
||||||
"preview:clean": "cd deployment && docker compose down -v",
|
"preview:clean": "cd deployment && docker compose down -v",
|
||||||
"clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean",
|
"clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean",
|
||||||
"install": "npm run backend:dependencies && npm run generate:env",
|
"install": "npm run backend:dependencies && if [[ ! -f deployment/.env ]]; then npm run generate:env; fi && npm run install:pussh",
|
||||||
|
"install:pussh": "bash ./scripts/install:pussh.sh",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"generate:env": "bash ./scripts/generate:env.sh"
|
"generate:env": "bash ./scripts/generate:env.sh",
|
||||||
|
"deploy": "bash ./scripts/deploy.sh"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"frontend/build/electron"
|
"frontend/build/electron"
|
||||||
|
|||||||
Executable
+364
@@ -0,0 +1,364 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Complete deployment script: build and push to server
|
||||||
|
# Usage: ./scripts/deploy.sh [server_user@server_host] [deployment_path] [platform]
|
||||||
|
# Example: ./scripts/deploy.sh user@example.com /home/user/fromchat linux/arm64
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
MAGENTA='\033[0;35m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
BOLD='\033[1m'
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
||||||
|
success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||||
|
warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}✗${NC} $1"; exit 1; }
|
||||||
|
step() { echo -e "${CYAN}${BOLD}→${NC} ${BOLD}$1${NC}"; }
|
||||||
|
substep() {
|
||||||
|
if [ "$2" = "-n" ]; then
|
||||||
|
echo -n -e " ${GREEN}•${NC} $1"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}•${NC} $1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
DEPLOYMENT_DIR="$PROJECT_ROOT/deployment"
|
||||||
|
ENV_FILE="$DEPLOYMENT_DIR/.env"
|
||||||
|
|
||||||
|
# Load .env file if it exists
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
# Export variables from .env file (ignore comments and empty lines)
|
||||||
|
set -a
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Skip comments and empty lines
|
||||||
|
case "$line" in
|
||||||
|
\#*|'') continue ;;
|
||||||
|
*)
|
||||||
|
# Export the variable
|
||||||
|
export "$line" 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read server from environment variable (from .env), command line argument, or fallback
|
||||||
|
SERVER="${1:-${DEPLOYMENT_SERVER:-}}"
|
||||||
|
DEPLOY_PATH="${2:-${DEPLOY_PATH:-/home/denis0001-dev/actions-runner/_work/FromChat/FromChat}}"
|
||||||
|
PLATFORM="${3:-linux/arm64}"
|
||||||
|
|
||||||
|
# Check if server is provided
|
||||||
|
if [ -z "$SERVER" ]; then
|
||||||
|
error "Server not specified. Usage: $0 [user@host] [deployment_path] [platform]"
|
||||||
|
echo " Or set DEPLOYMENT_SERVER in $ENV_FILE or as an environment variable"
|
||||||
|
echo ""
|
||||||
|
echo "Example:"
|
||||||
|
echo " $0 user@example.com /home/user/fromchat linux/arm64"
|
||||||
|
echo " Or add to $ENV_FILE: DEPLOYMENT_SERVER=user@example.com"
|
||||||
|
echo " Or: DEPLOYMENT_SERVER=user@example.com $0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${MAGENTA}${BOLD}🚀 Deployment${NC}\n"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BUILD PHASE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo -e "${MAGENTA}${BOLD}🔨 Building Docker images${NC}"
|
||||||
|
|
||||||
|
# Determine project name
|
||||||
|
if [ -n "$SERVER" ]; then
|
||||||
|
COMPOSE_DIR=$(ssh "$SERVER" "dirname $DEPLOY_PATH/deployment/docker-compose.yml" 2>/dev/null || echo "$DEPLOY_PATH/deployment")
|
||||||
|
PROJECT_NAME=$(ssh "$SERVER" "basename $COMPOSE_DIR" 2>/dev/null || echo "deployment")
|
||||||
|
else
|
||||||
|
PROJECT_NAME=$(basename "$DEPLOYMENT_DIR")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check buildx
|
||||||
|
if ! docker buildx version > /dev/null 2>&1; then
|
||||||
|
error "Docker buildx not available. Install Docker Desktop."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup buildx builder
|
||||||
|
step "Setting up buildx builder"
|
||||||
|
BUILDER_NAME="fromchat-builder"
|
||||||
|
BUILDER_EXISTS=false
|
||||||
|
|
||||||
|
if docker buildx inspect "$BUILDER_NAME" > /dev/null 2>&1; then
|
||||||
|
BUILDER_EXISTS=true
|
||||||
|
if ! docker buildx use "$BUILDER_NAME" > /dev/null 2>&1; then
|
||||||
|
substep "Recreating builder..."
|
||||||
|
docker buildx rm "$BUILDER_NAME" > /dev/null 2>&1 || true
|
||||||
|
BUILDER_EXISTS=false
|
||||||
|
elif ! docker buildx inspect "$BUILDER_NAME" > /dev/null 2>&1; then
|
||||||
|
substep "Recreating builder (inspection failed)..."
|
||||||
|
docker buildx rm "$BUILDER_NAME" > /dev/null 2>&1 || true
|
||||||
|
BUILDER_EXISTS=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$BUILDER_EXISTS" = false ]; then
|
||||||
|
substep "Creating builder with persistent cache..."
|
||||||
|
docker buildx create \
|
||||||
|
--name "$BUILDER_NAME" \
|
||||||
|
--driver docker-container \
|
||||||
|
--driver-opt image=moby/buildkit:latest \
|
||||||
|
--use \
|
||||||
|
--bootstrap > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker buildx use "$BUILDER_NAME" > /dev/null 2>&1
|
||||||
|
|
||||||
|
# Detect services
|
||||||
|
step "Detecting services"
|
||||||
|
cd "$DEPLOYMENT_DIR"
|
||||||
|
SERVICES=$(docker compose -f docker-compose.yml config --services 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$SERVICES" ]; then
|
||||||
|
error "No services found in docker-compose.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUILT_IMAGES=()
|
||||||
|
|
||||||
|
for SERVICE in $SERVICES; do
|
||||||
|
HAS_BUILD=$(docker compose -f docker-compose.yml config 2>/dev/null | \
|
||||||
|
grep -A 30 "^[[:space:]]*${SERVICE}:" | \
|
||||||
|
grep -q "build:" && echo "yes" || echo "no")
|
||||||
|
|
||||||
|
if [ "$HAS_BUILD" != "yes" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
IMAGE_TAG="${PROJECT_NAME}-${SERVICE}:latest"
|
||||||
|
|
||||||
|
substep "Building ${CYAN}$SERVICE${NC} -> ${CYAN}$IMAGE_TAG${NC}..."
|
||||||
|
|
||||||
|
BUILD_OUTPUT=$(docker compose -f docker-compose.yml config 2>/dev/null | \
|
||||||
|
grep -A 15 "^[[:space:]]*${SERVICE}:" | \
|
||||||
|
grep -A 10 "build:")
|
||||||
|
|
||||||
|
DOCKERFILE_REL=$(echo "$BUILD_OUTPUT" | grep "dockerfile:" | \
|
||||||
|
sed 's/.*dockerfile:[[:space:]]*\(.*\)/\1/' | \
|
||||||
|
tr -d '"' | tr -d "'" | xargs)
|
||||||
|
|
||||||
|
CONTEXT_REL=$(echo "$BUILD_OUTPUT" | grep "context:" | \
|
||||||
|
sed 's/.*context:[[:space:]]*\(.*\)/\1/' | \
|
||||||
|
tr -d '"' | tr -d "'" | xargs)
|
||||||
|
|
||||||
|
if [ -z "$CONTEXT_REL" ]; then
|
||||||
|
CONTEXT_REL=".."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CONTEXT_REL" == ".." ]]; then
|
||||||
|
BUILD_CONTEXT="$PROJECT_ROOT"
|
||||||
|
elif [[ "$CONTEXT_REL" == /* ]]; then
|
||||||
|
BUILD_CONTEXT="$CONTEXT_REL"
|
||||||
|
else
|
||||||
|
BUILD_CONTEXT="$DEPLOYMENT_DIR/$CONTEXT_REL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DOCKERFILE_REL" ]; then
|
||||||
|
if [[ "$DOCKERFILE_REL" == /* ]]; then
|
||||||
|
DOCKERFILE="$DOCKERFILE_REL"
|
||||||
|
else
|
||||||
|
if [[ "$CONTEXT_REL" == ".." ]] || [[ "$BUILD_CONTEXT" == "$PROJECT_ROOT" ]]; then
|
||||||
|
DOCKERFILE="$PROJECT_ROOT/$DOCKERFILE_REL"
|
||||||
|
else
|
||||||
|
DOCKERFILE="$BUILD_CONTEXT/$DOCKERFILE_REL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -f "$DEPLOYMENT_DIR/Dockerfile.$SERVICE" ]; then
|
||||||
|
DOCKERFILE="$DEPLOYMENT_DIR/Dockerfile.$SERVICE"
|
||||||
|
elif [ -f "$DEPLOYMENT_DIR/$SERVICE/Dockerfile" ]; then
|
||||||
|
DOCKERFILE="$DEPLOYMENT_DIR/$SERVICE/Dockerfile"
|
||||||
|
else
|
||||||
|
error "Could not determine Dockerfile for $SERVICE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if docker buildx build \
|
||||||
|
--platform "$PLATFORM" \
|
||||||
|
--file "$DOCKERFILE" \
|
||||||
|
--tag "$IMAGE_TAG" \
|
||||||
|
--load \
|
||||||
|
"$BUILD_CONTEXT"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Built ${CYAN}$SERVICE${NC}"
|
||||||
|
BUILT_IMAGES+=("$IMAGE_TAG")
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
error "Build failed for $SERVICE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
success "Build complete! ${#BUILT_IMAGES[@]} image(s) ready"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEPLOY PHASE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
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
|
||||||
|
if ! docker pussh --help > /dev/null 2>&1; then
|
||||||
|
error "docker pussh plugin not installed"
|
||||||
|
echo " Install: npm run install:pussh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect images
|
||||||
|
IMAGES=($(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${PROJECT_NAME}-" || true))
|
||||||
|
|
||||||
|
if [ ${#IMAGES[@]} -eq 0 ]; then
|
||||||
|
error "No ${PROJECT_NAME} images found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pre-pull unregistry image if needed
|
||||||
|
UNREGISTRY_IMAGE="ghcr.io/psviderski/unregistry:0.3.1"
|
||||||
|
if ! ssh "$SERVER" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -q '^${UNREGISTRY_IMAGE}$'" 2>/dev/null; then
|
||||||
|
substep "Pulling unregistry image (one-time setup)..."
|
||||||
|
ssh "$SERVER" "docker pull ${UNREGISTRY_IMAGE}" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Transfer images
|
||||||
|
step "Transferring images"
|
||||||
|
PUSH_FAILED=0
|
||||||
|
for IMAGE in "${IMAGES[@]}"; do
|
||||||
|
substep "Pushing ${CYAN}$IMAGE${NC}..."
|
||||||
|
if docker pussh "$IMAGE" "$SERVER"; then
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Failed to push ${CYAN}$IMAGE${NC}"
|
||||||
|
PUSH_FAILED=1
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $PUSH_FAILED -eq 1 ]; then
|
||||||
|
error "Image transfer failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Transfer files
|
||||||
|
step "Transferring deployment files"
|
||||||
|
TEMP_DIR="/tmp/fromchat-deploy-$$"
|
||||||
|
ssh "$SERVER" "mkdir -p $TEMP_DIR" > /dev/null 2>&1
|
||||||
|
|
||||||
|
# Copy docker-compose.yml
|
||||||
|
if scp "$DEPLOYMENT_DIR/docker-compose.yml" "$SERVER:$TEMP_DIR/docker-compose.yml" > /dev/null 2>&1; then
|
||||||
|
if [ -n "$SUDO_PASSWORD" ]; then
|
||||||
|
ssh "$SERVER" bash << REMOTE_SUDO_SCRIPT > /dev/null 2>&1
|
||||||
|
set -e
|
||||||
|
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 \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/docker-compose.yml 2>/dev/null || true
|
||||||
|
REMOTE_SUDO_SCRIPT
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy service file
|
||||||
|
scp "$DEPLOYMENT_DIR/fromchat.service" "$SERVER:$TEMP_DIR/fromchat.service" > /dev/null 2>&1 || {
|
||||||
|
error "Failed to copy fromchat.service"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$SUDO_PASSWORD" ]; then
|
||||||
|
ssh "$SERVER" bash << REMOTE_SUDO_SCRIPT > /dev/null 2>&1
|
||||||
|
set -e
|
||||||
|
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
|
||||||
|
echo '$SUDO_PASSWORD' | sudo -S -p '' chown \$(whoami):\$(whoami) $DEPLOY_PATH/deployment/fromchat.service 2>/dev/null
|
||||||
|
REMOTE_SUDO_SCRIPT
|
||||||
|
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 || {
|
||||||
|
error "Failed to copy fromchat.service"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
ssh "$SERVER" "rm -rf $TEMP_DIR" > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Deploy on server
|
||||||
|
step "Deploying on server"
|
||||||
|
ssh "$SERVER" SUDO_PASSWORD="$SUDO_PASSWORD" DEPLOY_PATH="$DEPLOY_PATH" bash << 'REMOTE_SCRIPT'
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REMOTE_SUDO_PASS="${SUDO_PASSWORD:-}"
|
||||||
|
REMOTE_DEPLOY_PATH="${DEPLOY_PATH:-}"
|
||||||
|
export SUDO_PROMPT=""
|
||||||
|
|
||||||
|
sudo_cmd() {
|
||||||
|
if [ -n "$REMOTE_SUDO_PASS" ]; then
|
||||||
|
echo "$REMOTE_SUDO_PASS" | sudo -S -p '' "$@" 2>/dev/null
|
||||||
|
else
|
||||||
|
sudo "$@" 2>/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$REMOTE_DEPLOY_PATH" ]; then
|
||||||
|
echo "❌ DEPLOY_PATH is not set"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$REMOTE_DEPLOY_PATH/deployment"
|
||||||
|
cd "$REMOTE_DEPLOY_PATH/deployment"
|
||||||
|
|
||||||
|
if [ ! -f "$REMOTE_DEPLOY_PATH/deployment/.env" ]; then
|
||||||
|
echo "⚠️ Warning: .env file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-active --quiet fromchat; then
|
||||||
|
sudo_cmd systemctl stop fromchat
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose down > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
sudo_cmd cp -f "$REMOTE_DEPLOY_PATH/deployment/fromchat.service" /etc/systemd/system/fromchat.service
|
||||||
|
sudo_cmd systemctl daemon-reload
|
||||||
|
sudo_cmd systemctl restart fromchat
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
if systemctl is-active --quiet fromchat; then
|
||||||
|
echo "✅ Service started"
|
||||||
|
else
|
||||||
|
echo "❌ Service failed to start"
|
||||||
|
sudo_cmd journalctl --no-pager -xeu fromchat -n 30
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
REMOTE_SCRIPT
|
||||||
|
|
||||||
|
success "Deployment complete!"
|
||||||
@@ -8,4 +8,5 @@ cat >> deployment/.env <<EOF
|
|||||||
JWT_SECRET="$(openssl rand -base64 32)"
|
JWT_SECRET="$(openssl rand -base64 32)"
|
||||||
TURN_USERNAME=<set>
|
TURN_USERNAME=<set>
|
||||||
TURN_SECRET=<set>
|
TURN_SECRET=<set>
|
||||||
|
DEPLOYMENT_SERVER=<set>
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Don't use set -e here - we want to continue if Homebrew installation fails
|
||||||
|
|
||||||
|
# Install docker-pussh plugin for Docker CLI
|
||||||
|
# This script installs the unregistry docker-pussh plugin
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo "📦 Installing docker-pussh plugin..."
|
||||||
|
|
||||||
|
# Check if Docker is installed
|
||||||
|
if ! command -v docker > /dev/null 2>&1; then
|
||||||
|
echo "⚠️ Docker is not installed. Skipping docker-pussh installation."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create docker plugins directory if it doesn't exist
|
||||||
|
PLUGIN_DIR="$HOME/.docker/cli-plugins"
|
||||||
|
mkdir -p "$PLUGIN_DIR"
|
||||||
|
|
||||||
|
# Check if already installed
|
||||||
|
if [ -f "$PLUGIN_DIR/docker-pussh" ] && docker pussh --help > /dev/null 2>&1; then
|
||||||
|
echo " ✓ docker-pussh is already installed"
|
||||||
|
docker pussh --version 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try installing via Homebrew first
|
||||||
|
if command -v brew > /dev/null 2>&1; then
|
||||||
|
echo " Attempting installation via Homebrew..."
|
||||||
|
if brew install psviderski/tap/docker-pussh 2>/dev/null; then
|
||||||
|
# Create symlink to use as Docker CLI plugin
|
||||||
|
BREW_PREFIX=$(brew --prefix 2>/dev/null || echo "/opt/homebrew")
|
||||||
|
if [ -f "$BREW_PREFIX/bin/docker-pussh" ]; then
|
||||||
|
mkdir -p "$PLUGIN_DIR"
|
||||||
|
ln -sf "$BREW_PREFIX/bin/docker-pussh" "$PLUGIN_DIR/docker-pussh" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if docker pussh --help > /dev/null 2>&1; then
|
||||||
|
echo " ✓ docker-pussh installed successfully via Homebrew"
|
||||||
|
docker pussh --version 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo " ⚠️ Homebrew installation failed or incomplete, trying direct download..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback: Download and install docker-pussh directly (using latest from main branch)
|
||||||
|
echo " Downloading docker-pussh from unregistry..."
|
||||||
|
if curl -sSL https://raw.githubusercontent.com/psviderski/unregistry/main/docker-pussh \
|
||||||
|
-o "$PLUGIN_DIR/docker-pussh" 2>/dev/null; then
|
||||||
|
chmod +x "$PLUGIN_DIR/docker-pussh"
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if docker pussh --help > /dev/null 2>&1; then
|
||||||
|
echo " ✓ docker-pussh installed successfully"
|
||||||
|
docker pussh --version 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo " ⚠️ Installation completed but plugin verification failed"
|
||||||
|
echo " You may need to restart your terminal or Docker Desktop"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ⚠️ Failed to download docker-pussh"
|
||||||
|
echo " You can install it manually:"
|
||||||
|
echo ""
|
||||||
|
echo " Via Homebrew:"
|
||||||
|
echo " brew install psviderski/tap/docker-pussh"
|
||||||
|
echo " mkdir -p ~/.docker/cli-plugins"
|
||||||
|
echo " ln -sf \$(brew --prefix)/bin/docker-pussh ~/.docker/cli-plugins/docker-pussh"
|
||||||
|
echo ""
|
||||||
|
echo " Or via direct download:"
|
||||||
|
echo " mkdir -p ~/.docker/cli-plugins"
|
||||||
|
echo " curl -sSL https://raw.githubusercontent.com/psviderski/unregistry/main/docker-pussh \\"
|
||||||
|
echo " -o ~/.docker/cli-plugins/docker-pussh"
|
||||||
|
echo " chmod +x ~/.docker/cli-plugins/docker-pussh"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Transfer unregistry image from local machine to server
|
||||||
|
# Usage: ./scripts/transfer:unregistry.sh [server_user@server_host]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVER="${1:-${DEPLOY_SERVER:-}}"
|
||||||
|
|
||||||
|
if [ -z "$SERVER" ]; then
|
||||||
|
echo "❌ Server not specified. Usage: $0 [user@host]"
|
||||||
|
echo " Or set DEPLOY_SERVER environment variable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📦 Transferring unregistry image to $SERVER..."
|
||||||
|
|
||||||
|
# Pull the image locally if not already present
|
||||||
|
if ! docker images ghcr.io/psviderski/unregistry:0.3.1 --format "{{.Repository}}:{{.Tag}}" | grep -q "unregistry:0.3.1"; then
|
||||||
|
echo " Pulling unregistry:0.3.1 locally..."
|
||||||
|
docker pull ghcr.io/psviderski/unregistry:0.3.1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save and transfer the image
|
||||||
|
echo " Saving and transferring image to server..."
|
||||||
|
docker save ghcr.io/psviderski/unregistry:0.3.1 | ssh "$SERVER" "docker load"
|
||||||
|
|
||||||
|
echo " ✓ Unregistry image transferred successfully"
|
||||||
|
echo ""
|
||||||
|
echo " Now you can run on the server:"
|
||||||
|
echo " docker run -d \\"
|
||||||
|
echo " --name unregistry \\"
|
||||||
|
echo " -p 5000:5000 \\"
|
||||||
|
echo " -v /run/containerd/containerd.sock:/run/containerd/containerd.sock \\"
|
||||||
|
echo " --restart unless-stopped \\"
|
||||||
|
echo " ghcr.io/psviderski/unregistry:0.3.1"
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user