- Updated `deploy-ubuntu.sh` to allow users to choose the installation directory dynamically. - Added checks for required files (`package.json`, `requirements.txt`, and `migrate.py`) with appropriate error messages. - Improved feedback during the installation process, including detailed logging for service status and troubleshooting steps. - Enhanced user experience with clearer prompts and success/error messages throughout the deployment process.
405 lines
14 KiB
Bash
405 lines
14 KiB
Bash
#!/bin/bash
|
||
# Скрипт развертывания AI Code Review Platform на Ubuntu с systemd
|
||
|
||
set -e
|
||
|
||
# Цвета для вывода
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Проверка что скрипт запущен с sudo
|
||
if [ "$EUID" -ne 0 ]; then
|
||
echo -e "${RED}Ошибка: Этот скрипт должен быть запущен с sudo${NC}"
|
||
echo "Используйте: sudo ./deploy-ubuntu.sh"
|
||
exit 1
|
||
fi
|
||
|
||
# Получаем имя реального пользователя (не root)
|
||
REAL_USER=${SUDO_USER:-$USER}
|
||
REAL_HOME=$(eval echo ~$REAL_USER)
|
||
|
||
echo -e "${BLUE}"
|
||
echo "========================================="
|
||
echo " AI Code Review Platform - Установка"
|
||
echo "========================================="
|
||
echo -e "${NC}"
|
||
|
||
# 1. Проверка зависимостей
|
||
echo -e "${YELLOW}[1/10] Проверка системных зависимостей...${NC}"
|
||
|
||
# Проверка Python
|
||
if ! command -v python3 &> /dev/null; then
|
||
echo -e "${RED}Python 3 не установлен!${NC}"
|
||
echo "Установка Python 3..."
|
||
apt-get update
|
||
apt-get install -y python3 python3-pip python3-venv
|
||
fi
|
||
|
||
# Проверка Node.js
|
||
if ! command -v node &> /dev/null; then
|
||
echo -e "${RED}Node.js не установлен!${NC}"
|
||
echo "Установка Node.js..."
|
||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||
apt-get install -y nodejs
|
||
fi
|
||
|
||
# Установка nginx (опционально)
|
||
if ! command -v nginx &> /dev/null; then
|
||
echo -e "${YELLOW}Установить nginx как reverse proxy? [y/N]${NC}"
|
||
read -r install_nginx
|
||
if [[ "$install_nginx" =~ ^[Yy]$ ]]; then
|
||
apt-get install -y nginx
|
||
fi
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ Зависимости проверены${NC}"
|
||
|
||
# 2. Определение пути установки
|
||
echo -e "${YELLOW}[2/10] Настройка путей...${NC}"
|
||
|
||
# Определить, откуда запущен скрипт
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
CURRENT_DIR="$SCRIPT_DIR"
|
||
|
||
# Спросить пользователя, куда устанавливать
|
||
echo "Текущая директория: $CURRENT_DIR"
|
||
echo ""
|
||
echo "Выберите директорию установки:"
|
||
echo " 1) Использовать текущую директорию ($CURRENT_DIR)"
|
||
echo " 2) Установить в /opt/ai-review"
|
||
echo ""
|
||
read -p "Выбор [1/2] (по умолчанию 1): " install_choice
|
||
install_choice=${install_choice:-1}
|
||
|
||
if [ "$install_choice" = "2" ]; then
|
||
INSTALL_DIR="/opt/ai-review"
|
||
NEED_COPY=true
|
||
else
|
||
INSTALL_DIR="$CURRENT_DIR"
|
||
NEED_COPY=false
|
||
fi
|
||
|
||
echo "Директория установки: $INSTALL_DIR"
|
||
|
||
# 3. Копирование файлов (если нужно)
|
||
echo -e "${YELLOW}[3/10] Подготовка файлов проекта...${NC}"
|
||
|
||
if [ "$NEED_COPY" = true ]; then
|
||
echo "Копирование в $INSTALL_DIR..."
|
||
mkdir -p "$INSTALL_DIR"
|
||
cp -r "$CURRENT_DIR"/* "$INSTALL_DIR"/ || {
|
||
echo -e "${RED}✗ Ошибка копирования файлов${NC}"
|
||
exit 1
|
||
}
|
||
chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR"
|
||
echo -e "${GREEN}✓ Файлы скопированы${NC}"
|
||
else
|
||
echo "Используем текущую директорию"
|
||
chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR"
|
||
echo -e "${GREEN}✓ Права установлены${NC}"
|
||
fi
|
||
|
||
cd "$INSTALL_DIR"
|
||
|
||
# 4. Сборка frontend
|
||
echo -e "${YELLOW}[4/10] Сборка frontend...${NC}"
|
||
cd frontend
|
||
|
||
# Проверить наличие package.json
|
||
if [ ! -f "package.json" ]; then
|
||
echo -e "${RED}✗ Не найден package.json${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
# Создаем .env.production для относительных путей
|
||
cat > .env.production << 'EOF'
|
||
# Production - используем относительные пути
|
||
VITE_API_URL=/api
|
||
VITE_WS_URL=
|
||
EOF
|
||
|
||
echo "Установка npm зависимостей..."
|
||
sudo -u "$REAL_USER" npm install || {
|
||
echo -e "${RED}✗ Ошибка установки npm зависимостей${NC}"
|
||
exit 1
|
||
}
|
||
|
||
echo "Сборка frontend..."
|
||
sudo -u "$REAL_USER" npm run build || {
|
||
echo -e "${RED}✗ Ошибка сборки frontend${NC}"
|
||
exit 1
|
||
}
|
||
|
||
# Проверить, что build создан
|
||
if [ ! -d "../backend/public" ]; then
|
||
echo -e "${RED}✗ Frontend не собрался (отсутствует backend/public)${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
cd ..
|
||
echo -e "${GREEN}✓ Frontend собран (файлов: $(ls -1 backend/public | wc -l))${NC}"
|
||
|
||
# 5. Установка Python зависимостей
|
||
echo -e "${YELLOW}[5/10] Установка Python зависимостей...${NC}"
|
||
cd backend
|
||
|
||
# Проверить наличие requirements.txt
|
||
if [ ! -f "requirements.txt" ]; then
|
||
echo -e "${RED}✗ Не найден requirements.txt${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Создание Python virtual environment..."
|
||
sudo -u "$REAL_USER" python3 -m venv venv || {
|
||
echo -e "${RED}✗ Ошибка создания venv${NC}"
|
||
exit 1
|
||
}
|
||
|
||
# Проверить, что venv создан
|
||
if [ ! -d "venv" ]; then
|
||
echo -e "${RED}✗ venv не создан${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Установка Python пакетов..."
|
||
sudo -u "$REAL_USER" bash -c "source venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt" || {
|
||
echo -e "${RED}✗ Ошибка установки Python зависимостей${NC}"
|
||
exit 1
|
||
}
|
||
|
||
echo -e "${GREEN}✓ Python зависимости установлены${NC}"
|
||
|
||
# Применить миграции БД
|
||
echo "Применение миграций БД..."
|
||
if [ -f "migrate.py" ]; then
|
||
sudo -u "$REAL_USER" bash -c "source venv/bin/activate && python migrate.py" || {
|
||
echo -e "${YELLOW}⚠️ Предупреждение: миграции не применились${NC}"
|
||
}
|
||
|
||
# Проверить, что БД создана
|
||
if [ -f "review.db" ]; then
|
||
echo -e "${GREEN}✓ База данных создана${NC}"
|
||
else
|
||
echo -e "${YELLOW}⚠️ База данных будет создана при первом запуске${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠️ Скрипт миграции не найден, БД будет создана автоматически${NC}"
|
||
fi
|
||
|
||
cd ..
|
||
echo -e "${GREEN}✓ Backend настроен${NC}"
|
||
|
||
# 6. Настройка .env
|
||
echo -e "${YELLOW}[6/10] Настройка конфигурации...${NC}"
|
||
|
||
if [ ! -f "backend/.env" ]; then
|
||
echo "Создание .env файла..."
|
||
|
||
# Генерируем ключ шифрования
|
||
ENCRYPTION_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
|
||
|
||
cat > backend/.env << EOF
|
||
# Ollama Configuration
|
||
OLLAMA_BASE_URL=http://localhost:11434
|
||
OLLAMA_MODEL=mistral:7b
|
||
|
||
# Database
|
||
DATABASE_URL=sqlite+aiosqlite:///$INSTALL_DIR/backend/review.db
|
||
|
||
# Security
|
||
SECRET_KEY=$(openssl rand -hex 32)
|
||
ENCRYPTION_KEY=$ENCRYPTION_KEY
|
||
|
||
# Master Git Tokens (optional)
|
||
MASTER_GITEA_TOKEN=
|
||
MASTER_GITHUB_TOKEN=
|
||
MASTER_BITBUCKET_TOKEN=
|
||
|
||
# Server
|
||
HOST=0.0.0.0
|
||
PORT=8000
|
||
DEBUG=false
|
||
|
||
# CORS
|
||
CORS_ORIGINS=http://localhost:8000,http://localhost:5173
|
||
EOF
|
||
|
||
chown "$REAL_USER":"$REAL_USER" backend/.env
|
||
echo -e "${GREEN}✓ Создан .env файл${NC}"
|
||
echo -e "${YELLOW}⚠️ ВАЖНО: Отредактируйте backend/.env и добавьте необходимые токены!${NC}"
|
||
else
|
||
echo ".env уже существует"
|
||
fi
|
||
|
||
# 7. Создание systemd service
|
||
echo -e "${YELLOW}[7/10] Создание systemd service...${NC}"
|
||
|
||
cat > /etc/systemd/system/ai-review.service << EOF
|
||
[Unit]
|
||
Description=AI Code Review Platform
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=$REAL_USER
|
||
Group=$REAL_USER
|
||
WorkingDirectory=$INSTALL_DIR/backend
|
||
Environment="PATH=$INSTALL_DIR/backend/venv/bin:/usr/local/bin:/usr/bin:/bin"
|
||
ExecStart=$INSTALL_DIR/backend/venv/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||
Restart=always
|
||
RestartSec=10
|
||
StandardOutput=append:/var/log/ai-review/access.log
|
||
StandardError=append:/var/log/ai-review/error.log
|
||
|
||
# Security
|
||
NoNewPrivileges=true
|
||
PrivateTmp=true
|
||
ProtectSystem=strict
|
||
ProtectHome=true
|
||
ReadWritePaths=$INSTALL_DIR/backend
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
echo -e "${GREEN}✓ Systemd service создан${NC}"
|
||
|
||
# 8. Создание директории для логов
|
||
echo -e "${YELLOW}[8/10] Настройка логирования...${NC}"
|
||
mkdir -p /var/log/ai-review
|
||
chown "$REAL_USER":"$REAL_USER" /var/log/ai-review
|
||
echo -e "${GREEN}✓ Директория логов создана${NC}"
|
||
|
||
# 9. Включение и запуск сервиса
|
||
echo -e "${YELLOW}[9/10] Запуск сервиса...${NC}"
|
||
systemctl daemon-reload
|
||
systemctl enable ai-review.service
|
||
systemctl start ai-review.service
|
||
|
||
# Ждем запуска
|
||
echo "Ожидание запуска (5 секунд)..."
|
||
sleep 5
|
||
|
||
# Проверка статуса
|
||
if systemctl is-active --quiet ai-review.service; then
|
||
echo -e "${GREEN}✓ Сервис успешно запущен${NC}"
|
||
else
|
||
echo -e "${RED}✗ Ошибка запуска сервиса${NC}"
|
||
echo ""
|
||
echo -e "${YELLOW}Последние 30 строк логов:${NC}"
|
||
journalctl -u ai-review.service -n 30 --no-pager
|
||
echo ""
|
||
echo -e "${YELLOW}Для детальной диагностики запустите:${NC}"
|
||
echo " journalctl -u ai-review.service -n 100"
|
||
echo " cd $INSTALL_DIR/backend && source venv/bin/activate && python -m uvicorn app.main:app"
|
||
echo ""
|
||
echo -e "${YELLOW}Проверьте что:${NC}"
|
||
echo " 1. venv создан: ls -la $INSTALL_DIR/backend/venv"
|
||
echo " 2. Frontend собран: ls -la $INSTALL_DIR/backend/public"
|
||
echo " 3. .env настроен: cat $INSTALL_DIR/backend/.env"
|
||
exit 1
|
||
fi
|
||
|
||
# 10. Настройка nginx (опционально)
|
||
echo -e "${YELLOW}[10/10] Настройка nginx...${NC}"
|
||
|
||
if command -v nginx &> /dev/null; then
|
||
echo -e "${YELLOW}Настроить nginx как reverse proxy? [y/N]${NC}"
|
||
read -r setup_nginx
|
||
|
||
if [[ "$setup_nginx" =~ ^[Yy]$ ]]; then
|
||
echo "Введите доменное имя (или нажмите Enter для localhost):"
|
||
read -r domain_name
|
||
domain_name=${domain_name:-localhost}
|
||
|
||
cat > /etc/nginx/sites-available/ai-review << EOF
|
||
server {
|
||
listen 80;
|
||
server_name $domain_name;
|
||
|
||
client_max_body_size 50M;
|
||
|
||
location / {
|
||
proxy_pass http://localhost:8000;
|
||
proxy_set_header Host \$host;
|
||
proxy_set_header X-Real-IP \$remote_addr;
|
||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||
}
|
||
|
||
location /ws {
|
||
proxy_pass http://localhost:8000;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade \$http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
proxy_set_header Host \$host;
|
||
}
|
||
|
||
location /api {
|
||
proxy_pass http://localhost:8000;
|
||
proxy_set_header Host \$host;
|
||
proxy_set_header X-Real-IP \$remote_addr;
|
||
}
|
||
}
|
||
EOF
|
||
|
||
ln -sf /etc/nginx/sites-available/ai-review /etc/nginx/sites-enabled/
|
||
nginx -t && systemctl reload nginx
|
||
echo -e "${GREEN}✓ Nginx настроен${NC}"
|
||
echo -e "${GREEN}✓ Доступ: http://$domain_name${NC}"
|
||
fi
|
||
fi
|
||
|
||
# Итоговая информация
|
||
echo ""
|
||
echo -e "${GREEN}=========================================${NC}"
|
||
echo -e "${GREEN} 🎉 Установка завершена!${NC}"
|
||
echo -e "${GREEN}=========================================${NC}"
|
||
echo ""
|
||
echo -e "📍 ${BLUE}Директория установки:${NC} $INSTALL_DIR"
|
||
echo -e "📍 ${BLUE}Конфигурация:${NC} $INSTALL_DIR/backend/.env"
|
||
echo -e "📍 ${BLUE}Логи:${NC} /var/log/ai-review/"
|
||
echo ""
|
||
echo -e "${YELLOW}Полезные команды:${NC}"
|
||
echo ""
|
||
echo -e " ${BLUE}Статус сервиса:${NC}"
|
||
echo " sudo systemctl status ai-review"
|
||
echo ""
|
||
echo -e " ${BLUE}Перезапуск:${NC}"
|
||
echo " sudo systemctl restart ai-review"
|
||
echo ""
|
||
echo -e " ${BLUE}Остановка:${NC}"
|
||
echo " sudo systemctl stop ai-review"
|
||
echo ""
|
||
echo -e " ${BLUE}Просмотр логов:${NC}"
|
||
echo " sudo journalctl -u ai-review -f"
|
||
echo " tail -f /var/log/ai-review/access.log"
|
||
echo " tail -f /var/log/ai-review/error.log"
|
||
echo ""
|
||
echo -e " ${BLUE}Редактирование конфигурации:${NC}"
|
||
echo " sudo nano $INSTALL_DIR/backend/.env"
|
||
echo " sudo systemctl restart ai-review"
|
||
echo ""
|
||
echo -e "${GREEN}🌐 Приложение доступно по адресу:${NC}"
|
||
echo -e " ${BLUE}http://localhost:8000${NC}"
|
||
if [ -n "$domain_name" ] && [ "$domain_name" != "localhost" ]; then
|
||
echo -e " ${BLUE}http://$domain_name${NC}"
|
||
fi
|
||
echo ""
|
||
echo -e "${YELLOW}⚠️ Не забудьте:${NC}"
|
||
echo " 1. Отредактировать $INSTALL_DIR/backend/.env"
|
||
echo " 2. Добавить токены для Git платформ"
|
||
echo " 3. Настроить firewall (разрешить порт 80/443)"
|
||
echo ""
|
||
echo -e "${YELLOW}📦 Проверка установки:${NC}"
|
||
echo " Backend venv: $([ -d $INSTALL_DIR/backend/venv ] && echo '✓' || echo '✗')"
|
||
echo " Backend DB: $([ -f $INSTALL_DIR/backend/review.db ] && echo '✓' || echo '⚠️ будет создана')"
|
||
echo " Frontend build: $([ -d $INSTALL_DIR/backend/public ] && echo '✓' || echo '✗')"
|
||
echo ""
|
||
echo -e "${GREEN}✨ Готово к работе!${NC}"
|
||
echo ""
|
||
|