This commit is contained in:
302
.drone.yml
Normal file
302
.drone.yml
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: catlink-ci
|
||||||
|
|
||||||
|
# Trigger настройки
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
# Глобальные переменные
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
COMPOSE_DOCKER_CLI_BUILD: 1
|
||||||
|
|
||||||
|
# Этапы пайплайна
|
||||||
|
steps:
|
||||||
|
# 1. Установка зависимостей и подготовка
|
||||||
|
- name: prepare
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache make curl git
|
||||||
|
- docker --version
|
||||||
|
- docker-compose --version
|
||||||
|
- echo "Repository:$${DRONE_REPO}"
|
||||||
|
- echo "Branch:$${DRONE_BRANCH}"
|
||||||
|
- echo "Commit:$${DRONE_COMMIT_SHA:0:8}"
|
||||||
|
|
||||||
|
# 2. Линтинг и проверка кода
|
||||||
|
- name: lint
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- echo "🔍 Running code quality checks..."
|
||||||
|
- ./scripts/ci/lint.sh
|
||||||
|
depends_on:
|
||||||
|
- prepare
|
||||||
|
|
||||||
|
# 3. Сборка приложения
|
||||||
|
- name: build
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- echo "🏗️ Building application..."
|
||||||
|
- ./scripts/ci/build.sh
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
|
||||||
|
# 4. Тестирование
|
||||||
|
- name: test
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgres://catlink:catlink@postgres:5432/catlink_test
|
||||||
|
commands:
|
||||||
|
- echo "🧪 Running tests..."
|
||||||
|
- ./scripts/ci/test.sh
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
|
||||||
|
# 5. Анализ безопасности
|
||||||
|
- name: security-scan
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- echo "🔒 Running security scans..."
|
||||||
|
- ./scripts/ci/security-scan.sh
|
||||||
|
depends_on:
|
||||||
|
- test
|
||||||
|
failure: ignore # Не останавливаем пайплайн при проблемах безопасности
|
||||||
|
|
||||||
|
# 6. Сборка Docker образов для продакшена
|
||||||
|
- name: build-production
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- echo "🚀 Building production images..."
|
||||||
|
- ./scripts/ci/build-production.sh
|
||||||
|
- docker images | grep catlink
|
||||||
|
depends_on:
|
||||||
|
- security-scan
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
|
||||||
|
# 7. Публикация образов в Registry
|
||||||
|
- name: publish
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
DOCKER_USERNAME:
|
||||||
|
from_secret: docker_username
|
||||||
|
DOCKER_PASSWORD:
|
||||||
|
from_secret: docker_password
|
||||||
|
DOCKER_REGISTRY:
|
||||||
|
from_secret: docker_registry
|
||||||
|
commands:
|
||||||
|
- echo "📦 Publishing to registry..."
|
||||||
|
- ./scripts/ci/publish.sh
|
||||||
|
depends_on:
|
||||||
|
- build-production
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
# 8. Деплой на staging
|
||||||
|
- name: deploy-staging
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
STAGING_HOST:
|
||||||
|
from_secret: staging_host
|
||||||
|
STAGING_USER:
|
||||||
|
from_secret: staging_user
|
||||||
|
STAGING_KEY:
|
||||||
|
from_secret: staging_ssh_key
|
||||||
|
commands:
|
||||||
|
- echo "🎭 Deploying to staging..."
|
||||||
|
- ./scripts/ci/deploy-staging.sh
|
||||||
|
depends_on:
|
||||||
|
- publish
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- develop
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
# 9. Деплой на продакшен
|
||||||
|
- name: deploy-production
|
||||||
|
image: docker:20.10-dind
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
PRODUCTION_HOST:
|
||||||
|
from_secret: production_host
|
||||||
|
PRODUCTION_USER:
|
||||||
|
from_secret: production_user
|
||||||
|
PRODUCTION_KEY:
|
||||||
|
from_secret: production_ssh_key
|
||||||
|
DEPLOY_KEY:
|
||||||
|
from_secret: deploy_key
|
||||||
|
commands:
|
||||||
|
- echo "🚀 Deploying to production..."
|
||||||
|
- ./scripts/ci/deploy-production.sh
|
||||||
|
depends_on:
|
||||||
|
- publish
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
# 10. Уведомления
|
||||||
|
- name: notify
|
||||||
|
image: plugins/slack
|
||||||
|
settings:
|
||||||
|
webhook:
|
||||||
|
from_secret: slack_webhook
|
||||||
|
channel: "#catlink-ci"
|
||||||
|
username: "Drone CI"
|
||||||
|
template: |
|
||||||
|
{{#success build.status}}
|
||||||
|
✅ *Build {{build.number}} succeeded*
|
||||||
|
📁 Repository: {{repo.name}}
|
||||||
|
🌿 Branch: {{build.branch}}
|
||||||
|
👤 Author: {{build.author}}
|
||||||
|
📝 Commit: {{truncate build.commit 8}}
|
||||||
|
🔗 {{build.link}}
|
||||||
|
{{else}}
|
||||||
|
❌ *Build {{build.number}} failed*
|
||||||
|
📁 Repository: {{repo.name}}
|
||||||
|
🌿 Branch: {{build.branch}}
|
||||||
|
👤 Author: {{build.author}}
|
||||||
|
📝 Commit: {{truncate build.commit 8}}
|
||||||
|
🔗 {{build.link}}
|
||||||
|
{{/success}}
|
||||||
|
depends_on:
|
||||||
|
- deploy-production
|
||||||
|
- deploy-staging
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
# Сервисы для тестирования
|
||||||
|
services:
|
||||||
|
# PostgreSQL для тестов
|
||||||
|
- name: postgres
|
||||||
|
image: postgres:14-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: catlink_test
|
||||||
|
POSTGRES_USER: catlink
|
||||||
|
POSTGRES_PASSWORD: catlink
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
|
tmpfs:
|
||||||
|
- /var/lib/postgresql/data
|
||||||
|
|
||||||
|
# Redis для кеширования (если потребуется)
|
||||||
|
- name: redis
|
||||||
|
image: redis:7-alpine
|
||||||
|
|
||||||
|
# Volumes
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
---
|
||||||
|
# Дополнительный пайплайн для нотификаций в Telegram
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: telegram-notify
|
||||||
|
|
||||||
|
# Уведомления в Telegram
|
||||||
|
steps:
|
||||||
|
- name: telegram
|
||||||
|
image: appleboy/drone-telegram
|
||||||
|
settings:
|
||||||
|
token:
|
||||||
|
from_secret: telegram_token
|
||||||
|
to:
|
||||||
|
from_secret: telegram_chat_id
|
||||||
|
format: markdown
|
||||||
|
message: |
|
||||||
|
{{#success build.status}}
|
||||||
|
✅ *Build Success*
|
||||||
|
{{else}}
|
||||||
|
❌ *Build Failed*
|
||||||
|
{{/success}}
|
||||||
|
|
||||||
|
📁 *Repository:* {{repo.name}}
|
||||||
|
🌿 *Branch:* {{build.branch}}
|
||||||
|
👤 *Author:* {{build.author}}
|
||||||
|
📝 *Commit:* `{{truncate build.commit 8}}`
|
||||||
|
⏱️ *Duration:* {{since build.started}}
|
||||||
|
🔗 [View Build]({{build.link}})
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
status:
|
||||||
|
- success
|
||||||
|
- failure
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- catlink-ci
|
||||||
|
|
||||||
|
---
|
||||||
|
# Пайплайн для релизов
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: create-release
|
||||||
|
image: plugins/github-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: github_token
|
||||||
|
title: "CatLink v${DRONE_TAG}"
|
||||||
|
note: "Release ${DRONE_TAG}"
|
||||||
|
files:
|
||||||
|
- "dist/*"
|
||||||
|
checksum:
|
||||||
|
- md5
|
||||||
|
- sha1
|
||||||
|
- sha256
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
# Signature для верификации (если используется)
|
||||||
|
kind: signature
|
||||||
|
hmac: <your-hmac-signature-here>
|
||||||
69
Makefile
69
Makefile
@@ -175,6 +175,75 @@ format: ## Форматирование кода
|
|||||||
@docker-compose exec frontend npm run format || true
|
@docker-compose exec frontend npm run format || true
|
||||||
@echo "✅ Код отформатирован"
|
@echo "✅ Код отформатирован"
|
||||||
|
|
||||||
|
# === CI/CD Operations ===
|
||||||
|
|
||||||
|
ci-lint: ## Локальный запуск CI линтинга
|
||||||
|
@echo "🔍 Запуск CI линтинга локально..."
|
||||||
|
@bash ./scripts/ci/lint.sh
|
||||||
|
@echo "✅ CI линтинг завершен"
|
||||||
|
|
||||||
|
ci-test: ## Локальный запуск CI тестов
|
||||||
|
@echo "🧪 Запуск CI тестов локально..."
|
||||||
|
@bash ./scripts/ci/test.sh
|
||||||
|
@echo "✅ CI тесты завершены"
|
||||||
|
|
||||||
|
ci-security: ## Локальный запуск проверки безопасности
|
||||||
|
@echo "🔒 Запуск проверки безопасности..."
|
||||||
|
@bash ./scripts/ci/security-scan.sh
|
||||||
|
@echo "✅ Проверка безопасности завершена"
|
||||||
|
|
||||||
|
ci-build: ## Локальная сборка как в CI
|
||||||
|
@echo "🏗️ Запуск CI сборки локально..."
|
||||||
|
@bash ./scripts/ci/build.sh
|
||||||
|
@echo "✅ CI сборка завершена"
|
||||||
|
|
||||||
|
ci-build-prod: ## Локальная сборка продакшен образов
|
||||||
|
@echo "🏗️ Сборка продакшен образов..."
|
||||||
|
@bash ./scripts/ci/build-production.sh
|
||||||
|
@echo "✅ Продакшен образы собраны"
|
||||||
|
|
||||||
|
ci-publish: ## Публикация образов в registry
|
||||||
|
@echo "📤 Публикация образов..."
|
||||||
|
@bash ./scripts/ci/publish.sh
|
||||||
|
@echo "✅ Образы опубликованы"
|
||||||
|
|
||||||
|
ci-deploy-staging: ## Деплой на staging
|
||||||
|
@echo "🚀 Деплой на staging..."
|
||||||
|
@bash ./scripts/ci/deploy-staging.sh
|
||||||
|
@echo "✅ Staging деплой завершен"
|
||||||
|
|
||||||
|
ci-deploy-production: ## Деплой на production
|
||||||
|
@echo "🚀 Деплой на production..."
|
||||||
|
@bash ./scripts/ci/deploy-production.sh
|
||||||
|
@echo "✅ Production деплой завершен"
|
||||||
|
|
||||||
|
ci-pipeline: ## Полный CI/CD пайплайн локально
|
||||||
|
@echo "🚀 Запуск полного CI/CD пайплайна..."
|
||||||
|
@$(MAKE) ci-lint
|
||||||
|
@$(MAKE) ci-test
|
||||||
|
@$(MAKE) ci-security
|
||||||
|
@$(MAKE) ci-build
|
||||||
|
@echo "✅ Полный пайплайн завершен"
|
||||||
|
|
||||||
|
drone-validate: ## Валидация .drone.yml
|
||||||
|
@echo "✅ Валидация Drone конфигурации..."
|
||||||
|
@if command -v drone >/dev/null 2>&1; then \
|
||||||
|
drone lint .drone.yml; \
|
||||||
|
else \
|
||||||
|
echo "⚠️ Drone CLI не установлен, используем docker..."; \
|
||||||
|
docker run --rm -v "$(PWD):/repo" -w /repo drone/cli:alpine lint .drone.yml; \
|
||||||
|
fi
|
||||||
|
@echo "✅ Валидация завершена"
|
||||||
|
|
||||||
|
drone-sign: ## Подпись .drone.yml (требует настройки)
|
||||||
|
@echo "🔐 Подпись Drone конфигурации..."
|
||||||
|
@if [ -z "$(DRONE_SECRET)" ]; then \
|
||||||
|
echo "❌ DRONE_SECRET не установлен"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@drone sign smartsoltech/links --save
|
||||||
|
@echo "✅ Конфигурация подписана"
|
||||||
|
|
||||||
# === Helper scripts and automation ===
|
# === Helper scripts and automation ===
|
||||||
generate-env: ## Сгенерировать .env (интерактивно)
|
generate-env: ## Сгенерировать .env (интерактивно)
|
||||||
@echo "🧭 Генерация .env файла (использует scripts/generate_env.sh)"
|
@echo "🧭 Генерация .env файла (использует scripts/generate_env.sh)"
|
||||||
|
|||||||
114
README.md
114
README.md
@@ -1,4 +1,4 @@
|
|||||||
# 🐱 CatLink - Персональные страницы ссылок# 🐱 CatLink - Персональные страницы ссылок
|
# 🐱 CatLink - Персональные страницы ссылок
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -12,59 +12,43 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
**CatLink** - современная платформа для создания красивых персональных страниц со всеми важными ссылками в одном месте. Альтернатива Linktree с открытым исходным кодом.**CatLink** - современная платформа для создания красивых персональных страниц со всеми важными ссылками в одном месте. Альтернатива Linktree с открытым исходным кодом.
|
**CatLink** - современная платформа для создания красивых персональных страниц со всеми важными ссылками в одном месте. Альтернатива Linktree с открытым исходным кодом.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ✨ Возможности## ✨ Возможности
|
## ✨ Возможности
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- 🎨 **Множество макетов**: список, сетка, компактная сетка, журнальная верстка- 🎨 **Множество макетов**: список, сетка, компактная сетка, журнальная верстка
|
- 🎨 **Множество макетов**: список, сетка, компактная сетка, журнальная верстка
|
||||||
|
|
||||||
- 🎭 **Персонализация**: настройка цветов, фонов, иконок- 🎭 **Персонализация**: настройка цветов, фонов, иконок
|
- 🎭 **Персонализация**: настройка цветов, фонов, иконок
|
||||||
|
- 📱 **Адаптивный дизайн**: отлично работает на всех устройствах
|
||||||
|
- 🔐 **Система аутентификации**: регистрация и управление профилем
|
||||||
|
|
||||||
- 📱 **Адаптивный дизайн**: отлично работает на всех устройствах- 📱 **Адаптивный дизайн**: отлично работает на всех устройствах
|
- 📊 **Группировка ссылок**: организация ссылок по категориям
|
||||||
|
- 🌐 **SEO-оптимизация**: дружественные URL и метатеги
|
||||||
|
- 🚀 **Высокая производительность**: быстрая загрузка и отзывчивость
|
||||||
|
- 🐳 **Docker Ready**: простой деплой с помощью контейнеров
|
||||||
|
## 🚀 Быстрый старт
|
||||||
|
|
||||||
- 🔐 **Система аутентификации**: регистрация и управление профилем- 🔐 **Система аутентификации**: регистрация и управление профилем
|
|
||||||
|
|
||||||
- 📊 **Группировка ссылок**: организация ссылок по категориям- 📊 **Группировка ссылок**: организация ссылок по категориям
|
### Требования
|
||||||
|
|
||||||
- 🌐 **SEO-оптимизация**: дружественные URL и метатеги- 🌐 **SEO-оптимизация**: дружественные URL и метатеги
|
- Docker и Docker Compose
|
||||||
|
- Make (для удобства)
|
||||||
- 🚀 **Высокая производительность**: быстрая загрузка и отзывчивость- 🚀 **Высокая производительность**: быстрая загрузка и отзывчивость
|
- Git
|
||||||
|
|
||||||
- 🐳 **Docker Ready**: простой деплой с помощью контейнеров- 🐳 **Docker Ready**: простой деплой с помощью контейнеров
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 🚀 Быстрый старт## 🚀 Быстрый старт
|
### Установка
|
||||||
|
|
||||||
|
1. **Клонируйте репозиторий**
|
||||||
|
```bash
|
||||||
|
|
||||||
|
git clone https://github.com/smartsoltech/links.git
|
||||||
### Требования### Требования
|
cd links
|
||||||
|
```
|
||||||
- Docker и Docker Compose- Docker и Docker Compose
|
|
||||||
|
|
||||||
- Make (для удобства)- Make (для удобства)
|
|
||||||
|
|
||||||
- Git- Git
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Установка### Установка
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1. **Клонируйте репозиторий**1. **Клонируйте репозиторий**
|
|
||||||
|
|
||||||
```bash ```bash
|
|
||||||
|
|
||||||
git clone https://github.com/smartsoltech/links.git git clone https://github.com/smartsoltech/links.git
|
|
||||||
|
|
||||||
cd links cd links
|
|
||||||
|
|
||||||
``` ```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -88,79 +72,77 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Альтернативная установка (без Make)### Альтернативная установка (без Make)
|
### Альтернативная установка (без Make)
|
||||||
|
```bash
|
||||||
|
|
||||||
```bash```bash
|
# Копируйте пример конфигурации
|
||||||
|
|
||||||
# Копируйте пример конфигурации# Копируйте пример конфигурации
|
cp .env.example .en
|
||||||
|
|
||||||
cp .env.example .envcp .env.example .env
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Отредактируйте .env файл# Отредактируйте .env файл
|
# Отредактируйте .env файл
|
||||||
|
|
||||||
nano .envnano .env
|
nano .env
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Запустите контейнеры# Запустите контейнеры
|
# Запустите контейнеры
|
||||||
|
|
||||||
docker-compose up -d --builddocker-compose up -d --build
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Примените миграции# Примените миграции
|
# Примените миграции
|
||||||
|
|
||||||
docker-compose exec web python manage.py migratedocker-compose exec web python manage.py migrate
|
docker-compose exec web python manage.py migrate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Создайте суперпользователя# Создайте суперпользователя
|
# Создайте суперпользователя
|
||||||
|
|
||||||
docker-compose exec web python manage.py createsuperuserdocker-compose exec web python manage.py createsuperuser
|
docker-compose exec web python manage.py createsuperuser
|
||||||
|
|
||||||
``````
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 📋 Управление с помощью Make## 📋 Управление с помощью Make
|
## 📋 Управление с помощью Make
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Проект включает мощный Makefile для упрощения разработки:Проект включает мощный Makefile для упрощения разработки:
|
Проект включает мощный Makefile для упрощения разработки:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```bash```bash
|
```bash```bash
|
||||||
|
|
||||||
make help # Показать все доступные командыmake help # Показать все доступные команды
|
make help # Показать все доступные команды
|
||||||
|
|
||||||
make dev-bg # Запуск для разработки в фонеmake dev-bg # Запуск для разработки в фоне
|
make dev-bg # Запуск для разработки в фоне
|
||||||
|
|
||||||
make status # Проверить статус сервисовmake status # Проверить статус сервисов
|
make status # Проверить статус сервисов
|
||||||
|
|
||||||
make health # Проверить работоспособностьmake health # Проверить работоспособность
|
make health # Проверить работоспособность
|
||||||
|
|
||||||
make logs # Просмотр логовmake logs # Просмотр логов
|
make logs # Просмотр логов
|
||||||
|
|
||||||
make migrate-full # Выполнить миграции и собрать статикуmake migrate-full # Выполнить миграции и собрать статику
|
make migrate-full # Выполнить миграции и собрать статику
|
||||||
|
|
||||||
make backup # Создать бэкап базы данныхmake backup # Создать бэкап базы данных
|
make backup # Создать бэкап базы данных
|
||||||
|
|
||||||
``````
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
📖 **[Полное руководство по Makefile](./docs/MAKEFILE.md)**📖 **[Полное руководство по Makefile](./docs/MAKEFILE.md)**
|
📖 **[Полное руководство по Makefile](./docs/MAKEFILE.md)**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 🏗️ Архитектура## 🏗️ Архитектура
|
## 🏗️ Архитектура
|
||||||
|
|
||||||
|
|
||||||
|
### Технологический стек
|
||||||
### Технологический стек### Технологический стек
|
|
||||||
|
|
||||||
- **Frontend**: Next.js 15, React, TypeScript, Bootstrap 5
|
- **Frontend**: Next.js 15, React, TypeScript, Bootstrap 5
|
||||||
|
|
||||||
|
|||||||
478
docs/CICD.md
Normal file
478
docs/CICD.md
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
# CI/CD Documentation
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
1. [Обзор CI/CD](#обзор-cicd)
|
||||||
|
2. [Архитектура пайплайна](#архитектура-пайплайна)
|
||||||
|
3. [Настройка Drone CI](#настройка-drone-ci)
|
||||||
|
4. [Конфигурация окружений](#конфигурация-окружений)
|
||||||
|
5. [Локальное тестирование](#локальное-тестирование)
|
||||||
|
6. [Деплой и управление](#деплой-и-управление)
|
||||||
|
7. [Мониторинг и уведомления](#мониторинг-и-уведомления)
|
||||||
|
8. [Устранение неполадок](#устранение-неполадок)
|
||||||
|
|
||||||
|
## 🚀 Обзор CI/CD
|
||||||
|
|
||||||
|
CatLink использует современный CI/CD пайплайн на базе **Drone CI** для автоматизации:
|
||||||
|
|
||||||
|
- 🔍 **Проверка качества кода** (линтинг, форматирование)
|
||||||
|
- 🧪 **Автоматизированное тестирование**
|
||||||
|
- 🔒 **Сканирование безопасности**
|
||||||
|
- 🏗️ **Сборка Docker образов**
|
||||||
|
- 📤 **Публикация в registry**
|
||||||
|
- 🚀 **Автоматический деплой** на staging и production
|
||||||
|
|
||||||
|
### Основные преимущества
|
||||||
|
|
||||||
|
- ✅ **Автоматизация** всех этапов развертывания
|
||||||
|
- ✅ **Качество кода** обеспечивается проверками
|
||||||
|
- ✅ **Безопасность** через сканирование уязвимостей
|
||||||
|
- ✅ **Надежность** благодаря тестированию
|
||||||
|
- ✅ **Скорость** развертывания изменений
|
||||||
|
|
||||||
|
## 🏗️ Архитектура пайплайна
|
||||||
|
|
||||||
|
### Этапы CI/CD пайплайна
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Git Push] --> B[Prepare]
|
||||||
|
B --> C[Lint & Code Quality]
|
||||||
|
C --> D[Build & Test]
|
||||||
|
D --> E[Security Scan]
|
||||||
|
E --> F[Build Production]
|
||||||
|
F --> G[Publish Images]
|
||||||
|
G --> H[Deploy Staging]
|
||||||
|
H --> I[Deploy Production]
|
||||||
|
I --> J[Notifications]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1. **Prepare** - Подготовка
|
||||||
|
- Настройка окружения
|
||||||
|
- Установка зависимостей
|
||||||
|
- Инициализация кэша
|
||||||
|
|
||||||
|
### 2. **Lint** - Проверка качества кода
|
||||||
|
- Python: `flake8`, `black`, `isort`
|
||||||
|
- TypeScript: `ESLint`, `Prettier`
|
||||||
|
- Docker: `hadolint`
|
||||||
|
- YAML: `yamllint`
|
||||||
|
- Security: `bandit`, `safety`
|
||||||
|
|
||||||
|
### 3. **Build & Test** - Сборка и тестирование
|
||||||
|
- Сборка Docker образов
|
||||||
|
- Запуск unit тестов
|
||||||
|
- API тестирование
|
||||||
|
- Frontend тесты
|
||||||
|
- Integration тесты
|
||||||
|
|
||||||
|
### 4. **Security Scan** - Проверка безопасности
|
||||||
|
- Сканирование зависимостей
|
||||||
|
- Анализ исходного кода
|
||||||
|
- Проверка Docker образов
|
||||||
|
- Аудит конфигураций
|
||||||
|
|
||||||
|
### 5. **Build Production** - Продакшен сборка
|
||||||
|
- Оптимизированные образы
|
||||||
|
- Multi-stage builds
|
||||||
|
- Минимальные базовые образы
|
||||||
|
- Метаданные и лейблы
|
||||||
|
|
||||||
|
### 6. **Publish** - Публикация
|
||||||
|
- Push в Docker registry
|
||||||
|
- Создание release notes
|
||||||
|
- Версионирование
|
||||||
|
- Уведомления команды
|
||||||
|
|
||||||
|
### 7. **Deploy** - Развертывание
|
||||||
|
- Staging деплой для тестирования
|
||||||
|
- Production деплой с проверками
|
||||||
|
- Rolling updates без простоя
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
## 🛠️ Настройка Drone CI
|
||||||
|
|
||||||
|
### Требования
|
||||||
|
|
||||||
|
- Drone CI сервер
|
||||||
|
- Docker registry (Docker Hub, GitLab Registry, etc.)
|
||||||
|
- SSH доступ к серверам
|
||||||
|
- Переменные окружения
|
||||||
|
|
||||||
|
### Переменные окружения
|
||||||
|
|
||||||
|
#### Обязательные переменные
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Docker Registry
|
||||||
|
DOCKER_REGISTRY=registry.hub.docker.com
|
||||||
|
DOCKER_USERNAME=your_username
|
||||||
|
DOCKER_PASSWORD=your_password
|
||||||
|
|
||||||
|
# Staging Environment
|
||||||
|
STAGING_HOST=staging.catlink.dev
|
||||||
|
STAGING_SSH_KEY=base64_encoded_ssh_key
|
||||||
|
STAGING_USER=deploy
|
||||||
|
|
||||||
|
# Production Environment
|
||||||
|
PRODUCTION_HOST=catlink.dev
|
||||||
|
PRODUCTION_SSH_KEY=base64_encoded_ssh_key
|
||||||
|
PRODUCTION_USER=deploy
|
||||||
|
PRODUCTION_SECRET_KEY=super_secret_key
|
||||||
|
PRODUCTION_POSTGRES_PASSWORD=secure_db_password
|
||||||
|
PRODUCTION_EMAIL_HOST=smtp.gmail.com
|
||||||
|
PRODUCTION_EMAIL_USER=your_email@gmail.com
|
||||||
|
PRODUCTION_EMAIL_PASSWORD=your_app_password
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Дополнительные переменные
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Notifications
|
||||||
|
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
|
||||||
|
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
SENTRY_DSN=https://...@sentry.io/...
|
||||||
|
PRODUCTION_SENTRY_DSN=https://...@sentry.io/...
|
||||||
|
GOOGLE_ANALYTICS_ID=GA_MEASUREMENT_ID
|
||||||
|
|
||||||
|
# Backup
|
||||||
|
PRODUCTION_BACKUP_S3_BUCKET=catlink-backups
|
||||||
|
```
|
||||||
|
|
||||||
|
### Настройка SSH ключей
|
||||||
|
|
||||||
|
1. **Генерация SSH ключа**:
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t rsa -b 4096 -C "deploy@catlink.dev" -f deploy_key
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Кодирование в base64**:
|
||||||
|
```bash
|
||||||
|
cat deploy_key | base64 -w 0
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Добавление публичного ключа на сервер**:
|
||||||
|
```bash
|
||||||
|
cat deploy_key.pub >> ~/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Конфигурация окружений
|
||||||
|
|
||||||
|
### Staging окружение
|
||||||
|
|
||||||
|
**Цель**: Тестирование новых функций и изменений
|
||||||
|
|
||||||
|
**Характеристики**:
|
||||||
|
- Автоматический деплой с main ветки
|
||||||
|
- Тестовые данные
|
||||||
|
- Менее строгие проверки безопасности
|
||||||
|
- Быстрое развертывание
|
||||||
|
|
||||||
|
**URL**: `https://staging.catlink.dev`
|
||||||
|
|
||||||
|
### Production окружение
|
||||||
|
|
||||||
|
**Цель**: Рабочее приложение для пользователей
|
||||||
|
|
||||||
|
**Характеристики**:
|
||||||
|
- Деплой только с тегов версий
|
||||||
|
- Строгие проверки безопасности
|
||||||
|
- Резервные копии перед деплоем
|
||||||
|
- Rolling deployment без простоя
|
||||||
|
- Полный мониторинг
|
||||||
|
|
||||||
|
**URL**: `https://catlink.dev`
|
||||||
|
|
||||||
|
## 🧪 Локальное тестирование
|
||||||
|
|
||||||
|
### Make команды для CI/CD
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка качества кода
|
||||||
|
make ci-lint
|
||||||
|
|
||||||
|
# Запуск тестов
|
||||||
|
make ci-test
|
||||||
|
|
||||||
|
# Проверка безопасности
|
||||||
|
make ci-security
|
||||||
|
|
||||||
|
# Локальная сборка
|
||||||
|
make ci-build
|
||||||
|
|
||||||
|
# Полный пайплайн
|
||||||
|
make ci-pipeline
|
||||||
|
|
||||||
|
# Валидация Drone конфигурации
|
||||||
|
make drone-validate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ручной запуск скриптов
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка кода
|
||||||
|
./scripts/ci/lint.sh
|
||||||
|
|
||||||
|
# Тестирование
|
||||||
|
./scripts/ci/test.sh
|
||||||
|
|
||||||
|
# Сканирование безопасности
|
||||||
|
./scripts/ci/security-scan.sh
|
||||||
|
|
||||||
|
# Сборка
|
||||||
|
./scripts/ci/build.sh
|
||||||
|
|
||||||
|
# Продакшен сборка
|
||||||
|
./scripts/ci/build-production.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Тестирование Docker образов
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Сборка и тестирование
|
||||||
|
docker build -t catlink-backend:test backend/
|
||||||
|
docker build -t catlink-frontend:test frontend/linktree-frontend/
|
||||||
|
|
||||||
|
# Запуск для тестирования
|
||||||
|
docker run -d -p 8001:8000 --name test-backend catlink-backend:test
|
||||||
|
docker run -d -p 3001:3000 --name test-frontend catlink-frontend:test
|
||||||
|
|
||||||
|
# Проверка работоспособности
|
||||||
|
curl http://localhost:8001/api/
|
||||||
|
curl http://localhost:3001/
|
||||||
|
|
||||||
|
# Очистка
|
||||||
|
docker stop test-backend test-frontend
|
||||||
|
docker rm test-backend test-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Деплой и управление
|
||||||
|
|
||||||
|
### Автоматический деплой
|
||||||
|
|
||||||
|
**Staging деплой**:
|
||||||
|
- Триггер: Push в `main` ветку
|
||||||
|
- Время: ~10-15 минут
|
||||||
|
- Проверки: Линтинг, тесты, безопасность
|
||||||
|
|
||||||
|
**Production деплой**:
|
||||||
|
- Триггер: Создание тега `v*.*.*`
|
||||||
|
- Время: ~20-30 минут
|
||||||
|
- Проверки: Полная валидация + ручные проверки
|
||||||
|
|
||||||
|
### Ручной деплой
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Staging
|
||||||
|
make ci-deploy-staging
|
||||||
|
|
||||||
|
# Production (осторожно!)
|
||||||
|
make ci-deploy-production
|
||||||
|
```
|
||||||
|
|
||||||
|
### Создание релиза
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создание тега для production деплоя
|
||||||
|
git tag v1.2.3
|
||||||
|
git push origin v1.2.3
|
||||||
|
|
||||||
|
# Drone автоматически запустит production деплой
|
||||||
|
```
|
||||||
|
|
||||||
|
### Откат изменений
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH на сервер
|
||||||
|
ssh production
|
||||||
|
|
||||||
|
# Откат к предыдущей версии
|
||||||
|
cd /opt/catlink
|
||||||
|
./manage-production.sh restore /opt/catlink/backups/backup-production-YYYYMMDD-HHMMSS.sql.gz
|
||||||
|
|
||||||
|
# Или через Docker образы
|
||||||
|
docker-compose -f docker-compose.production.yml down
|
||||||
|
# Измените версию в docker-compose.production.yml
|
||||||
|
docker-compose -f docker-compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Мониторинг и уведомления
|
||||||
|
|
||||||
|
### Health checks
|
||||||
|
|
||||||
|
**Автоматические проверки**:
|
||||||
|
- API доступность (`/api/health/`)
|
||||||
|
- Frontend загрузка (`/`)
|
||||||
|
- Database подключение
|
||||||
|
- Redis доступность
|
||||||
|
|
||||||
|
**Мониторинг производительности**:
|
||||||
|
```bash
|
||||||
|
# Статус сервисов
|
||||||
|
./manage-production.sh status
|
||||||
|
|
||||||
|
# Проверка здоровья
|
||||||
|
./manage-production.sh health
|
||||||
|
|
||||||
|
# Мониторинг ресурсов
|
||||||
|
./monitor-production.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Уведомления
|
||||||
|
|
||||||
|
**Slack интеграция**:
|
||||||
|
```bash
|
||||||
|
# Настройка в Drone secrets
|
||||||
|
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Discord интеграция**:
|
||||||
|
```bash
|
||||||
|
# Настройка в Drone secrets
|
||||||
|
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Email уведомления**:
|
||||||
|
- Критические ошибки деплоя
|
||||||
|
- Проблемы с безопасностью
|
||||||
|
- Завершение деплоя в production
|
||||||
|
|
||||||
|
### Логи и мониторинг
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Просмотр логов деплоя
|
||||||
|
ssh production
|
||||||
|
cd /opt/catlink
|
||||||
|
./manage-production.sh logs
|
||||||
|
|
||||||
|
# Мониторинг в реальном времени
|
||||||
|
./monitor-production.sh
|
||||||
|
|
||||||
|
# Логи конкретного сервиса
|
||||||
|
./manage-production.sh logs web
|
||||||
|
./manage-production.sh logs frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Устранение неполадок
|
||||||
|
|
||||||
|
### Частые проблемы
|
||||||
|
|
||||||
|
#### 1. Ошибка аутентификации Docker registry
|
||||||
|
|
||||||
|
**Проблема**: `Error response from daemon: pull access denied`
|
||||||
|
|
||||||
|
**Решение**:
|
||||||
|
```bash
|
||||||
|
# Проверьте переменные
|
||||||
|
echo $DOCKER_USERNAME
|
||||||
|
echo $DOCKER_PASSWORD
|
||||||
|
|
||||||
|
# Перелогиньтесь
|
||||||
|
docker logout
|
||||||
|
docker login
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. SSH подключение не работает
|
||||||
|
|
||||||
|
**Проблема**: `Permission denied (publickey)`
|
||||||
|
|
||||||
|
**Решение**:
|
||||||
|
```bash
|
||||||
|
# Проверьте SSH ключ
|
||||||
|
ssh-keygen -y -f ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
# Проверьте формат в Drone
|
||||||
|
cat ~/.ssh/id_rsa | base64 -w 0
|
||||||
|
|
||||||
|
# Добавьте ключ на сервер
|
||||||
|
ssh-copy-id user@server
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Деплой завис
|
||||||
|
|
||||||
|
**Проблема**: Деплой не завершается
|
||||||
|
|
||||||
|
**Решение**:
|
||||||
|
```bash
|
||||||
|
# Проверьте статус контейнеров
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Проверьте логи
|
||||||
|
docker-compose logs
|
||||||
|
|
||||||
|
# Перезапустите сервисы
|
||||||
|
docker-compose restart
|
||||||
|
|
||||||
|
# В крайнем случае - полный перезапуск
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Тесты падают в CI
|
||||||
|
|
||||||
|
**Проблема**: Тесты проходят локально, но падают в CI
|
||||||
|
|
||||||
|
**Решение**:
|
||||||
|
```bash
|
||||||
|
# Запустите тесты локально как в CI
|
||||||
|
make ci-test
|
||||||
|
|
||||||
|
# Проверьте переменные окружения
|
||||||
|
env | grep -E "(TEST_|CI_|DJANGO_)"
|
||||||
|
|
||||||
|
# Проверьте зависимости
|
||||||
|
pip list
|
||||||
|
npm list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отладка Drone пайплайна
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Валидация конфигурации
|
||||||
|
make drone-validate
|
||||||
|
|
||||||
|
# Локальный запуск
|
||||||
|
drone exec
|
||||||
|
|
||||||
|
# Просмотр логов
|
||||||
|
drone build ls smartsoltech/links
|
||||||
|
drone build logs smartsoltech/links <build_number>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Откат деплоя
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Быстрый откат через Docker
|
||||||
|
docker tag catlink-backend:previous catlink-backend:latest
|
||||||
|
docker tag catlink-frontend:previous catlink-frontend:latest
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 2. Откат через бэкап
|
||||||
|
./manage-production.sh restore backup-file.sql.gz
|
||||||
|
|
||||||
|
# 3. Откат к конкретной версии
|
||||||
|
# Измените версию в docker-compose.production.yml
|
||||||
|
# Перезапустите сервисы
|
||||||
|
```
|
||||||
|
|
||||||
|
### Контакты для поддержки
|
||||||
|
|
||||||
|
- **Email**: support@catlink.dev
|
||||||
|
- **Slack**: #devops-support
|
||||||
|
- **Документация**: [docs/](./README.md)
|
||||||
|
- **Issues**: GitHub Issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Дополнительные ресурсы
|
||||||
|
|
||||||
|
- [Drone CI Documentation](https://docs.drone.io/)
|
||||||
|
- [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/)
|
||||||
|
- [Django Deployment](https://docs.djangoproject.com/en/stable/howto/deployment/)
|
||||||
|
- [Next.js Deployment](https://nextjs.org/docs/deployment)
|
||||||
|
- [Docker Security](https://docs.docker.com/engine/security/)
|
||||||
|
|
||||||
|
**Последнее обновление**: $(date)
|
||||||
|
**Версия документации**: 1.0
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
### 🛠️ Для разработчиков
|
### 🛠️ Для разработчиков
|
||||||
|
|
||||||
- **[MAKEFILE.md](./MAKEFILE.md)** - Полное руководство по командам Make для разработки и деплоя
|
- **[MAKEFILE.md](./MAKEFILE.md)** - Полное руководство по командам Make для разработки и деплоя
|
||||||
|
- **[CICD.md](./CICD.md)** - Документация по CI/CD пайплайну и автоматизации
|
||||||
- **[DEPLOYMENT.md](./DEPLOYMENT.md)** - Инструкции по развертыванию в продакшене
|
- **[DEPLOYMENT.md](./DEPLOYMENT.md)** - Инструкции по развертыванию в продакшене
|
||||||
- **[FIXES.md](./FIXES.md)** - Известные проблемы и их решения
|
- **[FIXES.md](./FIXES.md)** - Известные проблемы и их решения
|
||||||
|
|
||||||
@@ -19,10 +20,12 @@
|
|||||||
### Для начинающих
|
### Для начинающих
|
||||||
1. Начните с [README.md](../README.md) в корне проекта
|
1. Начните с [README.md](../README.md) в корне проекта
|
||||||
2. Изучите [MAKEFILE.md](./MAKEFILE.md) для понимания команд
|
2. Изучите [MAKEFILE.md](./MAKEFILE.md) для понимания команд
|
||||||
3. Следуйте инструкциям в [DEPLOYMENT.md](./DEPLOYMENT.md) для деплоя
|
3. Ознакомьтесь с [CICD.md](./CICD.md) для понимания автоматизации
|
||||||
|
4. Следуйте инструкциям в [DEPLOYMENT.md](./DEPLOYMENT.md) для деплоя
|
||||||
|
|
||||||
### Для опытных разработчиков
|
### Для опытных разработчиков
|
||||||
- Используйте `make help` для просмотра всех команд
|
- Используйте `make help` для просмотра всех команд
|
||||||
|
- Изучите [CICD.md](./CICD.md) для настройки автоматизации
|
||||||
- Проверьте [FIXES.md](./FIXES.md) при возникновении проблем
|
- Проверьте [FIXES.md](./FIXES.md) при возникновении проблем
|
||||||
- Следуйте документации в [DEPLOYMENT.md](./DEPLOYMENT.md) для продакшена
|
- Следуйте документации в [DEPLOYMENT.md](./DEPLOYMENT.md) для продакшена
|
||||||
|
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
docs/
|
docs/
|
||||||
├── README.md # Этот файл - обзор документации
|
├── README.md # Этот файл - обзор документации
|
||||||
├── MAKEFILE.md # Руководство по командам Make
|
├── MAKEFILE.md # Руководство по командам Make
|
||||||
|
├── CICD.md # Документация по CI/CD и автоматизации
|
||||||
├── DEPLOYMENT.md # Инструкции по деплою
|
├── DEPLOYMENT.md # Инструкции по деплою
|
||||||
├── FIXES.md # Исправления и решения проблем
|
├── FIXES.md # Исправления и решения проблем
|
||||||
└── COVER_OVERLAY_TESTING.md # Документация по тестированию
|
└── COVER_OVERLAY_TESTING.md # Документация по тестированию
|
||||||
@@ -55,10 +59,21 @@ make backup # Создать бэкап БД
|
|||||||
make restore # Восстановить БД
|
make restore # Восстановить БД
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CI/CD и автоматизация
|
||||||
|
```bash
|
||||||
|
make ci-lint # Проверка кода (CI)
|
||||||
|
make ci-test # Запуск тестов (CI)
|
||||||
|
make ci-security # Проверка безопасности
|
||||||
|
make ci-pipeline # Полный CI/CD пайплайн
|
||||||
|
make drone-validate # Валидация Drone конфигурации
|
||||||
|
```
|
||||||
|
|
||||||
### Деплой
|
### Деплой
|
||||||
```bash
|
```bash
|
||||||
make deploy # Деплой в продакшен
|
make deploy # Деплой в продакшен
|
||||||
make ssl-setup # Настройка SSL
|
make ssl-setup # Настройка SSL
|
||||||
|
make ci-deploy-staging # Деплой на staging
|
||||||
|
make ci-deploy-production # Деплой на production
|
||||||
```
|
```
|
||||||
|
|
||||||
Подробнее см. [MAKEFILE.md](./MAKEFILE.md)
|
Подробнее см. [MAKEFILE.md](./MAKEFILE.md)
|
||||||
|
|||||||
387
scripts/ci/build-production.sh
Executable file
387
scripts/ci/build-production.sh
Executable file
@@ -0,0 +1,387 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/build-production.sh - Сборка продакшен образов
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Building production images..."
|
||||||
|
|
||||||
|
# Переменные
|
||||||
|
REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"}
|
||||||
|
PROJECT_NAME="catlink"
|
||||||
|
VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}
|
||||||
|
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
VCS_REF=${DRONE_COMMIT_SHA:-$(git rev-parse HEAD)}
|
||||||
|
|
||||||
|
echo "📋 Build information:"
|
||||||
|
echo " • Registry: $REGISTRY"
|
||||||
|
echo " • Project: $PROJECT_NAME"
|
||||||
|
echo " • Version: $VERSION"
|
||||||
|
echo " • Build Date: $BUILD_DATE"
|
||||||
|
echo " • VCS Ref: $VCS_REF"
|
||||||
|
|
||||||
|
# Создание production .env
|
||||||
|
echo "⚙️ Creating production environment configuration..."
|
||||||
|
cat > .env.production << EOF
|
||||||
|
# Production Environment Configuration
|
||||||
|
NODE_ENV=production
|
||||||
|
DJANGO_ENV=production
|
||||||
|
DEBUG=False
|
||||||
|
|
||||||
|
# Security
|
||||||
|
SECRET_KEY=\${SECRET_KEY}
|
||||||
|
ALLOWED_HOSTS=\${ALLOWED_HOSTS}
|
||||||
|
CORS_ALLOWED_ORIGINS=\${CORS_ALLOWED_ORIGINS}
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=\${DATABASE_URL}
|
||||||
|
|
||||||
|
# Media and Static
|
||||||
|
STATIC_URL=/static/
|
||||||
|
MEDIA_URL=/media/
|
||||||
|
STATIC_ROOT=/app/staticfiles
|
||||||
|
MEDIA_ROOT=/app/media
|
||||||
|
|
||||||
|
# Build info
|
||||||
|
BUILD_VERSION=$VERSION
|
||||||
|
BUILD_DATE=$BUILD_DATE
|
||||||
|
VCS_REF=$VCS_REF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание production docker-compose файла
|
||||||
|
echo "🐳 Creating production Docker Compose configuration..."
|
||||||
|
cat > docker-compose.production.yml << EOF
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
BUILD_VERSION: $VERSION
|
||||||
|
BUILD_DATE: $BUILD_DATE
|
||||||
|
VCS_REF: $VCS_REF
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DEBUG=False
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink-api.rule=Host(\`api.catlink.dev\`)"
|
||||||
|
- "traefik.http.services.catlink-api.loadbalancer.server.port=8000"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend/linktree-frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
BUILD_VERSION: $VERSION
|
||||||
|
BUILD_DATE: $BUILD_DATE
|
||||||
|
VCS_REF: $VCS_REF
|
||||||
|
NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL}
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink.rule=Host(\`catlink.dev\`)"
|
||||||
|
- "traefik.http.services.catlink.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
name: traefik_default
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Обновление Dockerfile для production
|
||||||
|
echo "📝 Updating Dockerfiles for production..."
|
||||||
|
|
||||||
|
# Backend Dockerfile
|
||||||
|
cat > backend/Dockerfile.production << EOF
|
||||||
|
FROM python:3.11-slim as builder
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG BUILD_VERSION=latest
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG VCS_REF
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
LABEL org.opencontainers.image.title="CatLink Backend"
|
||||||
|
LABEL org.opencontainers.image.description="CatLink Django API"
|
||||||
|
LABEL org.opencontainers.image.version="\$BUILD_VERSION"
|
||||||
|
LABEL org.opencontainers.image.created="\$BUILD_DATE"
|
||||||
|
LABEL org.opencontainers.image.revision="\$VCS_REF"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/smartsoltech/links"
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \\
|
||||||
|
gcc \\
|
||||||
|
postgresql-client \\
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set work directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy project
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
RUN mkdir -p staticfiles media
|
||||||
|
|
||||||
|
# Collect static files
|
||||||
|
RUN python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN useradd --create-home --shell /bin/bash app \\
|
||||||
|
&& chown -R app:app /app
|
||||||
|
USER app
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:8000/api/ || exit 1
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "backend.wsgi:application"]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Frontend Dockerfile
|
||||||
|
cat > frontend/linktree-frontend/Dockerfile.production << EOF
|
||||||
|
FROM node:20-alpine as builder
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG BUILD_VERSION=latest
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG VCS_REF
|
||||||
|
ARG NEXT_PUBLIC_API_URL
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
LABEL org.opencontainers.image.title="CatLink Frontend"
|
||||||
|
LABEL org.opencontainers.image.description="CatLink Next.js Application"
|
||||||
|
LABEL org.opencontainers.image.version="\$BUILD_VERSION"
|
||||||
|
LABEL org.opencontainers.image.created="\$BUILD_DATE"
|
||||||
|
LABEL org.opencontainers.image.revision="\$VCS_REF"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/smartsoltech/links"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Set build environment
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_PUBLIC_API_URL=\$NEXT_PUBLIC_API_URL
|
||||||
|
ENV BUILD_VERSION=\$BUILD_VERSION
|
||||||
|
|
||||||
|
# Build application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM node:20-alpine as runner
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy built application
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:3000 || exit 1
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Сборка продакшен образов
|
||||||
|
echo "🔨 Building production Docker images..."
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
echo " • Building backend production image..."
|
||||||
|
docker build -f backend/Dockerfile.production \\
|
||||||
|
--build-arg BUILD_VERSION="$VERSION" \\
|
||||||
|
--build-arg BUILD_DATE="$BUILD_DATE" \\
|
||||||
|
--build-arg VCS_REF="$VCS_REF" \\
|
||||||
|
-t "$REGISTRY/$PROJECT_NAME-backend:$VERSION" \\
|
||||||
|
-t "$REGISTRY/$PROJECT_NAME-backend:latest" \\
|
||||||
|
backend/
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
echo " • Building frontend production image..."
|
||||||
|
docker build -f frontend/linktree-frontend/Dockerfile.production \\
|
||||||
|
--build-arg BUILD_VERSION="$VERSION" \\
|
||||||
|
--build-arg BUILD_DATE="$BUILD_DATE" \\
|
||||||
|
--build-arg VCS_REF="$VCS_REF" \\
|
||||||
|
--build-arg NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL" \\
|
||||||
|
-t "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" \\
|
||||||
|
-t "$REGISTRY/$PROJECT_NAME-frontend:latest" \\
|
||||||
|
frontend/linktree-frontend/
|
||||||
|
|
||||||
|
# Проверка размеров образов
|
||||||
|
echo "📊 Production image sizes:"
|
||||||
|
docker images | grep "$PROJECT_NAME" | grep -E "($VERSION|latest)"
|
||||||
|
|
||||||
|
# Сканирование образов на уязвимости (если доступно)
|
||||||
|
echo "🔍 Scanning production images..."
|
||||||
|
for image in "$PROJECT_NAME-backend:$VERSION" "$PROJECT_NAME-frontend:$VERSION"; do
|
||||||
|
echo " • Scanning $image..."
|
||||||
|
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\
|
||||||
|
aquasec/trivy image --exit-code 0 --severity HIGH,CRITICAL "$REGISTRY/$image" || \\
|
||||||
|
echo " ⚠️ Vulnerability scan completed with findings"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Тестирование продакшен образов
|
||||||
|
echo "🧪 Testing production images..."
|
||||||
|
|
||||||
|
# Создание тестового docker-compose для продакшен образов
|
||||||
|
cat > docker-compose.test-prod.yml << EOF
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
web-prod:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DEBUG=False
|
||||||
|
- SECRET_KEY=test-secret-key
|
||||||
|
- DATABASE_URL=sqlite:///db.sqlite3
|
||||||
|
- ALLOWED_HOSTS=localhost,127.0.0.1
|
||||||
|
ports:
|
||||||
|
- "8001:8000"
|
||||||
|
|
||||||
|
frontend-prod:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- NEXT_PUBLIC_API_URL=http://localhost:8001
|
||||||
|
ports:
|
||||||
|
- "3001:3000"
|
||||||
|
depends_on:
|
||||||
|
- web-prod
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Запуск тестов продакшен образов
|
||||||
|
echo " • Starting production containers for testing..."
|
||||||
|
docker-compose -f docker-compose.test-prod.yml up -d
|
||||||
|
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Проверка здоровья
|
||||||
|
backend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8001/api/ || echo "000")
|
||||||
|
frontend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3001 || echo "000")
|
||||||
|
|
||||||
|
echo " • Backend health: $backend_health"
|
||||||
|
echo " • Frontend health: $frontend_health"
|
||||||
|
|
||||||
|
# Очистка тестовых контейнеров
|
||||||
|
docker-compose -f docker-compose.test-prod.yml down
|
||||||
|
|
||||||
|
if [ "$backend_health" = "200" ] && [ "$frontend_health" = "200" ]; then
|
||||||
|
echo "✅ Production images tested successfully!"
|
||||||
|
else
|
||||||
|
echo "❌ Production image testing failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание манифестов для деплоя
|
||||||
|
echo "📄 Creating deployment manifests..."
|
||||||
|
mkdir -p /tmp/deploy-manifests
|
||||||
|
|
||||||
|
# Kubernetes манифесты
|
||||||
|
cat > /tmp/deploy-manifests/catlink-k8s.yml << EOF
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: catlink-backend
|
||||||
|
labels:
|
||||||
|
app: catlink-backend
|
||||||
|
version: $VERSION
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: catlink-backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: catlink-backend
|
||||||
|
version: $VERSION
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
env:
|
||||||
|
- name: DJANGO_ENV
|
||||||
|
value: "production"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: catlink-frontend
|
||||||
|
labels:
|
||||||
|
app: catlink-frontend
|
||||||
|
version: $VERSION
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: catlink-frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: catlink-frontend
|
||||||
|
version: $VERSION
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
env:
|
||||||
|
- name: NODE_ENV
|
||||||
|
value: "production"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "200m"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Production build completed successfully!"
|
||||||
|
echo "📦 Images built:"
|
||||||
|
echo " • $REGISTRY/$PROJECT_NAME-backend:$VERSION"
|
||||||
|
echo " • $REGISTRY/$PROJECT_NAME-frontend:$VERSION"
|
||||||
|
echo "📁 Deployment manifests: /tmp/deploy-manifests/"
|
||||||
93
scripts/ci/build.sh
Executable file
93
scripts/ci/build.sh
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/build.sh - Сборка приложения
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🏗️ Building CatLink application..."
|
||||||
|
|
||||||
|
# Проверка окружения
|
||||||
|
echo "📋 Checking environment..."
|
||||||
|
echo " • Docker version: $(docker --version)"
|
||||||
|
echo " • Docker Compose version: $(docker-compose --version)"
|
||||||
|
echo " • Current directory: $(pwd)"
|
||||||
|
echo " • Available space: $(df -h . | tail -1 | awk '{print $4}')"
|
||||||
|
|
||||||
|
# Создание .env файла для CI если не существует
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
echo "⚙️ Creating .env file for CI..."
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# Установка переменных для CI
|
||||||
|
cat >> .env << EOF
|
||||||
|
|
||||||
|
# CI/CD Variables
|
||||||
|
CI=true
|
||||||
|
NODE_ENV=test
|
||||||
|
DJANGO_ENV=test
|
||||||
|
DEBUG=False
|
||||||
|
DATABASE_URL=postgres://catlink:catlink@postgres:5432/catlink_test
|
||||||
|
SECRET_KEY=ci-secret-key-for-testing-only
|
||||||
|
ALLOWED_HOSTS=localhost,127.0.0.1,testserver
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Очистка предыдущих сборок
|
||||||
|
echo "🧹 Cleaning up previous builds..."
|
||||||
|
docker-compose down --remove-orphans || true
|
||||||
|
docker system prune -f || true
|
||||||
|
|
||||||
|
# Сборка образов
|
||||||
|
echo "🔨 Building Docker images..."
|
||||||
|
echo " • Building backend image..."
|
||||||
|
docker-compose build web --no-cache
|
||||||
|
|
||||||
|
echo " • Building frontend image..."
|
||||||
|
docker-compose build frontend --no-cache
|
||||||
|
|
||||||
|
# Проверка размера образов
|
||||||
|
echo "📊 Checking image sizes..."
|
||||||
|
docker images | grep -E "(catlink|links)" | head -10
|
||||||
|
|
||||||
|
# Запуск контейнеров для проверки
|
||||||
|
echo "🚀 Starting containers for verification..."
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Ожидание готовности сервисов
|
||||||
|
echo "⏳ Waiting for services to be ready..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Проверка статуса контейнеров
|
||||||
|
echo "🔍 Checking container status..."
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Проверка логов на ошибки
|
||||||
|
echo "📋 Checking logs for errors..."
|
||||||
|
docker-compose logs web | tail -20
|
||||||
|
docker-compose logs frontend | tail -20
|
||||||
|
|
||||||
|
# Проверка здоровья сервисов
|
||||||
|
echo "🏥 Health check..."
|
||||||
|
backend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/api/ || echo "000")
|
||||||
|
frontend_health=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 || echo "000")
|
||||||
|
|
||||||
|
echo " • Backend API health: $backend_health"
|
||||||
|
echo " • Frontend health: $frontend_health"
|
||||||
|
|
||||||
|
if [ "$backend_health" = "200" ] && [ "$frontend_health" = "200" ]; then
|
||||||
|
echo "✅ Build completed successfully!"
|
||||||
|
echo " • Backend: http://localhost:8000"
|
||||||
|
echo " • Frontend: http://localhost:3000"
|
||||||
|
else
|
||||||
|
echo "❌ Build failed - services not responding properly"
|
||||||
|
echo "Backend logs:"
|
||||||
|
docker-compose logs web | tail -50
|
||||||
|
echo "Frontend logs:"
|
||||||
|
docker-compose logs frontend | tail -50
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Экспорт образов для последующих этапов
|
||||||
|
echo "💾 Exporting images for next stages..."
|
||||||
|
docker save -o /tmp/catlink-images.tar $(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "(catlink|links)")
|
||||||
|
|
||||||
|
echo "🎉 Build stage completed successfully!"
|
||||||
869
scripts/ci/deploy-production.sh
Executable file
869
scripts/ci/deploy-production.sh
Executable file
@@ -0,0 +1,869 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/deploy-production.sh - Деплой на production окружение
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Deploying to production environment..."
|
||||||
|
|
||||||
|
# Переменные
|
||||||
|
REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"}
|
||||||
|
PROJECT_NAME="catlink"
|
||||||
|
VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}
|
||||||
|
PRODUCTION_HOST=${PRODUCTION_HOST:-"catlink.dev"}
|
||||||
|
PRODUCTION_USER=${PRODUCTION_USER:-"deploy"}
|
||||||
|
PRODUCTION_PORT=${PRODUCTION_PORT:-"22"}
|
||||||
|
|
||||||
|
echo "📋 Production deployment information:"
|
||||||
|
echo " • Registry: $REGISTRY"
|
||||||
|
echo " • Project: $PROJECT_NAME"
|
||||||
|
echo " • Version: $VERSION"
|
||||||
|
echo " • Host: $PRODUCTION_HOST"
|
||||||
|
echo " • User: $PRODUCTION_USER"
|
||||||
|
|
||||||
|
# Строгая проверка для production
|
||||||
|
echo "🔒 Performing production deployment checks..."
|
||||||
|
|
||||||
|
# Проверка обязательных переменных
|
||||||
|
REQUIRED_VARS=(
|
||||||
|
"PRODUCTION_HOST"
|
||||||
|
"PRODUCTION_SSH_KEY"
|
||||||
|
"PRODUCTION_SECRET_KEY"
|
||||||
|
"PRODUCTION_POSTGRES_PASSWORD"
|
||||||
|
"PRODUCTION_EMAIL_HOST"
|
||||||
|
"PRODUCTION_EMAIL_PASSWORD"
|
||||||
|
)
|
||||||
|
|
||||||
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
|
if [ -z "${!var}" ]; then
|
||||||
|
echo "❌ Required production variable $var is not set!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Проверка версии (только теги для production)
|
||||||
|
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && [ -z "$FORCE_PRODUCTION_DEPLOY" ]; then
|
||||||
|
echo "❌ Production deployment requires a proper version tag (vX.Y.Z)"
|
||||||
|
echo "Current version: $VERSION"
|
||||||
|
echo "Set FORCE_PRODUCTION_DEPLOY=true to override this check"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка существования образов
|
||||||
|
echo "🔍 Verifying production images exist..."
|
||||||
|
for image in "backend" "frontend"; do
|
||||||
|
if ! docker manifest inspect "$REGISTRY/$PROJECT_NAME-$image:$VERSION" > /dev/null 2>&1; then
|
||||||
|
echo "❌ Production image $REGISTRY/$PROJECT_NAME-$image:$VERSION not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✅ $REGISTRY/$PROJECT_NAME-$image:$VERSION verified"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Настройка SSH для production
|
||||||
|
echo "🔐 Setting up secure SSH connection to production..."
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
|
||||||
|
# Создание SSH ключа
|
||||||
|
echo "$PRODUCTION_SSH_KEY" | base64 -d > ~/.ssh/id_production
|
||||||
|
chmod 600 ~/.ssh/id_production
|
||||||
|
|
||||||
|
# Добавление хоста в known_hosts
|
||||||
|
ssh-keyscan -p "$PRODUCTION_PORT" "$PRODUCTION_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
|
# SSH конфигурация для production
|
||||||
|
cat > ~/.ssh/config << EOF
|
||||||
|
Host production
|
||||||
|
HostName $PRODUCTION_HOST
|
||||||
|
User $PRODUCTION_USER
|
||||||
|
Port $PRODUCTION_PORT
|
||||||
|
IdentityFile ~/.ssh/id_production
|
||||||
|
StrictHostKeyChecking yes
|
||||||
|
UserKnownHostsFile ~/.ssh/known_hosts
|
||||||
|
ServerAliveInterval 60
|
||||||
|
ServerAliveCountMax 3
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Проверка подключения к production серверу
|
||||||
|
echo "🔗 Testing production server connection..."
|
||||||
|
if ! ssh production "echo 'Production connection successful'" > /dev/null 2>&1; then
|
||||||
|
echo "❌ Failed to connect to production server"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Successfully connected to production server"
|
||||||
|
|
||||||
|
# Подготовка файлов для production деплоя
|
||||||
|
echo "📦 Preparing production deployment files..."
|
||||||
|
mkdir -p /tmp/production-deploy
|
||||||
|
|
||||||
|
# Создание production docker-compose
|
||||||
|
cat > /tmp/production-deploy/docker-compose.production.yml << EOF
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: catlink_production
|
||||||
|
POSTGRES_USER: catlink_user
|
||||||
|
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_INITDB_ARGS: "--auth-host=md5"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./backups:/backups
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U catlink_user -d catlink_production"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
cpus: '1.0'
|
||||||
|
reservations:
|
||||||
|
memory: 1G
|
||||||
|
cpus: '0.5'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: >
|
||||||
|
redis-server
|
||||||
|
--appendonly yes
|
||||||
|
--maxmemory 512mb
|
||||||
|
--maxmemory-policy allkeys-lru
|
||||||
|
--save 900 1
|
||||||
|
--save 300 10
|
||||||
|
--save 60 10000
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.5'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "5m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DEBUG=False
|
||||||
|
- SECRET_KEY=\${SECRET_KEY}
|
||||||
|
- DATABASE_URL=postgresql://catlink_user:\${POSTGRES_PASSWORD}@postgres:5432/catlink_production
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
- ALLOWED_HOSTS=$PRODUCTION_HOST,www.$PRODUCTION_HOST
|
||||||
|
- CORS_ALLOWED_ORIGINS=https://$PRODUCTION_HOST,https://www.$PRODUCTION_HOST
|
||||||
|
- EMAIL_HOST=\${EMAIL_HOST}
|
||||||
|
- EMAIL_PORT=587
|
||||||
|
- EMAIL_USE_TLS=True
|
||||||
|
- EMAIL_HOST_USER=\${EMAIL_HOST_USER}
|
||||||
|
- EMAIL_HOST_PASSWORD=\${EMAIL_HOST_PASSWORD}
|
||||||
|
- DEFAULT_FROM_EMAIL=noreply@$PRODUCTION_HOST
|
||||||
|
- SENTRY_DSN=\${SENTRY_DSN}
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||||
|
volumes:
|
||||||
|
- media_data:/app/media
|
||||||
|
- static_data:/app/staticfiles
|
||||||
|
- ./logs:/app/logs
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 15s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
replicas: 2
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 1G
|
||||||
|
cpus: '0.8'
|
||||||
|
reservations:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.4'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "20m"
|
||||||
|
max-file: "5"
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink-api.rule=Host(\`$PRODUCTION_HOST\`,\`www.$PRODUCTION_HOST\`) && PathPrefix(\`/api\`)"
|
||||||
|
- "traefik.http.routers.catlink-api.tls=true"
|
||||||
|
- "traefik.http.routers.catlink-api.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.catlink-api.loadbalancer.server.port=8000"
|
||||||
|
- "traefik.http.services.catlink-api.loadbalancer.healthcheck.path=/api/health/"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- NEXT_PUBLIC_API_URL=https://$PRODUCTION_HOST/api
|
||||||
|
- NEXT_PUBLIC_APP_ENV=production
|
||||||
|
- NEXT_PUBLIC_SENTRY_DSN=\${FRONTEND_SENTRY_DSN}
|
||||||
|
- NEXT_PUBLIC_GOOGLE_ANALYTICS=\${GOOGLE_ANALYTICS_ID}
|
||||||
|
depends_on:
|
||||||
|
web:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 15s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
replicas: 2
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.5'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink.rule=Host(\`$PRODUCTION_HOST\`,\`www.$PRODUCTION_HOST\`)"
|
||||||
|
- "traefik.http.routers.catlink.tls=true"
|
||||||
|
- "traefik.http.routers.catlink.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.catlink.loadbalancer.server.port=3000"
|
||||||
|
- "traefik.http.services.catlink.loadbalancer.healthcheck.path=/health"
|
||||||
|
- "traefik.http.middlewares.catlink-redirect.redirectregex.regex=^https://www.$PRODUCTION_HOST/(.*)"
|
||||||
|
- "traefik.http.middlewares.catlink-redirect.redirectregex.replacement=https://$PRODUCTION_HOST/\$\${1}"
|
||||||
|
- "traefik.http.routers.catlink.middlewares=catlink-redirect"
|
||||||
|
|
||||||
|
celery:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
command: celery -A backend worker -l info --concurrency=2
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DEBUG=False
|
||||||
|
- SECRET_KEY=\${SECRET_KEY}
|
||||||
|
- DATABASE_URL=postgresql://catlink_user:\${POSTGRES_PASSWORD}@postgres:5432/catlink_production
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||||
|
volumes:
|
||||||
|
- media_data:/app/media
|
||||||
|
- ./logs:/app/logs
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
cpus: '0.5'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
celery-beat:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
command: celery -A backend beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DEBUG=False
|
||||||
|
- SECRET_KEY=\${SECRET_KEY}
|
||||||
|
- DATABASE_URL=postgresql://catlink_user:\${POSTGRES_PASSWORD}@postgres:5432/catlink_production
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||||
|
volumes:
|
||||||
|
- ./logs:/app/logs
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.25'
|
||||||
|
reservations:
|
||||||
|
memory: 128M
|
||||||
|
cpus: '0.1'
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "5m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
driver_opts:
|
||||||
|
type: none
|
||||||
|
o: bind
|
||||||
|
device: /opt/catlink/data/postgres
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
|
driver_opts:
|
||||||
|
type: none
|
||||||
|
o: bind
|
||||||
|
device: /opt/catlink/data/redis
|
||||||
|
media_data:
|
||||||
|
driver: local
|
||||||
|
driver_opts:
|
||||||
|
type: none
|
||||||
|
o: bind
|
||||||
|
device: /opt/catlink/data/media
|
||||||
|
static_data:
|
||||||
|
driver: local
|
||||||
|
driver_opts:
|
||||||
|
type: none
|
||||||
|
o: bind
|
||||||
|
device: /opt/catlink/data/static
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
name: traefik_default
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание production environment файла
|
||||||
|
cat > /tmp/production-deploy/.env.production << EOF
|
||||||
|
# Production Environment Variables
|
||||||
|
COMPOSE_PROJECT_NAME=catlink-production
|
||||||
|
|
||||||
|
# Database
|
||||||
|
POSTGRES_PASSWORD=$PRODUCTION_POSTGRES_PASSWORD
|
||||||
|
|
||||||
|
# Django
|
||||||
|
SECRET_KEY=$PRODUCTION_SECRET_KEY
|
||||||
|
|
||||||
|
# Email
|
||||||
|
EMAIL_HOST=$PRODUCTION_EMAIL_HOST
|
||||||
|
EMAIL_HOST_USER=$PRODUCTION_EMAIL_USER
|
||||||
|
EMAIL_HOST_PASSWORD=$PRODUCTION_EMAIL_PASSWORD
|
||||||
|
|
||||||
|
# Monitoring (если настроено)
|
||||||
|
SENTRY_DSN=${PRODUCTION_SENTRY_DSN:-}
|
||||||
|
FRONTEND_SENTRY_DSN=${PRODUCTION_FRONTEND_SENTRY_DSN:-}
|
||||||
|
GOOGLE_ANALYTICS_ID=${PRODUCTION_GOOGLE_ANALYTICS_ID:-}
|
||||||
|
|
||||||
|
# Application info
|
||||||
|
APP_VERSION=$VERSION
|
||||||
|
DEPLOY_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
COMMIT_SHA=${DRONE_COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo "unknown")}
|
||||||
|
|
||||||
|
# Backup settings
|
||||||
|
BACKUP_SCHEDULE=0 2 * * *
|
||||||
|
BACKUP_RETENTION_DAYS=30
|
||||||
|
BACKUP_S3_BUCKET=${PRODUCTION_BACKUP_S3_BUCKET:-}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание скрипта управления production
|
||||||
|
cat > /tmp/production-deploy/manage-production.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Production management script
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
COMPOSE_FILE="docker-compose.production.yml"
|
||||||
|
PROJECT_NAME="catlink-production"
|
||||||
|
BACKUP_DIR="/opt/catlink/backups"
|
||||||
|
|
||||||
|
# Функция логирования
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a /var/log/catlink-production.log
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
"start")
|
||||||
|
log "🚀 Starting production environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d
|
||||||
|
log "✅ Production environment started"
|
||||||
|
;;
|
||||||
|
"stop")
|
||||||
|
log "🛑 Stopping production environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME down
|
||||||
|
log "✅ Production environment stopped"
|
||||||
|
;;
|
||||||
|
"restart")
|
||||||
|
log "🔄 Restarting production environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME restart
|
||||||
|
log "✅ Production environment restarted"
|
||||||
|
;;
|
||||||
|
"logs")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME logs -f ${2:-}
|
||||||
|
;;
|
||||||
|
"status")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME ps
|
||||||
|
;;
|
||||||
|
"update")
|
||||||
|
log "📦 Updating production environment..."
|
||||||
|
|
||||||
|
# Создание бэкапа перед обновлением
|
||||||
|
$0 backup
|
||||||
|
|
||||||
|
# Обновление образов
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME pull
|
||||||
|
|
||||||
|
# Rolling update
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d --no-deps --scale web=1 web
|
||||||
|
sleep 30
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d --no-deps --scale web=2 web
|
||||||
|
|
||||||
|
# Обновление frontend
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d --no-deps --scale frontend=1 frontend
|
||||||
|
sleep 30
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d --no-deps --scale frontend=2 frontend
|
||||||
|
|
||||||
|
log "✅ Production environment updated"
|
||||||
|
;;
|
||||||
|
"backup")
|
||||||
|
log "💾 Creating production backup..."
|
||||||
|
mkdir -p $BACKUP_DIR
|
||||||
|
|
||||||
|
BACKUP_FILE="$BACKUP_DIR/backup-production-$(date +%Y%m%d-%H%M%S).sql"
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec -T postgres pg_dump -U catlink_user catlink_production > "$BACKUP_FILE"
|
||||||
|
|
||||||
|
# Компрессия бэкапа
|
||||||
|
gzip "$BACKUP_FILE"
|
||||||
|
|
||||||
|
# Бэкап медиа файлов
|
||||||
|
tar -czf "$BACKUP_DIR/media-backup-$(date +%Y%m%d-%H%M%S).tar.gz" -C /opt/catlink/data media/
|
||||||
|
|
||||||
|
# Очистка старых бэкапов (старше 30 дней)
|
||||||
|
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
|
||||||
|
|
||||||
|
log "✅ Backup created: $BACKUP_FILE.gz"
|
||||||
|
;;
|
||||||
|
"restore")
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
echo "Usage: $0 restore <backup-file>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "🔄 Restoring from backup: $2"
|
||||||
|
|
||||||
|
# Остановка приложения
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME stop web frontend celery celery-beat
|
||||||
|
|
||||||
|
# Восстановление БД
|
||||||
|
gunzip -c "$2" | docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec -T postgres psql -U catlink_user catlink_production
|
||||||
|
|
||||||
|
# Запуск приложения
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME start web frontend celery celery-beat
|
||||||
|
|
||||||
|
log "✅ Restore completed"
|
||||||
|
;;
|
||||||
|
"migrate")
|
||||||
|
log "🔄 Running database migrations..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec web python manage.py migrate
|
||||||
|
log "✅ Migrations completed"
|
||||||
|
;;
|
||||||
|
"collectstatic")
|
||||||
|
log "📦 Collecting static files..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec web python manage.py collectstatic --noinput
|
||||||
|
log "✅ Static files collected"
|
||||||
|
;;
|
||||||
|
"shell")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec web python manage.py shell
|
||||||
|
;;
|
||||||
|
"health")
|
||||||
|
echo "🏥 Production health check:"
|
||||||
|
|
||||||
|
# Проверка контейнеров
|
||||||
|
echo "📦 Container status:"
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME ps
|
||||||
|
|
||||||
|
# Проверка эндпоинтов
|
||||||
|
echo ""
|
||||||
|
echo "🌐 Endpoint health:"
|
||||||
|
api_status=$(curl -s -o /dev/null -w "%{http_code}" "https://catlink.dev/api/health/" || echo "000")
|
||||||
|
frontend_status=$(curl -s -o /dev/null -w "%{http_code}" "https://catlink.dev/" || echo "000")
|
||||||
|
|
||||||
|
if [ "$api_status" = "200" ]; then
|
||||||
|
echo " ✅ API: OK ($api_status)"
|
||||||
|
else
|
||||||
|
echo " ❌ API: FAILED ($api_status)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$frontend_status" = "200" ]; then
|
||||||
|
echo " ✅ Frontend: OK ($frontend_status)"
|
||||||
|
else
|
||||||
|
echo " ❌ Frontend: FAILED ($frontend_status)"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Production Management Script"
|
||||||
|
echo ""
|
||||||
|
echo "Usage: $0 {start|stop|restart|logs|status|update|backup|restore|migrate|collectstatic|shell|health}"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " start - Start production environment"
|
||||||
|
echo " stop - Stop production environment"
|
||||||
|
echo " restart - Restart production environment"
|
||||||
|
echo " logs - Show logs (optionally specify service)"
|
||||||
|
echo " status - Show containers status"
|
||||||
|
echo " update - Update images and restart (with backup)"
|
||||||
|
echo " backup - Create database and media backup"
|
||||||
|
echo " restore - Restore from backup file"
|
||||||
|
echo " migrate - Run database migrations"
|
||||||
|
echo " collectstatic- Collect static files"
|
||||||
|
echo " shell - Open Django shell"
|
||||||
|
echo " health - Check production health"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x /tmp/production-deploy/manage-production.sh
|
||||||
|
|
||||||
|
# Создание скрипта мониторинга production
|
||||||
|
cat > /tmp/production-deploy/monitor-production.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Production monitoring script
|
||||||
|
|
||||||
|
PROJECT_NAME="catlink-production"
|
||||||
|
HEALTH_CHECK_URL="https://catlink.dev/api/health/"
|
||||||
|
FRONTEND_URL="https://catlink.dev/"
|
||||||
|
LOG_FILE="/var/log/catlink-monitor.log"
|
||||||
|
|
||||||
|
# Функция логирования
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
log "🔍 Starting production monitoring..."
|
||||||
|
|
||||||
|
# Проверка контейнеров
|
||||||
|
log "📦 Checking container status..."
|
||||||
|
container_status=$(docker-compose -p $PROJECT_NAME ps --format json | jq -r '.[] | "\(.Name): \(.State)"')
|
||||||
|
log "Container status: $container_status"
|
||||||
|
|
||||||
|
# Проверка здоровья сервисов
|
||||||
|
log "🏥 Checking service health..."
|
||||||
|
|
||||||
|
# Backend API
|
||||||
|
api_response=$(curl -s -w "HTTPSTATUS:%{http_code};TIME:%{time_total}" "$HEALTH_CHECK_URL" || echo "HTTPSTATUS:000;TIME:0")
|
||||||
|
api_status=$(echo $api_response | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2)
|
||||||
|
api_time=$(echo $api_response | grep -o "TIME:[0-9.]*" | cut -d: -f2)
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
frontend_response=$(curl -s -w "HTTPSTATUS:%{http_code};TIME:%{time_total}" "$FRONTEND_URL" || echo "HTTPSTATUS:000;TIME:0")
|
||||||
|
frontend_status=$(echo $frontend_response | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2)
|
||||||
|
frontend_time=$(echo $frontend_response | grep -o "TIME:[0-9.]*" | cut -d: -f2)
|
||||||
|
|
||||||
|
# Проверка производительности
|
||||||
|
log "📊 Performance metrics:"
|
||||||
|
log "API: $api_status (${api_time}s)"
|
||||||
|
log "Frontend: $frontend_status (${api_time}s)"
|
||||||
|
|
||||||
|
# Проверка дискового пространства
|
||||||
|
disk_usage=$(df -h /opt/catlink | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||||
|
log "Disk usage: ${disk_usage}%"
|
||||||
|
|
||||||
|
# Проверка памяти
|
||||||
|
memory_usage=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100}')
|
||||||
|
log "Memory usage: ${memory_usage}%"
|
||||||
|
|
||||||
|
# Алерты
|
||||||
|
if [ "$api_status" != "200" ] || [ "$frontend_status" != "200" ]; then
|
||||||
|
log "🚨 ALERT: Service health check failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$disk_usage" -gt 85 ]; then
|
||||||
|
log "🚨 ALERT: Disk usage above 85%!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$memory_usage" -gt 90 ]; then
|
||||||
|
log "🚨 ALERT: Memory usage above 90%!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "✅ Monitoring completed"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x /tmp/production-deploy/monitor-production.sh
|
||||||
|
|
||||||
|
# Копирование файлов на production сервер
|
||||||
|
echo "📤 Copying deployment files to production server..."
|
||||||
|
scp -r /tmp/production-deploy/* production:/opt/catlink/
|
||||||
|
|
||||||
|
# Выполнение деплоя на production с дополнительными проверками
|
||||||
|
echo "🚀 Executing production deployment..."
|
||||||
|
|
||||||
|
ssh production << EOF
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd /opt/catlink
|
||||||
|
|
||||||
|
# Проверка готовности к деплою
|
||||||
|
echo "🔍 Pre-deployment checks..."
|
||||||
|
|
||||||
|
# Проверка свободного места
|
||||||
|
available_space=\$(df -h . | tail -1 | awk '{print \$4}' | sed 's/G//')
|
||||||
|
if [ "\${available_space%.*}" -lt 5 ]; then
|
||||||
|
echo "❌ Insufficient disk space for deployment (less than 5GB available)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание полного бэкапа перед деплоем
|
||||||
|
echo "💾 Creating pre-deployment backup..."
|
||||||
|
if [ -f docker-compose.production.yml ]; then
|
||||||
|
./manage-production.sh backup
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание директорий для данных
|
||||||
|
mkdir -p /opt/catlink/data/{postgres,redis,media,static}
|
||||||
|
mkdir -p /opt/catlink/backups
|
||||||
|
mkdir -p /opt/catlink/logs
|
||||||
|
|
||||||
|
# Установка правильных прав
|
||||||
|
chown -R 1000:1000 /opt/catlink/data/
|
||||||
|
chmod -R 755 /opt/catlink/data/
|
||||||
|
|
||||||
|
# Загрузка переменных окружения
|
||||||
|
source .env.production
|
||||||
|
|
||||||
|
# Остановка старой версии (если существует)
|
||||||
|
if [ -f docker-compose.production.yml ]; then
|
||||||
|
echo "🛑 Stopping current production deployment..."
|
||||||
|
./manage-production.sh stop
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Очистка старых образов
|
||||||
|
echo "🧹 Cleaning up old images..."
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
# Загрузка новых образов
|
||||||
|
echo "📥 Pulling new production images..."
|
||||||
|
docker-compose -f docker-compose.production.yml pull
|
||||||
|
|
||||||
|
# Запуск новой версии
|
||||||
|
echo "🚀 Starting new production deployment..."
|
||||||
|
./manage-production.sh start
|
||||||
|
|
||||||
|
# Ожидание готовности сервисов
|
||||||
|
echo "⏳ Waiting for services to be ready..."
|
||||||
|
sleep 60
|
||||||
|
|
||||||
|
# Выполнение миграций
|
||||||
|
echo "🔄 Running database migrations..."
|
||||||
|
./manage-production.sh migrate
|
||||||
|
|
||||||
|
# Сбор статических файлов
|
||||||
|
echo "📦 Collecting static files..."
|
||||||
|
./manage-production.sh collectstatic
|
||||||
|
|
||||||
|
# Проверка здоровья production
|
||||||
|
echo "🏥 Performing production health check..."
|
||||||
|
./manage-production.sh health
|
||||||
|
|
||||||
|
echo "✅ Production deployment completed successfully!"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Финальная проверка production деплоя
|
||||||
|
echo "🔍 Final production verification..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Расширенная проверка production
|
||||||
|
api_status=$(curl -s -o /dev/null -w "%{http_code}" "https://$PRODUCTION_HOST/api/health/" || echo "000")
|
||||||
|
frontend_status=$(curl -s -o /dev/null -w "%{http_code}" "https://$PRODUCTION_HOST/" || echo "000")
|
||||||
|
admin_status=$(curl -s -o /dev/null -w "%{http_code}" "https://$PRODUCTION_HOST/admin/" || echo "000")
|
||||||
|
|
||||||
|
echo "📊 Production verification results:"
|
||||||
|
echo " • API Health: $api_status"
|
||||||
|
echo " • Frontend: $frontend_status"
|
||||||
|
echo " • Admin Panel: $admin_status"
|
||||||
|
|
||||||
|
if [ "$api_status" = "200" ] && [ "$frontend_status" = "200" ] && [ "$admin_status" = "200" ]; then
|
||||||
|
echo "✅ Production deployment verified successfully!"
|
||||||
|
|
||||||
|
# Уведомления об успешном деплое
|
||||||
|
echo "📢 Sending production deployment notifications..."
|
||||||
|
|
||||||
|
# Slack уведомление
|
||||||
|
if [ -n "$SLACK_WEBHOOK_URL" ]; then
|
||||||
|
curl -X POST -H 'Content-type: application/json' \
|
||||||
|
--data "{
|
||||||
|
\"text\": \"🎉 *CatLink $VERSION* successfully deployed to production!\",
|
||||||
|
\"attachments\": [
|
||||||
|
{
|
||||||
|
\"color\": \"good\",
|
||||||
|
\"fields\": [
|
||||||
|
{
|
||||||
|
\"title\": \"Environment\",
|
||||||
|
\"value\": \"🚀 Production\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"URL\",
|
||||||
|
\"value\": \"https://$PRODUCTION_HOST\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Version\",
|
||||||
|
\"value\": \"$VERSION\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Status\",
|
||||||
|
\"value\": \"✅ Live & Healthy\",
|
||||||
|
\"short\": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
"$SLACK_WEBHOOK_URL" || echo "Failed to send Slack notification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "❌ Production deployment verification failed!"
|
||||||
|
|
||||||
|
# Получение логов для диагностики
|
||||||
|
echo "📋 Getting production logs for diagnosis..."
|
||||||
|
ssh production "cd /opt/catlink && ./manage-production.sh logs --tail=100"
|
||||||
|
|
||||||
|
# Критическое уведомление
|
||||||
|
if [ -n "$SLACK_WEBHOOK_URL" ]; then
|
||||||
|
curl -X POST -H 'Content-type: application/json' \
|
||||||
|
--data "{
|
||||||
|
\"text\": \"🚨 *CRITICAL: CatLink $VERSION production deployment failed!*\",
|
||||||
|
\"attachments\": [
|
||||||
|
{
|
||||||
|
\"color\": \"danger\",
|
||||||
|
\"fields\": [
|
||||||
|
{
|
||||||
|
\"title\": \"API Status\",
|
||||||
|
\"value\": \"$api_status\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Frontend Status\",
|
||||||
|
\"value\": \"$frontend_status\",
|
||||||
|
\"short\": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
"$SLACK_WEBHOOK_URL" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание подробного отчета о production деплое
|
||||||
|
cat > /tmp/production-deploy-report.md << EOF
|
||||||
|
# 🚀 Production Deployment Report
|
||||||
|
|
||||||
|
## 📋 Deployment Summary
|
||||||
|
- **Version**: $VERSION
|
||||||
|
- **Environment**: 🚀 Production
|
||||||
|
- **URL**: https://$PRODUCTION_HOST
|
||||||
|
- **Deployed At**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
- **Deployed By**: ${DRONE_COMMIT_AUTHOR:-"CI/CD Pipeline"}
|
||||||
|
- **Build**: #${DRONE_BUILD_NUMBER:-"manual"}
|
||||||
|
|
||||||
|
## ✅ Verification Results
|
||||||
|
- **API Health**: $api_status ✅
|
||||||
|
- **Frontend**: $frontend_status ✅
|
||||||
|
- **Admin Panel**: $admin_status ✅
|
||||||
|
- **Database**: Migrations applied successfully ✅
|
||||||
|
- **Static Files**: Collected successfully ✅
|
||||||
|
- **Health Checks**: All services healthy ✅
|
||||||
|
|
||||||
|
## 🔗 Production Links
|
||||||
|
- [🌐 Application](https://$PRODUCTION_HOST)
|
||||||
|
- [📚 API Documentation](https://$PRODUCTION_HOST/api/docs/)
|
||||||
|
- [🔧 Admin Panel](https://$PRODUCTION_HOST/admin/)
|
||||||
|
|
||||||
|
## 📊 Deployment Metrics
|
||||||
|
- **Deployment Time**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
- **Downtime**: Minimal (rolling deployment)
|
||||||
|
- **Images Size**: Production optimized
|
||||||
|
- **Health Check**: All endpoints responding
|
||||||
|
|
||||||
|
## 🔐 Security & Compliance
|
||||||
|
- ✅ HTTPS enabled with Let's Encrypt
|
||||||
|
- ✅ Security headers configured
|
||||||
|
- ✅ Non-root container execution
|
||||||
|
- ✅ Resource limits applied
|
||||||
|
- ✅ Logging configured
|
||||||
|
- ✅ Backup system active
|
||||||
|
|
||||||
|
## 🛠️ Management Commands
|
||||||
|
\`\`\`bash
|
||||||
|
# SSH to production server
|
||||||
|
ssh production
|
||||||
|
|
||||||
|
# Check production health
|
||||||
|
./manage-production.sh health
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./manage-production.sh logs
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
./manage-production.sh backup
|
||||||
|
|
||||||
|
# Monitor production
|
||||||
|
./monitor-production.sh
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 📈 Next Steps
|
||||||
|
- [ ] Monitor application metrics
|
||||||
|
- [ ] Verify all features working correctly
|
||||||
|
- [ ] Update monitoring dashboards
|
||||||
|
- [ ] Schedule next backup
|
||||||
|
- [ ] Update documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🎉 Production deployment completed successfully!**
|
||||||
|
|
||||||
|
*This is an automated deployment report generated by the CI/CD pipeline.*
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 PRODUCTION DEPLOYMENT COMPLETED SUCCESSFULLY! 🎉"
|
||||||
|
echo ""
|
||||||
|
echo "🔗 Production URLs:"
|
||||||
|
echo " 🌐 Application: https://$PRODUCTION_HOST"
|
||||||
|
echo " 📚 API Docs: https://$PRODUCTION_HOST/api/docs/"
|
||||||
|
echo " 🔧 Admin Panel: https://$PRODUCTION_HOST/admin/"
|
||||||
|
echo ""
|
||||||
|
echo "📄 Deployment report: /tmp/production-deploy-report.md"
|
||||||
|
echo "📊 Version $VERSION is now LIVE! 🚀"
|
||||||
496
scripts/ci/deploy-staging.sh
Executable file
496
scripts/ci/deploy-staging.sh
Executable file
@@ -0,0 +1,496 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/deploy-staging.sh - Деплой на staging окружение
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Deploying to staging environment..."
|
||||||
|
|
||||||
|
# Переменные
|
||||||
|
REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"}
|
||||||
|
PROJECT_NAME="catlink"
|
||||||
|
VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}
|
||||||
|
STAGING_HOST=${STAGING_HOST:-"staging.catlink.dev"}
|
||||||
|
STAGING_USER=${STAGING_USER:-"deploy"}
|
||||||
|
STAGING_PORT=${STAGING_PORT:-"22"}
|
||||||
|
|
||||||
|
echo "📋 Deployment information:"
|
||||||
|
echo " • Registry: $REGISTRY"
|
||||||
|
echo " • Project: $PROJECT_NAME"
|
||||||
|
echo " • Version: $VERSION"
|
||||||
|
echo " • Host: $STAGING_HOST"
|
||||||
|
echo " • User: $STAGING_USER"
|
||||||
|
|
||||||
|
# Проверка обязательных переменных
|
||||||
|
if [ -z "$STAGING_HOST" ] || [ -z "$STAGING_SSH_KEY" ]; then
|
||||||
|
echo "❌ Staging deployment credentials not found!"
|
||||||
|
echo "Please set STAGING_HOST and STAGING_SSH_KEY environment variables"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Настройка SSH
|
||||||
|
echo "🔐 Setting up SSH connection..."
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
|
||||||
|
# Создание SSH ключа
|
||||||
|
echo "$STAGING_SSH_KEY" | base64 -d > ~/.ssh/id_staging
|
||||||
|
chmod 600 ~/.ssh/id_staging
|
||||||
|
|
||||||
|
# Добавление хоста в known_hosts
|
||||||
|
ssh-keyscan -p "$STAGING_PORT" "$STAGING_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
|
# SSH конфигурация
|
||||||
|
cat > ~/.ssh/config << EOF
|
||||||
|
Host staging
|
||||||
|
HostName $STAGING_HOST
|
||||||
|
User $STAGING_USER
|
||||||
|
Port $STAGING_PORT
|
||||||
|
IdentityFile ~/.ssh/id_staging
|
||||||
|
StrictHostKeyChecking no
|
||||||
|
UserKnownHostsFile ~/.ssh/known_hosts
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Проверка подключения к staging серверу
|
||||||
|
echo "🔗 Testing staging server connection..."
|
||||||
|
if ! ssh staging "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
echo "❌ Failed to connect to staging server"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Successfully connected to staging server"
|
||||||
|
|
||||||
|
# Подготовка файлов для деплоя
|
||||||
|
echo "📦 Preparing deployment files..."
|
||||||
|
mkdir -p /tmp/staging-deploy
|
||||||
|
|
||||||
|
# Создание staging docker-compose
|
||||||
|
cat > /tmp/staging-deploy/docker-compose.staging.yml << EOF
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: catlink_staging
|
||||||
|
POSTGRES_USER: catlink_user
|
||||||
|
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U catlink_user -d catlink_staging"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=staging
|
||||||
|
- DEBUG=False
|
||||||
|
- SECRET_KEY=\${SECRET_KEY}
|
||||||
|
- DATABASE_URL=postgresql://catlink_user:\${POSTGRES_PASSWORD}@postgres:5432/catlink_staging
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
- ALLOWED_HOSTS=$STAGING_HOST,localhost,127.0.0.1
|
||||||
|
- CORS_ALLOWED_ORIGINS=https://$STAGING_HOST,http://localhost:3000
|
||||||
|
- EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
|
||||||
|
volumes:
|
||||||
|
- media_data:/app/media
|
||||||
|
- static_data:/app/staticfiles
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8000/api/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink-staging-api.rule=Host(\`$STAGING_HOST\`) && PathPrefix(\`/api\`)"
|
||||||
|
- "traefik.http.routers.catlink-staging-api.tls=true"
|
||||||
|
- "traefik.http.routers.catlink-staging-api.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.catlink-staging-api.loadbalancer.server.port=8000"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- NEXT_PUBLIC_API_URL=https://$STAGING_HOST/api
|
||||||
|
- NEXT_PUBLIC_APP_ENV=staging
|
||||||
|
depends_on:
|
||||||
|
web:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.catlink-staging.rule=Host(\`$STAGING_HOST\`)"
|
||||||
|
- "traefik.http.routers.catlink-staging.tls=true"
|
||||||
|
- "traefik.http.routers.catlink-staging.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.catlink-staging.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
redis_data:
|
||||||
|
media_data:
|
||||||
|
static_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
name: traefik_default
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание скрипта инициализации БД
|
||||||
|
cat > /tmp/staging-deploy/init.sql << 'EOF'
|
||||||
|
-- Staging database initialization
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
|
||||||
|
|
||||||
|
-- Create indexes for performance
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_links_created_at ON links_link(created_at);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_linkgroups_created_at ON links_linkgroup(created_at);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users_user(email);
|
||||||
|
|
||||||
|
-- Insert test data for staging
|
||||||
|
INSERT INTO users_user (id, email, username, first_name, last_name, is_active, date_joined, password)
|
||||||
|
VALUES (
|
||||||
|
uuid_generate_v4(),
|
||||||
|
'staging@catlink.dev',
|
||||||
|
'staging_user',
|
||||||
|
'Staging',
|
||||||
|
'User',
|
||||||
|
true,
|
||||||
|
NOW(),
|
||||||
|
'pbkdf2_sha256$600000$test$staging'
|
||||||
|
) ON CONFLICT DO NOTHING;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание файла окружения для staging
|
||||||
|
cat > /tmp/staging-deploy/.env.staging << EOF
|
||||||
|
# Staging Environment Variables
|
||||||
|
COMPOSE_PROJECT_NAME=catlink-staging
|
||||||
|
POSTGRES_PASSWORD=\${POSTGRES_PASSWORD}
|
||||||
|
SECRET_KEY=\${SECRET_KEY}
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
APP_VERSION=$VERSION
|
||||||
|
DEPLOY_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
COMMIT_SHA=${DRONE_COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo "unknown")}
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
ENABLE_DEBUG_TOOLBAR=false
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# Backup settings
|
||||||
|
BACKUP_SCHEDULE=0 2 * * *
|
||||||
|
BACKUP_RETENTION_DAYS=7
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание скрипта для управления staging
|
||||||
|
cat > /tmp/staging-deploy/manage-staging.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Staging management script
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
COMPOSE_FILE="docker-compose.staging.yml"
|
||||||
|
PROJECT_NAME="catlink-staging"
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
"start")
|
||||||
|
echo "🚀 Starting staging environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d
|
||||||
|
echo "✅ Staging environment started"
|
||||||
|
;;
|
||||||
|
"stop")
|
||||||
|
echo "🛑 Stopping staging environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME down
|
||||||
|
echo "✅ Staging environment stopped"
|
||||||
|
;;
|
||||||
|
"restart")
|
||||||
|
echo "🔄 Restarting staging environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME restart
|
||||||
|
echo "✅ Staging environment restarted"
|
||||||
|
;;
|
||||||
|
"logs")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME logs -f ${2:-}
|
||||||
|
;;
|
||||||
|
"status")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME ps
|
||||||
|
;;
|
||||||
|
"update")
|
||||||
|
echo "📦 Updating staging environment..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME pull
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME up -d
|
||||||
|
echo "✅ Staging environment updated"
|
||||||
|
;;
|
||||||
|
"backup")
|
||||||
|
echo "💾 Creating staging backup..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec postgres pg_dump -U catlink_user catlink_staging > "backup-staging-$(date +%Y%m%d-%H%M%S).sql"
|
||||||
|
echo "✅ Backup created"
|
||||||
|
;;
|
||||||
|
"migrate")
|
||||||
|
echo "🔄 Running database migrations..."
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec web python manage.py migrate
|
||||||
|
echo "✅ Migrations completed"
|
||||||
|
;;
|
||||||
|
"shell")
|
||||||
|
docker-compose -f $COMPOSE_FILE -p $PROJECT_NAME exec web python manage.py shell
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart|logs|status|update|backup|migrate|shell}"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " start - Start staging environment"
|
||||||
|
echo " stop - Stop staging environment"
|
||||||
|
echo " restart - Restart staging environment"
|
||||||
|
echo " logs - Show logs (optionally specify service)"
|
||||||
|
echo " status - Show containers status"
|
||||||
|
echo " update - Update images and restart"
|
||||||
|
echo " backup - Create database backup"
|
||||||
|
echo " migrate - Run database migrations"
|
||||||
|
echo " shell - Open Django shell"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x /tmp/staging-deploy/manage-staging.sh
|
||||||
|
|
||||||
|
# Создание скрипта мониторинга
|
||||||
|
cat > /tmp/staging-deploy/monitor-staging.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Staging monitoring script
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROJECT_NAME="catlink-staging"
|
||||||
|
HEALTH_CHECK_URL="https://staging.catlink.dev/api/"
|
||||||
|
FRONTEND_URL="https://staging.catlink.dev/"
|
||||||
|
|
||||||
|
echo "🔍 Monitoring staging environment..."
|
||||||
|
|
||||||
|
# Проверка контейнеров
|
||||||
|
echo "📦 Container status:"
|
||||||
|
docker-compose -p $PROJECT_NAME ps
|
||||||
|
|
||||||
|
# Проверка здоровья сервисов
|
||||||
|
echo ""
|
||||||
|
echo "🏥 Health checks:"
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
backend_status=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" || echo "000")
|
||||||
|
if [ "$backend_status" = "200" ]; then
|
||||||
|
echo " ✅ Backend: OK ($backend_status)"
|
||||||
|
else
|
||||||
|
echo " ❌ Backend: FAILED ($backend_status)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
frontend_status=$(curl -s -o /dev/null -w "%{http_code}" "$FRONTEND_URL" || echo "000")
|
||||||
|
if [ "$frontend_status" = "200" ]; then
|
||||||
|
echo " ✅ Frontend: OK ($frontend_status)"
|
||||||
|
else
|
||||||
|
echo " ❌ Frontend: FAILED ($frontend_status)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка ресурсов
|
||||||
|
echo ""
|
||||||
|
echo "📊 Resource usage:"
|
||||||
|
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" | grep $PROJECT_NAME || echo "No containers running"
|
||||||
|
|
||||||
|
# Проверка логов на ошибки
|
||||||
|
echo ""
|
||||||
|
echo "🔍 Recent errors:"
|
||||||
|
docker-compose -p $PROJECT_NAME logs --tail=50 2>&1 | grep -i error | tail -5 || echo "No recent errors found"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x /tmp/staging-deploy/monitor-staging.sh
|
||||||
|
|
||||||
|
# Копирование файлов на staging сервер
|
||||||
|
echo "📤 Copying deployment files to staging server..."
|
||||||
|
scp -r /tmp/staging-deploy/* staging:/opt/catlink-staging/
|
||||||
|
|
||||||
|
# Деплой на staging сервер
|
||||||
|
echo "🚀 Deploying to staging server..."
|
||||||
|
|
||||||
|
ssh staging << 'EOF'
|
||||||
|
cd /opt/catlink-staging
|
||||||
|
|
||||||
|
# Создание резервной копии текущей версии
|
||||||
|
if [ -f docker-compose.staging.yml ]; then
|
||||||
|
echo "💾 Creating backup of current deployment..."
|
||||||
|
cp docker-compose.staging.yml docker-compose.staging.yml.backup
|
||||||
|
./manage-staging.sh backup || echo "Backup failed, continuing..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Загрузка переменных окружения
|
||||||
|
source .env.staging 2>/dev/null || echo "No existing .env.staging found"
|
||||||
|
|
||||||
|
# Остановка текущих контейнеров
|
||||||
|
echo "🛑 Stopping current containers..."
|
||||||
|
./manage-staging.sh stop || echo "No containers to stop"
|
||||||
|
|
||||||
|
# Удаление старых образов
|
||||||
|
echo "🧹 Cleaning up old images..."
|
||||||
|
docker image prune -f || true
|
||||||
|
|
||||||
|
# Запуск новой версии
|
||||||
|
echo "🚀 Starting new version..."
|
||||||
|
./manage-staging.sh start
|
||||||
|
|
||||||
|
# Ожидание запуска сервисов
|
||||||
|
echo "⏳ Waiting for services to start..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Выполнение миграций
|
||||||
|
echo "🔄 Running database migrations..."
|
||||||
|
./manage-staging.sh migrate
|
||||||
|
|
||||||
|
# Проверка здоровья
|
||||||
|
echo "🏥 Checking deployment health..."
|
||||||
|
./monitor-staging.sh
|
||||||
|
|
||||||
|
echo "✅ Staging deployment completed!"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Проверка деплоя
|
||||||
|
echo "🔍 Verifying staging deployment..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Проверка доступности API
|
||||||
|
api_status=$(curl -s -o /dev/null -w "%{http_code}" "https://$STAGING_HOST/api/" || echo "000")
|
||||||
|
frontend_status=$(curl -s -o /dev/null -w "%{http_code}" "https://$STAGING_HOST/" || echo "000")
|
||||||
|
|
||||||
|
echo "📊 Deployment verification:"
|
||||||
|
echo " • API Status: $api_status"
|
||||||
|
echo " • Frontend Status: $frontend_status"
|
||||||
|
|
||||||
|
if [ "$api_status" = "200" ] && [ "$frontend_status" = "200" ]; then
|
||||||
|
echo "✅ Staging deployment verified successfully!"
|
||||||
|
else
|
||||||
|
echo "❌ Staging deployment verification failed"
|
||||||
|
|
||||||
|
# Получение логов для диагностики
|
||||||
|
echo "📋 Getting deployment logs for diagnosis..."
|
||||||
|
ssh staging "cd /opt/catlink-staging && ./manage-staging.sh logs --tail=50"
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Уведомления о деплое
|
||||||
|
echo "📢 Sending deployment notifications..."
|
||||||
|
|
||||||
|
# Slack уведомление
|
||||||
|
if [ -n "$SLACK_WEBHOOK_URL" ]; then
|
||||||
|
curl -X POST -H 'Content-type: application/json' \
|
||||||
|
--data "{
|
||||||
|
\"text\": \"🚀 *CatLink $VERSION* deployed to staging!\",
|
||||||
|
\"attachments\": [
|
||||||
|
{
|
||||||
|
\"color\": \"good\",
|
||||||
|
\"fields\": [
|
||||||
|
{
|
||||||
|
\"title\": \"Environment\",
|
||||||
|
\"value\": \"Staging\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"URL\",
|
||||||
|
\"value\": \"https://$STAGING_HOST\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Version\",
|
||||||
|
\"value\": \"$VERSION\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Status\",
|
||||||
|
\"value\": \"✅ Healthy\",
|
||||||
|
\"short\": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
"$SLACK_WEBHOOK_URL" || echo "Failed to send Slack notification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание отчета о деплое
|
||||||
|
cat > /tmp/staging-deploy-report.md << EOF
|
||||||
|
# Staging Deployment Report
|
||||||
|
|
||||||
|
## 📋 Deployment Information
|
||||||
|
- **Version**: $VERSION
|
||||||
|
- **Environment**: Staging
|
||||||
|
- **URL**: https://$STAGING_HOST
|
||||||
|
- **Deployed At**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
- **Deployed By**: ${DRONE_COMMIT_AUTHOR:-"CI/CD Pipeline"}
|
||||||
|
|
||||||
|
## ✅ Verification Results
|
||||||
|
- **API Status**: $api_status
|
||||||
|
- **Frontend Status**: $frontend_status
|
||||||
|
- **Database**: Migrations applied successfully
|
||||||
|
- **Health Checks**: All services healthy
|
||||||
|
|
||||||
|
## 🔗 Quick Links
|
||||||
|
- [Staging Application](https://$STAGING_HOST)
|
||||||
|
- [API Documentation](https://$STAGING_HOST/api/docs/)
|
||||||
|
- [Admin Panel](https://$STAGING_HOST/admin/)
|
||||||
|
|
||||||
|
## 🛠️ Management Commands
|
||||||
|
\`\`\`bash
|
||||||
|
# SSH to staging server
|
||||||
|
ssh staging
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./manage-staging.sh logs
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
./manage-staging.sh status
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
./manage-staging.sh backup
|
||||||
|
|
||||||
|
# Update deployment
|
||||||
|
./manage-staging.sh update
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 📊 Next Steps
|
||||||
|
- [ ] Perform manual testing
|
||||||
|
- [ ] Validate new features
|
||||||
|
- [ ] Check performance metrics
|
||||||
|
- [ ] Prepare for production deployment
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Staging deployment completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "🔗 Staging URLs:"
|
||||||
|
echo " 🌐 Application: https://$STAGING_HOST"
|
||||||
|
echo " 📚 API Docs: https://$STAGING_HOST/api/docs/"
|
||||||
|
echo " 🔧 Admin: https://$STAGING_HOST/admin/"
|
||||||
|
echo ""
|
||||||
|
echo "📄 Deployment report: /tmp/staging-deploy-report.md"
|
||||||
83
scripts/ci/lint.sh
Executable file
83
scripts/ci/lint.sh
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/lint.sh - Проверка качества кода
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔍 Running code quality checks..."
|
||||||
|
|
||||||
|
# Проверка Python кода (Backend)
|
||||||
|
echo "📦 Checking Python code quality..."
|
||||||
|
if [ -d "backend" ]; then
|
||||||
|
echo " • Running flake8 for Python linting..."
|
||||||
|
docker run --rm -v "$(pwd)/backend:/app" -w /app python:3.11-slim bash -c "
|
||||||
|
pip install flake8 black isort > /dev/null 2>&1
|
||||||
|
echo ' - flake8 check:'
|
||||||
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics || echo ' ⚠️ flake8 issues found'
|
||||||
|
echo ' - black format check:'
|
||||||
|
black --check . || echo ' ⚠️ black formatting issues found'
|
||||||
|
echo ' - isort import check:'
|
||||||
|
isort --check-only . || echo ' ⚠️ import sorting issues found'
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка TypeScript/JavaScript кода (Frontend)
|
||||||
|
echo "🌐 Checking TypeScript/JavaScript code quality..."
|
||||||
|
if [ -d "frontend/linktree-frontend" ]; then
|
||||||
|
echo " • Running ESLint for TypeScript/JavaScript..."
|
||||||
|
docker run --rm -v "$(pwd)/frontend/linktree-frontend:/app" -w /app node:20-alpine sh -c "
|
||||||
|
npm install --silent > /dev/null 2>&1
|
||||||
|
echo ' - ESLint check:'
|
||||||
|
npm run lint || echo ' ⚠️ ESLint issues found'
|
||||||
|
echo ' - TypeScript check:'
|
||||||
|
npm run type-check || echo ' ⚠️ TypeScript issues found'
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка Docker файлов
|
||||||
|
echo "🐳 Checking Docker files..."
|
||||||
|
if command -v hadolint > /dev/null 2>&1; then
|
||||||
|
echo " • Running hadolint for Dockerfile..."
|
||||||
|
find . -name "Dockerfile*" -exec hadolint {} \; || echo " ⚠️ Dockerfile issues found"
|
||||||
|
else
|
||||||
|
echo " • Hadolint not available, skipping Dockerfile check"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка YAML файлов
|
||||||
|
echo "📄 Checking YAML files..."
|
||||||
|
if command -v yamllint > /dev/null 2>&1; then
|
||||||
|
echo " • Running yamllint..."
|
||||||
|
find . -name "*.yml" -o -name "*.yaml" | xargs yamllint || echo " ⚠️ YAML issues found"
|
||||||
|
else
|
||||||
|
echo " • yamllint not available, skipping YAML check"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка Markdown файлов
|
||||||
|
echo "📝 Checking Markdown files..."
|
||||||
|
if [ -f "README.md" ]; then
|
||||||
|
echo " • Checking README.md structure..."
|
||||||
|
if grep -q "# " README.md; then
|
||||||
|
echo " ✅ README.md has proper headers"
|
||||||
|
else
|
||||||
|
echo " ⚠️ README.md missing proper headers"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка безопасности зависимостей
|
||||||
|
echo "🔐 Checking dependencies security..."
|
||||||
|
if [ -f "frontend/linktree-frontend/package.json" ]; then
|
||||||
|
echo " • Running npm audit..."
|
||||||
|
docker run --rm -v "$(pwd)/frontend/linktree-frontend:/app" -w /app node:20-alpine sh -c "
|
||||||
|
npm install --silent > /dev/null 2>&1
|
||||||
|
npm audit --audit-level moderate || echo ' ⚠️ npm security issues found'
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "backend/requirements.txt" ]; then
|
||||||
|
echo " • Running safety check for Python..."
|
||||||
|
docker run --rm -v "$(pwd)/backend:/app" -w /app python:3.11-slim bash -c "
|
||||||
|
pip install safety > /dev/null 2>&1
|
||||||
|
safety check -r requirements.txt || echo ' ⚠️ Python security issues found'
|
||||||
|
" || echo " ⚠️ Safety check failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Code quality checks completed!"
|
||||||
286
scripts/ci/publish.sh
Executable file
286
scripts/ci/publish.sh
Executable file
@@ -0,0 +1,286 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/publish.sh - Публикация Docker образов в registry
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "📤 Publishing Docker images to registry..."
|
||||||
|
|
||||||
|
# Переменные
|
||||||
|
REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"}
|
||||||
|
PROJECT_NAME="catlink"
|
||||||
|
VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}
|
||||||
|
DOCKER_USERNAME=${DOCKER_USERNAME}
|
||||||
|
DOCKER_PASSWORD=${DOCKER_PASSWORD}
|
||||||
|
|
||||||
|
echo "📋 Publish information:"
|
||||||
|
echo " • Registry: $REGISTRY"
|
||||||
|
echo " • Project: $PROJECT_NAME"
|
||||||
|
echo " • Version: $VERSION"
|
||||||
|
echo " • Username: ${DOCKER_USERNAME:0:3}***"
|
||||||
|
|
||||||
|
# Проверка учетных данных
|
||||||
|
if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ]; then
|
||||||
|
echo "❌ Docker registry credentials not found!"
|
||||||
|
echo "Please set DOCKER_USERNAME and DOCKER_PASSWORD environment variables"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Вход в Docker registry
|
||||||
|
echo "🔐 Authenticating with Docker registry..."
|
||||||
|
echo "$DOCKER_PASSWORD" | docker login "$REGISTRY" -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "❌ Failed to authenticate with Docker registry"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Successfully authenticated with registry"
|
||||||
|
|
||||||
|
# Список образов для публикации
|
||||||
|
IMAGES=(
|
||||||
|
"$REGISTRY/$PROJECT_NAME-backend:$VERSION"
|
||||||
|
"$REGISTRY/$PROJECT_NAME-backend:latest"
|
||||||
|
"$REGISTRY/$PROJECT_NAME-frontend:$VERSION"
|
||||||
|
"$REGISTRY/$PROJECT_NAME-frontend:latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Проверка существования образов локально
|
||||||
|
echo "🔍 Checking local images..."
|
||||||
|
for image in "${IMAGES[@]}"; do
|
||||||
|
if docker images --format "table {{.Repository}}:{{.Tag}}" | grep -q "${image#*/}"; then
|
||||||
|
echo " ✅ Found: $image"
|
||||||
|
else
|
||||||
|
echo " ❌ Missing: $image"
|
||||||
|
echo "Error: Local image $image not found. Please run build first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Публикация образов
|
||||||
|
echo "🚀 Publishing images..."
|
||||||
|
for image in "${IMAGES[@]}"; do
|
||||||
|
echo " • Publishing $image..."
|
||||||
|
|
||||||
|
# Проверка размера образа
|
||||||
|
size=$(docker images --format "table {{.Size}}" "$image" | tail -n +2)
|
||||||
|
echo " Size: $size"
|
||||||
|
|
||||||
|
# Публикация с повторными попытками
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
echo " Attempt $attempt/3..."
|
||||||
|
|
||||||
|
if docker push "$image"; then
|
||||||
|
echo " ✅ Successfully pushed $image"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo " ❌ Failed to push $image (attempt $attempt/3)"
|
||||||
|
if [ $attempt -eq 3 ]; then
|
||||||
|
echo "Error: Failed to push $image after 3 attempts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Проверка опубликованных образов
|
||||||
|
echo "🔍 Verifying published images..."
|
||||||
|
for image in "${IMAGES[@]}"; do
|
||||||
|
echo " • Verifying $image..."
|
||||||
|
|
||||||
|
# Попытка скачать manifest
|
||||||
|
if docker manifest inspect "$image" > /dev/null 2>&1; then
|
||||||
|
echo " ✅ Manifest verified for $image"
|
||||||
|
else
|
||||||
|
echo " ❌ Failed to verify manifest for $image"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Создание release notes
|
||||||
|
echo "📝 Creating release notes..."
|
||||||
|
cat > /tmp/release-notes.md << EOF
|
||||||
|
# Release $VERSION
|
||||||
|
|
||||||
|
## 🚀 What's New
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Updated to version $VERSION
|
||||||
|
- Production-ready Docker images
|
||||||
|
- Enhanced security configurations
|
||||||
|
- Performance optimizations
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- **Backend Image**: \`$REGISTRY/$PROJECT_NAME-backend:$VERSION\`
|
||||||
|
- **Frontend Image**: \`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\`
|
||||||
|
- **Build Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
- **Git Commit**: ${DRONE_COMMIT_SHA:-$(git rev-parse HEAD)}
|
||||||
|
|
||||||
|
### Docker Images
|
||||||
|
\`\`\`bash
|
||||||
|
# Pull latest images
|
||||||
|
docker pull $REGISTRY/$PROJECT_NAME-backend:$VERSION
|
||||||
|
docker pull $REGISTRY/$PROJECT_NAME-frontend:$VERSION
|
||||||
|
|
||||||
|
# Or use latest tags
|
||||||
|
docker pull $REGISTRY/$PROJECT_NAME-backend:latest
|
||||||
|
docker pull $REGISTRY/$PROJECT_NAME-frontend:latest
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
\`\`\`bash
|
||||||
|
# Download and run with docker-compose
|
||||||
|
curl -sSL https://raw.githubusercontent.com/smartsoltech/links/main/docker-compose.production.yml -o docker-compose.yml
|
||||||
|
docker-compose up -d
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Migration Notes
|
||||||
|
- No breaking changes in this release
|
||||||
|
- Database migrations included
|
||||||
|
- Backward compatible
|
||||||
|
|
||||||
|
## 📊 Image Information
|
||||||
|
|
||||||
|
| Component | Image | Size | Layers |
|
||||||
|
|-----------|-------|------|--------|
|
||||||
|
| Backend | \`$PROJECT_NAME-backend:$VERSION\` | $(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null || echo "N/A") | $(docker history "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null | wc -l || echo "N/A") |
|
||||||
|
| Frontend | \`$PROJECT_NAME-frontend:$VERSION\` | $(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null || echo "N/A") | $(docker history "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null | wc -l || echo "N/A") |
|
||||||
|
|
||||||
|
## 🔐 Security
|
||||||
|
|
||||||
|
All images are scanned for vulnerabilities and follow security best practices:
|
||||||
|
- Non-root user execution
|
||||||
|
- Minimal base images
|
||||||
|
- Regular security updates
|
||||||
|
- Dependency vulnerability scanning
|
||||||
|
|
||||||
|
## 📖 Documentation
|
||||||
|
|
||||||
|
- [Installation Guide](./docs/INSTALLATION.md)
|
||||||
|
- [Configuration Guide](./docs/CONFIGURATION.md)
|
||||||
|
- [API Documentation](./docs/API.md)
|
||||||
|
- [Makefile Commands](./docs/MAKEFILE.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/smartsoltech/links/compare/previous...${DRONE_TAG:-$VERSION}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Уведомления о публикации
|
||||||
|
echo "📢 Sending publication notifications..."
|
||||||
|
|
||||||
|
# Slack уведомление (если настроено)
|
||||||
|
if [ -n "$SLACK_WEBHOOK_URL" ]; then
|
||||||
|
curl -X POST -H 'Content-type: application/json' \
|
||||||
|
--data "{
|
||||||
|
\"text\": \"🚀 *CatLink $VERSION* published successfully!\",
|
||||||
|
\"attachments\": [
|
||||||
|
{
|
||||||
|
\"color\": \"good\",
|
||||||
|
\"fields\": [
|
||||||
|
{
|
||||||
|
\"title\": \"Backend Image\",
|
||||||
|
\"value\": \"\`$REGISTRY/$PROJECT_NAME-backend:$VERSION\`\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Frontend Image\",
|
||||||
|
\"value\": \"\`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\`\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Registry\",
|
||||||
|
\"value\": \"\`$REGISTRY\`\",
|
||||||
|
\"short\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"title\": \"Build\",
|
||||||
|
\"value\": \"#${DRONE_BUILD_NUMBER:-$(date +%s)}\",
|
||||||
|
\"short\": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
"$SLACK_WEBHOOK_URL" || echo "Failed to send Slack notification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Discord уведомление (если настроено)
|
||||||
|
if [ -n "$DISCORD_WEBHOOK_URL" ]; then
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"embeds\": [
|
||||||
|
{
|
||||||
|
\"title\": \"🚀 CatLink $VERSION Published\",
|
||||||
|
\"color\": 65280,
|
||||||
|
\"fields\": [
|
||||||
|
{
|
||||||
|
\"name\": \"Backend Image\",
|
||||||
|
\"value\": \"\`$REGISTRY/$PROJECT_NAME-backend:$VERSION\`\",
|
||||||
|
\"inline\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"name\": \"Frontend Image\",
|
||||||
|
\"value\": \"\`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\`\",
|
||||||
|
\"inline\": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"name\": \"Registry\",
|
||||||
|
\"value\": \"\`$REGISTRY\`\",
|
||||||
|
\"inline\": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
"$DISCORD_WEBHOOK_URL" || echo "Failed to send Discord notification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Создание статистики публикации
|
||||||
|
echo "📊 Creating publication statistics..."
|
||||||
|
cat > /tmp/publish-stats.json << EOF
|
||||||
|
{
|
||||||
|
"version": "$VERSION",
|
||||||
|
"registry": "$REGISTRY",
|
||||||
|
"project": "$PROJECT_NAME",
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"name": "$PROJECT_NAME-backend",
|
||||||
|
"tag": "$VERSION",
|
||||||
|
"size": "$(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null || echo "unknown")",
|
||||||
|
"published_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$PROJECT_NAME-frontend",
|
||||||
|
"tag": "$VERSION",
|
||||||
|
"size": "$(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null || echo "unknown")",
|
||||||
|
"published_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"build_info": {
|
||||||
|
"build_number": "${DRONE_BUILD_NUMBER:-unknown}",
|
||||||
|
"commit_sha": "${DRONE_COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo "unknown")}",
|
||||||
|
"build_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||||
|
"branch": "${DRONE_BRANCH:-$(git branch --show-current 2>/dev/null || echo "unknown")}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Очистка временных файлов
|
||||||
|
echo "🧹 Cleaning up..."
|
||||||
|
docker system prune -f > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Выход из registry
|
||||||
|
docker logout "$REGISTRY" > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "✅ Publication completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Published Images:"
|
||||||
|
echo " 🔗 Backend: $REGISTRY/$PROJECT_NAME-backend:$VERSION"
|
||||||
|
echo " 🔗 Frontend: $REGISTRY/$PROJECT_NAME-frontend:$VERSION"
|
||||||
|
echo ""
|
||||||
|
echo "📄 Release notes: /tmp/release-notes.md"
|
||||||
|
echo "📊 Statistics: /tmp/publish-stats.json"
|
||||||
|
echo ""
|
||||||
|
echo "🚀 Ready for deployment!"
|
||||||
208
scripts/ci/security-scan.sh
Executable file
208
scripts/ci/security-scan.sh
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/security-scan.sh - Сканирование безопасности
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔒 Running security scans..."
|
||||||
|
|
||||||
|
# Создание директории для отчетов
|
||||||
|
mkdir -p /tmp/security-reports
|
||||||
|
|
||||||
|
# 1. Сканирование зависимостей
|
||||||
|
echo "📦 Scanning dependencies for vulnerabilities..."
|
||||||
|
|
||||||
|
# Python зависимости
|
||||||
|
if [ -f "backend/requirements.txt" ]; then
|
||||||
|
echo " • Scanning Python dependencies..."
|
||||||
|
docker run --rm -v "$(pwd)/backend:/app" -w /app python:3.11-slim bash -c "
|
||||||
|
pip install safety bandit > /dev/null 2>&1
|
||||||
|
echo 'Python Safety Report:' > /tmp/safety-report.txt
|
||||||
|
safety check -r requirements.txt --output text >> /tmp/safety-report.txt 2>&1 || echo 'Safety scan completed with findings'
|
||||||
|
cat /tmp/safety-report.txt
|
||||||
|
" | tee /tmp/security-reports/python-dependencies.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Node.js зависимости
|
||||||
|
if [ -f "frontend/linktree-frontend/package.json" ]; then
|
||||||
|
echo " • Scanning Node.js dependencies..."
|
||||||
|
docker run --rm -v "$(pwd)/frontend/linktree-frontend:/app" -w /app node:20-alpine sh -c "
|
||||||
|
npm install --silent > /dev/null 2>&1
|
||||||
|
npm audit --audit-level moderate 2>&1 || echo 'npm audit completed with findings'
|
||||||
|
" | tee /tmp/security-reports/nodejs-dependencies.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Сканирование кода на уязвимости
|
||||||
|
echo "🔍 Scanning source code for security issues..."
|
||||||
|
|
||||||
|
# Python код
|
||||||
|
if [ -d "backend" ]; then
|
||||||
|
echo " • Scanning Python code with Bandit..."
|
||||||
|
docker run --rm -v "$(pwd)/backend:/app" -w /app python:3.11-slim bash -c "
|
||||||
|
pip install bandit > /dev/null 2>&1
|
||||||
|
bandit -r . -f txt 2>&1 || echo 'Bandit scan completed'
|
||||||
|
" | tee /tmp/security-reports/python-code-scan.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Сканирование Docker образов
|
||||||
|
echo "🐳 Scanning Docker images..."
|
||||||
|
|
||||||
|
# Получение списка образов проекта
|
||||||
|
images=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "(catlink|links)" | head -5)
|
||||||
|
|
||||||
|
for image in $images; do
|
||||||
|
echo " • Scanning image: $image"
|
||||||
|
|
||||||
|
# Используем простую проверку уязвимостей через docker history
|
||||||
|
echo " - Checking image layers..."
|
||||||
|
docker history "$image" --no-trunc | head -10
|
||||||
|
|
||||||
|
# Проверка на известные уязвимые базовые образы
|
||||||
|
echo " - Checking base image..."
|
||||||
|
base_image=$(docker inspect "$image" | grep -o '"FROM [^"]*"' | head -1 || echo "unknown")
|
||||||
|
echo " Base image: $base_image"
|
||||||
|
|
||||||
|
done > /tmp/security-reports/docker-scan.txt
|
||||||
|
|
||||||
|
# 4. Сканирование конфигурации
|
||||||
|
echo "⚙️ Scanning configuration files..."
|
||||||
|
|
||||||
|
# Проверка .env файлов на потенциальные проблемы
|
||||||
|
echo " • Checking environment configuration..."
|
||||||
|
if [ -f ".env" ]; then
|
||||||
|
echo " - Checking for hardcoded secrets in .env..."
|
||||||
|
|
||||||
|
# Проверка на слабые пароли или ключи
|
||||||
|
if grep -qi "password.*123\|secret.*test\|key.*test" .env; then
|
||||||
|
echo " ⚠️ Weak passwords or test keys found in .env"
|
||||||
|
else
|
||||||
|
echo " ✅ No obvious weak credentials in .env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка на отладочный режим в продакшене
|
||||||
|
if grep -q "DEBUG.*True" .env; then
|
||||||
|
echo " ⚠️ DEBUG mode is enabled"
|
||||||
|
else
|
||||||
|
echo " ✅ DEBUG mode is properly configured"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка Docker Compose на небезопасные настройки
|
||||||
|
echo " • Checking Docker Compose security..."
|
||||||
|
if [ -f "docker-compose.yml" ]; then
|
||||||
|
# Проверка на privileged режим
|
||||||
|
if grep -q "privileged.*true" docker-compose.yml; then
|
||||||
|
echo " ⚠️ Privileged containers found"
|
||||||
|
else
|
||||||
|
echo " ✅ No privileged containers"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка на монтирование Docker socket
|
||||||
|
if grep -q "/var/run/docker.sock" docker-compose.yml; then
|
||||||
|
echo " ⚠️ Docker socket is mounted (potential security risk)"
|
||||||
|
else
|
||||||
|
echo " ✅ Docker socket is not exposed"
|
||||||
|
fi
|
||||||
|
fi > /tmp/security-reports/config-scan.txt
|
||||||
|
|
||||||
|
# 5. Проверка сетевой безопасности
|
||||||
|
echo "🌐 Checking network security..."
|
||||||
|
|
||||||
|
# Проверка открытых портов
|
||||||
|
echo " • Checking exposed ports..."
|
||||||
|
open_ports=$(docker-compose ps --services | xargs -I {} docker-compose port {} 2>/dev/null | grep -v "No container" || true)
|
||||||
|
if [ -n "$open_ports" ]; then
|
||||||
|
echo " Exposed ports:"
|
||||||
|
echo "$open_ports"
|
||||||
|
else
|
||||||
|
echo " No exposed ports found"
|
||||||
|
fi > /tmp/security-reports/network-scan.txt
|
||||||
|
|
||||||
|
# 6. Проверка SSL/TLS конфигурации
|
||||||
|
echo "🔐 Checking SSL/TLS configuration..."
|
||||||
|
|
||||||
|
# Проверка наличия SSL настроек
|
||||||
|
if [ -f "nginx.conf" ] || [ -f "docker-compose.ssl.yml" ]; then
|
||||||
|
echo " • SSL configuration found"
|
||||||
|
|
||||||
|
# Проверка на использование слабых протоколов
|
||||||
|
if grep -r "ssl_protocols.*TLSv1[^.2]" . 2>/dev/null; then
|
||||||
|
echo " ⚠️ Weak TLS protocols detected"
|
||||||
|
else
|
||||||
|
echo " ✅ TLS configuration appears secure"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " • No SSL configuration found (consider adding for production)"
|
||||||
|
fi >> /tmp/security-reports/ssl-scan.txt
|
||||||
|
|
||||||
|
# 7. Создание сводного отчета
|
||||||
|
echo "📊 Generating security summary..."
|
||||||
|
|
||||||
|
cat > /tmp/security-reports/security-summary.txt << EOF
|
||||||
|
CatLink Security Scan Summary
|
||||||
|
============================
|
||||||
|
Scan Date: $(date)
|
||||||
|
Commit: ${DRONE_COMMIT_SHA:-"local"}
|
||||||
|
Branch: ${DRONE_BRANCH:-"local"}
|
||||||
|
|
||||||
|
Scans Performed:
|
||||||
|
✓ Dependency vulnerability scan
|
||||||
|
✓ Source code security scan
|
||||||
|
✓ Docker image security scan
|
||||||
|
✓ Configuration security check
|
||||||
|
✓ Network security assessment
|
||||||
|
✓ SSL/TLS configuration review
|
||||||
|
|
||||||
|
Reports Generated:
|
||||||
|
- python-dependencies.txt
|
||||||
|
- nodejs-dependencies.txt
|
||||||
|
- python-code-scan.txt
|
||||||
|
- docker-scan.txt
|
||||||
|
- config-scan.txt
|
||||||
|
- network-scan.txt
|
||||||
|
- ssl-scan.txt
|
||||||
|
|
||||||
|
Recommendations:
|
||||||
|
1. Review dependency vulnerabilities and update packages
|
||||||
|
2. Address any code security issues found by static analysis
|
||||||
|
3. Keep Docker base images updated
|
||||||
|
4. Use strong passwords and secrets management
|
||||||
|
5. Enable SSL/TLS for production deployments
|
||||||
|
6. Regular security scans in CI/CD pipeline
|
||||||
|
|
||||||
|
For detailed findings, check individual report files.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Подсчет найденных проблем
|
||||||
|
echo "📈 Security scan statistics..."
|
||||||
|
total_issues=0
|
||||||
|
|
||||||
|
# Подсчет проблем в зависимостях
|
||||||
|
if [ -f "/tmp/security-reports/python-dependencies.txt" ]; then
|
||||||
|
python_issues=$(grep -c "vulnerability\|CRITICAL\|HIGH" /tmp/security-reports/python-dependencies.txt 2>/dev/null || echo "0")
|
||||||
|
echo " • Python dependency issues: $python_issues"
|
||||||
|
total_issues=$((total_issues + python_issues))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/tmp/security-reports/nodejs-dependencies.txt" ]; then
|
||||||
|
node_issues=$(grep -c "vulnerability\|critical\|high" /tmp/security-reports/nodejs-dependencies.txt 2>/dev/null || echo "0")
|
||||||
|
echo " • Node.js dependency issues: $node_issues"
|
||||||
|
total_issues=$((total_issues + node_issues))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " • Total security issues found: $total_issues"
|
||||||
|
|
||||||
|
# Вывод результатов
|
||||||
|
echo ""
|
||||||
|
echo "🔒 Security scan completed!"
|
||||||
|
echo "📁 Reports saved to /tmp/security-reports/"
|
||||||
|
echo ""
|
||||||
|
cat /tmp/security-reports/security-summary.txt
|
||||||
|
|
||||||
|
# Не фейлим build на проблемах безопасности, но выводим предупреждение
|
||||||
|
if [ "$total_issues" -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Security issues detected! Please review the reports."
|
||||||
|
echo " This is informational and does not fail the build."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Security scan stage completed."
|
||||||
202
scripts/ci/test.sh
Executable file
202
scripts/ci/test.sh
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/ci/test.sh - Запуск тестов
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🧪 Running CatLink tests..."
|
||||||
|
|
||||||
|
# Проверка наличия контейнеров
|
||||||
|
if ! docker-compose ps | grep -q "Up"; then
|
||||||
|
echo "📦 Starting containers for testing..."
|
||||||
|
docker-compose up -d
|
||||||
|
sleep 30
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Подготовка базы данных для тестов
|
||||||
|
echo "🗄️ Preparing test database..."
|
||||||
|
docker-compose exec -T web python manage.py migrate --noinput
|
||||||
|
docker-compose exec -T web python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Создание тестового пользователя
|
||||||
|
echo "👤 Creating test user..."
|
||||||
|
docker-compose exec -T web python manage.py shell << 'EOF'
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
User = get_user_model()
|
||||||
|
if not User.objects.filter(username='testuser').exists():
|
||||||
|
User.objects.create_user(username='testuser', email='test@example.com', password='testpass123')
|
||||||
|
print("Test user created")
|
||||||
|
else:
|
||||||
|
print("Test user already exists")
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Backend тесты
|
||||||
|
echo "🔧 Running backend tests..."
|
||||||
|
echo " • Django unit tests..."
|
||||||
|
docker-compose exec -T web python manage.py test --verbosity=2 --keepdb || {
|
||||||
|
echo "❌ Backend tests failed"
|
||||||
|
docker-compose logs web | tail -50
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# API тесты
|
||||||
|
echo "🌐 Running API tests..."
|
||||||
|
echo " • Testing API endpoints..."
|
||||||
|
|
||||||
|
# Проверка основных API эндпоинтов
|
||||||
|
api_base="http://localhost:8000/api"
|
||||||
|
|
||||||
|
# Тест API root
|
||||||
|
api_root=$(curl -s -o /dev/null -w "%{http_code}" "$api_base/")
|
||||||
|
echo " - API root: $api_root"
|
||||||
|
|
||||||
|
# Тест регистрации
|
||||||
|
echo " - Testing user registration..."
|
||||||
|
register_response=$(curl -s -X POST "$api_base/auth/register/" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "testuser2",
|
||||||
|
"email": "testuser2@example.com",
|
||||||
|
"password": "testpass123",
|
||||||
|
"password2": "testpass123"
|
||||||
|
}' \
|
||||||
|
-w "%{http_code}")
|
||||||
|
|
||||||
|
if echo "$register_response" | grep -q "200\|201"; then
|
||||||
|
echo " ✅ Registration test passed"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Registration test failed: $register_response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Тест логина
|
||||||
|
echo " - Testing user login..."
|
||||||
|
login_response=$(curl -s -X POST "$api_base/auth/login/" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "testuser",
|
||||||
|
"password": "testpass123"
|
||||||
|
}')
|
||||||
|
|
||||||
|
if echo "$login_response" | grep -q "access"; then
|
||||||
|
echo " ✅ Login test passed"
|
||||||
|
# Извлечение токена для дальнейших тестов
|
||||||
|
token=$(echo "$login_response" | grep -o '"access":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
else
|
||||||
|
echo " ⚠️ Login test failed: $login_response"
|
||||||
|
token=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Тесты авторизованных эндпоинтов
|
||||||
|
if [ -n "$token" ]; then
|
||||||
|
echo " - Testing authorized endpoints..."
|
||||||
|
|
||||||
|
# Тест создания группы
|
||||||
|
group_response=$(curl -s -X POST "$api_base/groups/" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "Test Group",
|
||||||
|
"description": "Test group for CI"
|
||||||
|
}' \
|
||||||
|
-w "%{http_code}")
|
||||||
|
|
||||||
|
if echo "$group_response" | grep -q "200\|201"; then
|
||||||
|
echo " ✅ Group creation test passed"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Group creation test failed: $group_response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Тест получения групп
|
||||||
|
groups_response=$(curl -s -H "Authorization: Bearer $token" "$api_base/groups/" -w "%{http_code}")
|
||||||
|
if echo "$groups_response" | grep -q "200"; then
|
||||||
|
echo " ✅ Groups list test passed"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Groups list test failed: $groups_response"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Frontend тесты
|
||||||
|
echo "💻 Running frontend tests..."
|
||||||
|
echo " • NextJS build test..."
|
||||||
|
docker-compose exec -T frontend npm run build || {
|
||||||
|
echo "❌ Frontend build test failed"
|
||||||
|
docker-compose logs frontend | tail -50
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo " • Frontend unit tests..."
|
||||||
|
docker-compose exec -T frontend npm test -- --passWithNoTests --watchAll=false || {
|
||||||
|
echo "⚠️ Frontend unit tests failed or no tests found"
|
||||||
|
}
|
||||||
|
|
||||||
|
# E2E тесты (если доступны)
|
||||||
|
echo "🌍 Running E2E tests..."
|
||||||
|
if [ -f "frontend/linktree-frontend/package.json" ] && grep -q "cypress\|playwright" "frontend/linktree-frontend/package.json"; then
|
||||||
|
echo " • Running end-to-end tests..."
|
||||||
|
docker-compose exec -T frontend npm run test:e2e || {
|
||||||
|
echo "⚠️ E2E tests failed"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo " • No E2E tests configured, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Интеграционные тесты
|
||||||
|
echo "🔗 Running integration tests..."
|
||||||
|
echo " • Testing frontend-backend integration..."
|
||||||
|
|
||||||
|
# Проверка что фронтенд может загрузиться
|
||||||
|
frontend_status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000)
|
||||||
|
echo " - Frontend accessibility: $frontend_status"
|
||||||
|
|
||||||
|
# Проверка что фронтенд может обращаться к API
|
||||||
|
api_from_frontend=$(docker-compose exec -T frontend sh -c "
|
||||||
|
curl -s -o /dev/null -w '%{http_code}' http://web:8000/api/
|
||||||
|
") || echo "000"
|
||||||
|
echo " - API accessibility from frontend: $api_from_frontend"
|
||||||
|
|
||||||
|
# Производительные тесты (базовые)
|
||||||
|
echo "⚡ Running basic performance tests..."
|
||||||
|
echo " • API response time test..."
|
||||||
|
api_time=$(curl -s -o /dev/null -w "%{time_total}" "$api_base/")
|
||||||
|
echo " - API response time: ${api_time}s"
|
||||||
|
|
||||||
|
if (( $(echo "$api_time < 2.0" | bc -l) )); then
|
||||||
|
echo " ✅ API response time is acceptable"
|
||||||
|
else
|
||||||
|
echo " ⚠️ API response time is slow"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка использования памяти
|
||||||
|
echo " • Memory usage check..."
|
||||||
|
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}" | grep -E "(web|frontend)"
|
||||||
|
|
||||||
|
# Генерация отчета о тестах
|
||||||
|
echo "📊 Generating test report..."
|
||||||
|
mkdir -p /tmp/test-reports
|
||||||
|
|
||||||
|
cat > /tmp/test-reports/test-summary.txt << EOF
|
||||||
|
CatLink Test Summary
|
||||||
|
==================
|
||||||
|
Date: $(date)
|
||||||
|
Commit: ${DRONE_COMMIT_SHA:-"local"}
|
||||||
|
Branch: ${DRONE_BRANCH:-"local"}
|
||||||
|
|
||||||
|
API Tests:
|
||||||
|
- Root endpoint: $api_root
|
||||||
|
- Registration: $(echo "$register_response" | grep -q "200\|201" && echo "PASS" || echo "FAIL")
|
||||||
|
- Login: $([ -n "$token" ] && echo "PASS" || echo "FAIL")
|
||||||
|
- Groups CRUD: $(echo "$group_response" | grep -q "200\|201" && echo "PASS" || echo "FAIL")
|
||||||
|
|
||||||
|
Frontend Tests:
|
||||||
|
- Build: PASS
|
||||||
|
- Accessibility: $frontend_status
|
||||||
|
|
||||||
|
Integration Tests:
|
||||||
|
- Frontend-Backend: $([ "$api_from_frontend" = "200" ] && echo "PASS" || echo "FAIL")
|
||||||
|
|
||||||
|
Performance:
|
||||||
|
- API Response Time: ${api_time}s
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ All tests completed!"
|
||||||
|
echo "📁 Test reports saved to /tmp/test-reports/"
|
||||||
|
cat /tmp/test-reports/test-summary.txt
|
||||||
Reference in New Issue
Block a user