feat: добавлены CI/CD пайплайны
This commit is contained in:
93
.github/workflows/ci.yml
vendored
Normal file
93
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: CI pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'dev-*', 'feature-*' ]
|
||||
pull_request:
|
||||
branches: [ 'dev-*', 'feature-*', 'main' ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test & Code Quality
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Code formatting check (Black)
|
||||
run: |
|
||||
echo "🔍 Checking code formatting with Black..."
|
||||
black --check . || (echo "❌ Code formatting issues found. Run 'black .' to fix." && exit 1)
|
||||
|
||||
- name: Import sorting check (isort)
|
||||
run: |
|
||||
echo "🔍 Checking import sorting with isort..."
|
||||
isort --check-only . || (echo "❌ Import sorting issues found. Run 'isort .' to fix." && exit 1)
|
||||
|
||||
- name: Linting (flake8) - Critical errors
|
||||
run: |
|
||||
echo "🔍 Running flake8 linter (critical errors only)..."
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics || true
|
||||
|
||||
- name: Linting (flake8) - Warnings
|
||||
run: |
|
||||
echo "🔍 Running flake8 linter (warnings)..."
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics || true
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
echo "🧪 Running tests..."
|
||||
python -m pytest tests/ -v --tb=short
|
||||
|
||||
- name: Send test success notification
|
||||
if: success()
|
||||
uses: appleboy/telegram-action@v1.0.0
|
||||
with:
|
||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
message: |
|
||||
✅ CI Tests Passed
|
||||
|
||||
📦 Repository: telegram-helper-bot
|
||||
🌿 Branch: ${{ github.ref_name }}
|
||||
📝 Commit: ${{ github.sha }}
|
||||
👤 Author: ${{ github.actor }}
|
||||
|
||||
✅ All tests passed! Code quality checks completed successfully.
|
||||
|
||||
🔗 View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Send test failure notification
|
||||
if: failure()
|
||||
uses: appleboy/telegram-action@v1.0.0
|
||||
with:
|
||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
message: |
|
||||
❌ CI Tests Failed
|
||||
|
||||
📦 Repository: telegram-helper-bot
|
||||
🌿 Branch: ${{ github.ref_name }}
|
||||
📝 Commit: ${{ github.sha }}
|
||||
👤 Author: ${{ github.actor }}
|
||||
|
||||
❌ Tests failed! Deployment blocked. Please fix the issues and try again.
|
||||
|
||||
🔗 View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
continue-on-error: true
|
||||
356
.github/workflows/deploy.yml
vendored
Normal file
356
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
name: Deploy to Production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
description: 'Action to perform'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- deploy
|
||||
- rollback
|
||||
rollback_commit:
|
||||
description: 'Commit hash to rollback to (optional, uses last successful if empty)'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy to Production
|
||||
if: |
|
||||
github.event_name == 'push' ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.action == 'deploy')
|
||||
concurrency:
|
||||
group: production-deploy-telegram-helper-bot
|
||||
cancel-in-progress: false
|
||||
environment:
|
||||
name: production
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Deploy to server
|
||||
uses: appleboy/ssh-action@v1.0.0
|
||||
with:
|
||||
host: ${{ vars.SERVER_HOST || secrets.SERVER_HOST }}
|
||||
username: ${{ vars.SERVER_USER || secrets.SERVER_USER }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
port: ${{ vars.SSH_PORT || secrets.SSH_PORT || 22 }}
|
||||
script: |
|
||||
set -e
|
||||
export TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||
export TELEGRAM_TEST_BOT_TOKEN="${{ secrets.TELEGRAM_TEST_BOT_TOKEN }}"
|
||||
|
||||
echo "🚀 Starting deployment to production..."
|
||||
|
||||
cd /home/prod
|
||||
|
||||
# Сохраняем информацию о коммите
|
||||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s" || echo "Unknown")
|
||||
COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an" || echo "Unknown")
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
|
||||
echo "📝 Current commit: $CURRENT_COMMIT"
|
||||
echo "📝 Commit message: $COMMIT_MESSAGE"
|
||||
echo "📝 Author: $COMMIT_AUTHOR"
|
||||
|
||||
# Записываем в историю деплоев
|
||||
HISTORY_FILE="/home/prod/.deploy_history_telegram_helper_bot.txt"
|
||||
HISTORY_SIZE="${DEPLOY_HISTORY_SIZE:-10}"
|
||||
echo "${TIMESTAMP}|${CURRENT_COMMIT}|${COMMIT_MESSAGE}|${COMMIT_AUTHOR}|deploying" >> "$HISTORY_FILE"
|
||||
tail -n "$HISTORY_SIZE" "$HISTORY_FILE" > "${HISTORY_FILE}.tmp" && mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
|
||||
|
||||
# Обновляем код
|
||||
echo "📥 Pulling latest changes from main..."
|
||||
sudo chown -R deploy:deploy /home/prod/bots/telegram-helper-bot || true
|
||||
cd /home/prod/bots/telegram-helper-bot
|
||||
git fetch origin main
|
||||
git reset --hard origin/main
|
||||
sudo chown -R deploy:deploy /home/prod/bots/telegram-helper-bot || true
|
||||
|
||||
NEW_COMMIT=$(git rev-parse HEAD)
|
||||
echo "✅ Code updated: $CURRENT_COMMIT → $NEW_COMMIT"
|
||||
|
||||
# Применяем миграции БД перед перезапуском контейнера
|
||||
echo "🔄 Applying database migrations..."
|
||||
DB_PATH="/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db"
|
||||
|
||||
if [ -f "$DB_PATH" ]; then
|
||||
cd /home/prod/bots/telegram-helper-bot
|
||||
python3 scripts/apply_migrations.py --db "$DB_PATH" || {
|
||||
echo "❌ Ошибка при применении миграций!"
|
||||
exit 1
|
||||
}
|
||||
echo "✅ Миграции применены успешно"
|
||||
else
|
||||
echo "⚠️ База данных не найдена, пропускаем миграции (будет создана при первом запуске)"
|
||||
fi
|
||||
|
||||
# Валидация docker-compose
|
||||
echo "🔍 Validating docker-compose configuration..."
|
||||
cd /home/prod
|
||||
docker-compose config > /dev/null || exit 1
|
||||
echo "✅ docker-compose.yml is valid"
|
||||
|
||||
# Проверка дискового пространства
|
||||
MIN_FREE_GB=5
|
||||
AVAILABLE_SPACE=$(df -BG /home/prod 2>/dev/null | tail -1 | awk '{print $4}' | sed 's/G//' || echo "0")
|
||||
echo "💾 Available disk space: ${AVAILABLE_SPACE}GB"
|
||||
|
||||
if [ "$AVAILABLE_SPACE" -lt "$MIN_FREE_GB" ]; then
|
||||
echo "⚠️ Insufficient disk space! Cleaning up Docker resources..."
|
||||
docker system prune -f --volumes || true
|
||||
fi
|
||||
|
||||
# Пересобираем и перезапускаем контейнер бота
|
||||
echo "🔨 Rebuilding and restarting telegram-bot container..."
|
||||
cd /home/prod
|
||||
|
||||
export TELEGRAM_BOT_TOKEN TELEGRAM_TEST_BOT_TOKEN
|
||||
docker-compose stop telegram-bot || true
|
||||
docker-compose build --pull telegram-bot
|
||||
docker-compose up -d telegram-bot
|
||||
|
||||
echo "✅ Telegram bot container rebuilt and started"
|
||||
|
||||
# Ждем немного и проверяем healthcheck
|
||||
echo "⏳ Waiting for container to start..."
|
||||
sleep 10
|
||||
|
||||
if docker ps | grep -q bots_telegram_bot; then
|
||||
echo "✅ Container is running"
|
||||
else
|
||||
echo "❌ Container failed to start!"
|
||||
docker logs bots_telegram_bot --tail 50 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Update deploy history
|
||||
if: always()
|
||||
uses: appleboy/ssh-action@v1.0.0
|
||||
with:
|
||||
host: ${{ vars.SERVER_HOST || secrets.SERVER_HOST }}
|
||||
username: ${{ vars.SERVER_USER || secrets.SERVER_USER }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
port: ${{ vars.SSH_PORT || secrets.SSH_PORT || 22 }}
|
||||
script: |
|
||||
HISTORY_FILE="/home/prod/.deploy_history_telegram_helper_bot.txt"
|
||||
|
||||
if [ -f "$HISTORY_FILE" ]; then
|
||||
DEPLOY_STATUS="failed"
|
||||
if [ "${{ job.status }}" = "success" ]; then
|
||||
DEPLOY_STATUS="success"
|
||||
fi
|
||||
|
||||
sed -i '$s/|deploying$/|'"$DEPLOY_STATUS"'/' "$HISTORY_FILE"
|
||||
echo "✅ Deploy history updated: $DEPLOY_STATUS"
|
||||
fi
|
||||
|
||||
- name: Send deployment notification
|
||||
if: always()
|
||||
uses: appleboy/telegram-action@v1.0.0
|
||||
with:
|
||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
message: |
|
||||
${{ job.status == 'success' && '✅' || '❌' }} Deployment: ${{ job.status }}
|
||||
|
||||
📦 Repository: telegram-helper-bot
|
||||
🌿 Branch: main
|
||||
📝 Commit: ${{ github.sha }}
|
||||
👤 Author: ${{ github.actor }}
|
||||
|
||||
${{ job.status == 'success' && '✅ Deployment successful! Container restarted with migrations applied.' || '❌ Deployment failed! Check logs for details.' }}
|
||||
|
||||
🔗 View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Get PR body from merged PR
|
||||
if: job.status == 'success' && github.event_name == 'push'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "🔍 Searching for merged PR associated with commit ${{ github.sha }}..."
|
||||
|
||||
# Находим последний мерженный PR для main ветки по merge commit SHA
|
||||
COMMIT_SHA="${{ github.sha }}"
|
||||
PR_NUMBER=$(gh pr list --state merged --base main --limit 10 --json number,mergeCommit --jq ".[] | select(.mergeCommit.oid == \"$COMMIT_SHA\") | .number" | head -1)
|
||||
|
||||
# Если не нашли по merge commit, ищем последний мерженный PR
|
||||
if [ -z "$PR_NUMBER" ]; then
|
||||
echo "⚠️ PR not found by merge commit, trying to get latest merged PR..."
|
||||
PR_NUMBER=$(gh pr list --state merged --base main --limit 1 --json number --jq '.[0].number')
|
||||
fi
|
||||
|
||||
if [ -n "$PR_NUMBER" ] && [ "$PR_NUMBER" != "null" ]; then
|
||||
echo "✅ Found PR #$PR_NUMBER"
|
||||
PR_BODY=$(gh pr view $PR_NUMBER --json body --jq '.body // ""')
|
||||
|
||||
if [ -n "$PR_BODY" ] && [ "$PR_BODY" != "null" ]; then
|
||||
echo "PR_BODY<<EOF" >> $GITHUB_ENV
|
||||
echo "$PR_BODY" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
|
||||
echo "✅ PR body extracted successfully"
|
||||
else
|
||||
echo "⚠️ PR body is empty"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ No merged PR found for this commit"
|
||||
fi
|
||||
continue-on-error: true
|
||||
|
||||
- name: Send PR body to important logs
|
||||
if: job.status == 'success' && github.event_name == 'push' && env.PR_BODY != ''
|
||||
uses: appleboy/telegram-action@v1.0.0
|
||||
with:
|
||||
to: ${{ secrets.IMPORTANT_LOGS_CHAT }}
|
||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
message: |
|
||||
📋 Pull Request Description (PR #${{ env.PR_NUMBER }}):
|
||||
|
||||
${{ env.PR_BODY }}
|
||||
|
||||
🔗 PR: ${{ github.server_url }}/${{ github.repository }}/pull/${{ env.PR_NUMBER }}
|
||||
📝 Commit: ${{ github.sha }}
|
||||
continue-on-error: true
|
||||
|
||||
rollback:
|
||||
runs-on: ubuntu-latest
|
||||
name: Rollback to Previous Version
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' &&
|
||||
github.event.inputs.action == 'rollback'
|
||||
environment:
|
||||
name: production
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Rollback on server
|
||||
uses: appleboy/ssh-action@v1.0.0
|
||||
with:
|
||||
host: ${{ vars.SERVER_HOST || secrets.SERVER_HOST }}
|
||||
username: ${{ vars.SERVER_USER || secrets.SERVER_USER }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
port: ${{ vars.SSH_PORT || secrets.SSH_PORT || 22 }}
|
||||
script: |
|
||||
set -e
|
||||
export TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||
export TELEGRAM_TEST_BOT_TOKEN="${{ secrets.TELEGRAM_TEST_BOT_TOKEN }}"
|
||||
|
||||
echo "🔄 Starting rollback..."
|
||||
|
||||
cd /home/prod
|
||||
|
||||
# Определяем коммит для отката
|
||||
ROLLBACK_COMMIT="${{ github.event.inputs.rollback_commit }}"
|
||||
HISTORY_FILE="/home/prod/.deploy_history_telegram_helper_bot.txt"
|
||||
|
||||
if [ -z "$ROLLBACK_COMMIT" ]; then
|
||||
echo "📝 No commit specified, finding last successful deploy..."
|
||||
if [ -f "$HISTORY_FILE" ]; then
|
||||
ROLLBACK_COMMIT=$(grep "|success$" "$HISTORY_FILE" | tail -1 | cut -d'|' -f2 || echo "")
|
||||
fi
|
||||
|
||||
if [ -z "$ROLLBACK_COMMIT" ]; then
|
||||
echo "❌ No successful deploy found in history!"
|
||||
echo "💡 Please specify commit hash manually or check deploy history"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "📝 Rolling back to commit: $ROLLBACK_COMMIT"
|
||||
|
||||
# Проверяем, что коммит существует
|
||||
cd /home/prod/bots/telegram-helper-bot
|
||||
if ! git cat-file -e "$ROLLBACK_COMMIT" 2>/dev/null; then
|
||||
echo "❌ Commit $ROLLBACK_COMMIT not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Сохраняем текущий коммит
|
||||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s" "$ROLLBACK_COMMIT" || echo "Rollback")
|
||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
|
||||
echo "📝 Current commit: $CURRENT_COMMIT"
|
||||
echo "📝 Target commit: $ROLLBACK_COMMIT"
|
||||
echo "📝 Commit message: $COMMIT_MESSAGE"
|
||||
|
||||
# Исправляем права перед откатом
|
||||
sudo chown -R deploy:deploy /home/prod/bots/telegram-helper-bot || true
|
||||
|
||||
# Откатываем код
|
||||
echo "🔄 Rolling back code..."
|
||||
git fetch origin main
|
||||
git reset --hard "$ROLLBACK_COMMIT"
|
||||
|
||||
# Исправляем права после отката
|
||||
sudo chown -R deploy:deploy /home/prod/bots/telegram-helper-bot || true
|
||||
|
||||
echo "✅ Code rolled back: $CURRENT_COMMIT → $ROLLBACK_COMMIT"
|
||||
|
||||
# Валидация docker-compose
|
||||
echo "🔍 Validating docker-compose configuration..."
|
||||
cd /home/prod
|
||||
docker-compose config > /dev/null || exit 1
|
||||
echo "✅ docker-compose.yml is valid"
|
||||
|
||||
# Проверка дискового пространства
|
||||
MIN_FREE_GB=5
|
||||
AVAILABLE_SPACE=$(df -BG /home/prod 2>/dev/null | tail -1 | awk '{print $4}' | sed 's/G//' || echo "0")
|
||||
echo "💾 Available disk space: ${AVAILABLE_SPACE}GB"
|
||||
|
||||
if [ "$AVAILABLE_SPACE" -lt "$MIN_FREE_GB" ]; then
|
||||
echo "⚠️ Insufficient disk space! Cleaning up Docker resources..."
|
||||
docker system prune -f --volumes || true
|
||||
fi
|
||||
|
||||
# Пересобираем и перезапускаем контейнер
|
||||
echo "🔨 Rebuilding and restarting telegram-bot container..."
|
||||
cd /home/prod
|
||||
|
||||
export TELEGRAM_BOT_TOKEN TELEGRAM_TEST_BOT_TOKEN
|
||||
docker-compose stop telegram-bot || true
|
||||
docker-compose build --pull telegram-bot
|
||||
docker-compose up -d telegram-bot
|
||||
|
||||
echo "✅ Telegram bot container rebuilt and started"
|
||||
|
||||
# Записываем в историю
|
||||
echo "${TIMESTAMP}|${ROLLBACK_COMMIT}|Rollback to: ${COMMIT_MESSAGE}|github-actions|rolled_back" >> "$HISTORY_FILE"
|
||||
HISTORY_SIZE="${DEPLOY_HISTORY_SIZE:-10}"
|
||||
tail -n "$HISTORY_SIZE" "$HISTORY_FILE" > "${HISTORY_FILE}.tmp" && mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
|
||||
|
||||
echo "✅ Rollback completed successfully"
|
||||
|
||||
- name: Send rollback notification
|
||||
if: always()
|
||||
uses: appleboy/telegram-action@v1.0.0
|
||||
with:
|
||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
message: |
|
||||
${{ job.status == 'success' && '🔄' || '❌' }} Rollback: ${{ job.status }}
|
||||
|
||||
📦 Repository: telegram-helper-bot
|
||||
🌿 Branch: main
|
||||
📝 Rolled back to: ${{ github.event.inputs.rollback_commit || 'Last successful commit' }}
|
||||
👤 Triggered by: ${{ github.actor }}
|
||||
|
||||
${{ job.status == 'success' && '✅ Rollback completed successfully! Services restored to previous version.' || '❌ Rollback failed! Check logs for details.' }}
|
||||
|
||||
🔗 View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
continue-on-error: true
|
||||
Reference in New Issue
Block a user