uchill/.github/workflows/ci-cd.yml

263 lines
7.4 KiB
YAML

name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
tags: [ 'v*' ]
pull_request:
branches: [ main, develop ]
env:
POSTGRES_DB: platform_test
POSTGRES_USER: platform_user
POSTGRES_PASSWORD: test_password
DATABASE_URL: "postgresql://platform_user:test_password@localhost:5432/platform_test"
REDIS_URL: "redis://localhost:6379/0"
jobs:
# Backend тесты
test-backend:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: ${{ env.POSTGRES_DB }}
POSTGRES_USER: ${{ env.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
working-directory: ./backend
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Django checks
working-directory: ./backend
env:
DJANGO_SETTINGS_MODULE: config.settings
SECRET_KEY: test-secret-key-for-ci
DEBUG: False
ALLOWED_HOSTS: "*"
run: python manage.py check
- name: Run tests with coverage
working-directory: ./backend
env:
DJANGO_SETTINGS_MODULE: config.settings
SECRET_KEY: test-secret-key-for-ci
DEBUG: False
ALLOWED_HOSTS: "*"
run: |
pytest --cov=apps --cov-report=xml --cov-report=html --cov-report=term-missing
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./backend/coverage.xml
flags: backend
name: backend-coverage
# Backend linting
lint-backend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install linting tools
run: pip install flake8 black isort
- name: Run flake8
working-directory: ./backend
run: flake8 apps config --max-line-length=120 --exclude=migrations,__pycache__
- name: Run black
working-directory: ./backend
run: black --check apps config --exclude migrations
- name: Run isort
working-directory: ./backend
run: isort --check-only apps config --skip migrations
# Frontend тесты
test-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js 18
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
working-directory: ./frontend
run: npm ci
- name: Run linting
working-directory: ./frontend
run: npm run lint
- name: Run type checking
working-directory: ./frontend
run: npm run type-check || true
- name: Build
working-directory: ./frontend
run: npm run build
# Security проверки
security-backend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install security tools
run: pip install safety bandit
- name: Run safety check
working-directory: ./backend
run: safety check --json || true
- name: Run bandit
working-directory: ./backend
run: bandit -r apps config -ll || true
# Сборка Docker образов
build:
needs: [test-backend, test-frontend, lint-backend]
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags/v'))
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ secrets.DOCKER_USERNAME }}/platform-backend
${{ secrets.DOCKER_USERNAME }}/platform-frontend
- name: Build and push Backend image
uses: docker/build-push-action@v5
with:
context: ./backend
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/platform-backend:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push Frontend image
uses: docker/build-push-action@v5
with:
context: ./frontend
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/platform-frontend:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Deploy на Staging
deploy-staging:
needs: [build]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.platform.example.com
steps:
- name: Deploy to Staging
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/platform
docker compose pull
docker compose up -d
# Deploy на Production
deploy-production:
needs: [build]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
environment:
name: production
url: https://platform.example.com
steps:
- name: Deploy to Production
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PRODUCTION_HOST }}
username: ${{ secrets.PRODUCTION_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/platform
docker compose pull
docker compose up -d
- name: Notify deployment
if: always()
run: echo "Deployment completed with status ${{ job.status }}"