Files
iistwin/.github/workflows/deploy.yml
Ильяс Султанов 0fb3b54b66 ci(deploy): прод-compose из приватного инфра-репо iistwin-infra (шаг 0.14)
Убраны git stash кастомизаций и восстановление compose из бэкапа вне репо.
Источник правды для прод-compose: iisultanov/iistwin-infra (приватный),
клонируется в /opt/crm-infra deploy-ключом при каждом деплое
2026-09-08 14:27:29 +03:00

176 lines
7.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Deploy to Server
on:
push:
branches:
- main
schedule:
# Daily production deploy at 03:00 UTC.
# All feature branches merged into main during the day are deployed at this time.
- cron: '0 3 * * *'
workflow_dispatch:
# Manual deploy for emergencies only.
jobs:
check:
name: Type Check & Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
# googleapis добавляет очень тяжёлые .d.ts — без увеличения heap tsc падает с OOM (Ineffective mark-compacts)
env:
NODE_OPTIONS: --max-old-space-size=6144
run: npm run check
- name: Type check document worker
run: |
cd server/documents/worker
npm ci
npm run check
- name: Build
run: npm run build
deploy:
name: Deploy to VPS
needs: check
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
timeout-minutes: 50
concurrency:
group: production-deploy
cancel-in-progress: false
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
env:
GHCR_USERNAME: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.SSH_KEY }}
timeout: 60s
command_timeout: 45m
envs: GHCR_USERNAME,GHCR_TOKEN
script: |
set -e
cd /opt/crm
# 1. Получить ровно ту версию кода, что в GitHub (без merge-конфликтов)
git fetch origin main
git reset --hard origin/main
# 2. Прод-compose — из приватного инфра-репозитория (единственный источник правды).
# Раньше: git stash кастомизаций + копия из бэкапа вне репо (дрейф, шаг 0.14).
if [ ! -d /opt/crm-infra/.git ]; then
GIT_SSH_COMMAND="ssh -i /home/deploy/.ssh/id_ed25519_infra" \
git clone git@github.com:iisultanov/iistwin-infra.git /opt/crm-infra
else
git -C /opt/crm-infra fetch origin main
git -C /opt/crm-infra reset --hard origin/main
fi
cp /opt/crm-infra/docker-compose.prod.yml docker-compose.yml
# 5. Удалить старые/зависшие контейнеры, чтобы избежать
# Conflict: container name is already in use
docker stop crm-app-1 crm-document-worker-1 2>/dev/null || true
docker rm -f crm-app-1 crm-document-worker-1 2>/dev/null || true
docker compose stop app document-worker || true
docker compose rm -f app document-worker || true
docker ps -aq --filter "name=crm-app" | xargs -r docker rm -f || true
docker ps -aq --filter "name=crm-document-worker" | xargs -r docker rm -f || true
# 5.5 Логин в GitHub Container Registry для скачивания prebuilt образа document-worker
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GHCR_USERNAME" --password-stdin
# 5.6 Мягкая очистка: удалить старые контейнеры и dangling образы,
# но оставить build cache для ускорения сборки CRM.
docker system prune -f
# 6. Собрать и запустить приложение.
# document-worker больше не собирается здесь — используется готовый образ из GHCR.
# BUILD_ARG инвалидирует слой сборки фронтенда, если код изменился,
# но позволяет Docker кэшировать слои npm ci.
# Retry 3 times to work around transient Alpine mirror I/O errors.
BUILD_SHA=${{ github.sha }}
BUILD_OK=false
for attempt in 1 2 3; do
echo "=== Docker build attempt $attempt ==="
if docker compose build --build-arg BUILD_SHA=$BUILD_SHA --build-arg ALPINE_MIRROR=mirrors.aliyun.com app; then
BUILD_OK=true
break
fi
echo "Build failed on attempt $attempt"
if [ "$attempt" -lt 3 ]; then
echo "Cleaning Docker cache and retrying in 30s..."
docker system prune -a -f
docker builder prune -f
sleep 30
fi
done
if [ "$BUILD_OK" != "true" ]; then
echo "Docker build failed after 3 attempts"
exit 1
fi
# 6.5 Получить свежий prebuilt образ document-worker из GHCR
docker compose pull document-worker
docker compose up -d app document-worker
# 7. Дождаться healthcheck'ов
echo "Waiting for healthchecks..."
APP_HEALTHY=false
WORKER_HEALTHY=false
for i in $(seq 1 30); do
if [ "$APP_HEALTHY" != "true" ] && docker ps | grep crm-app | grep -q healthy; then
echo "iistwin app is healthy!"
APP_HEALTHY=true
fi
if [ "$WORKER_HEALTHY" != "true" ] && docker ps | grep crm-document-worker | grep -q healthy; then
echo "document-worker is healthy!"
WORKER_HEALTHY=true
fi
if [ "$APP_HEALTHY" = "true" ] && [ "$WORKER_HEALTHY" = "true" ]; then
break
fi
sleep 2
done
if [ "$APP_HEALTHY" != "true" ]; then
echo "App healthcheck failed"
exit 1
fi
if [ "$WORKER_HEALTHY" != "true" ]; then
echo "Document worker healthcheck failed"
exit 1
fi
# 8. Мягкая очистка Docker: оставляем build cache и tagged базовые образы,
# чтобы следующий деплой переиспользовал слои npm ci и сборки.
# Полная очистка запускается ежедневно через cron (disk-check.sh / docker-cleanup.sh).
docker system prune -f
docker builder prune -f --keep-storage 5gb
# 9. Удалить старые образы crm-app, чтобы они не копились между деплоями.
# Текущий образ используется запущенным контейнером, поэтому он не удалится.
CURRENT_IMAGE_ID=$(docker inspect --format='{{.Image}}' crm-app-1)
docker images crm-app --format "{{.ID}}" | grep -v "$CURRENT_IMAGE_ID" | xargs -r docker rmi -f || true
echo "Cleanup completed"