Obsidian Self-hosted LiveSync (CouchDB) выведен в отдельный docker-compose stack и GHA workflow. Любой стек теперь редеплоится независимо. * docker-compose.obsidian.yml: только couchdb в shared external network * docker-compose.prod.yml: убрана couchdb-секция; caddy подключён к shared network для маршрута obsidian.gendsgn.ru → couchdb:5984 * .github/workflows/deploy-obsidian.yml: новый workflow с path-filters (триггерится только при изменениях obsidian-related файлов) * .github/workflows/deploy.yml: + path-filters (триггерится только при main-related файлах) + создание shared network в bootstrap * docs/obsidian-livesync.md: обновлены инструкции под split-архитектуру После push: 1. ssh gendesign 'docker network create gendesign_shared' (один раз) 2. Прописать COUCHDB_USER/PASSWORD в backend/.env.runtime 3. Добавить DNS A-record obsidian.gendsgn.ru → IP VPS 4. git push → GHA задеплоит обе части независимо Преимущества: - Изменения main не разрывают LiveSync клиентов - Изменения obsidian не рестартуют main - Падение obsidian-стека не влияет на основное приложение
134 lines
5.1 KiB
YAML
134 lines
5.1 KiB
YAML
name: Deploy
|
||
|
||
# Деплоится только при изменениях основного стека.
|
||
# Obsidian-стек (CouchDB) — отдельный workflow `deploy-obsidian.yml`.
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- "backend/**"
|
||
- "frontend/**"
|
||
- "docker-compose.prod.yml"
|
||
- "Caddyfile"
|
||
- ".github/workflows/deploy.yml"
|
||
# NB: shared compose-fragments (network) — тоже триггерят main
|
||
# потому что Caddy шарит сеть с obsidian-stack
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: deploy-prod
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
IMAGE_BACKEND: ghcr.io/lekss361/gendesign-backend
|
||
IMAGE_WORKER: ghcr.io/lekss361/gendesign-worker
|
||
IMAGE_FRONTEND: ghcr.io/lekss361/gendesign-frontend
|
||
|
||
jobs:
|
||
build-and-deploy:
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Set image tag
|
||
id: meta
|
||
run: echo "tag=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Login to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ghcr.io
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Build & push backend (lean — без Chromium)
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ./backend
|
||
target: runner
|
||
push: true
|
||
cache-from: type=gha,scope=backend
|
||
cache-to: type=gha,mode=max,scope=backend
|
||
tags: |
|
||
${{ env.IMAGE_BACKEND }}:latest
|
||
${{ env.IMAGE_BACKEND }}:${{ steps.meta.outputs.tag }}
|
||
|
||
- name: Build & push worker (с Chromium для Playwright)
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ./backend
|
||
target: runner-with-chromium
|
||
push: true
|
||
cache-from: type=gha,scope=worker
|
||
cache-to: type=gha,mode=max,scope=worker
|
||
tags: |
|
||
${{ env.IMAGE_WORKER }}:latest
|
||
${{ env.IMAGE_WORKER }}:${{ steps.meta.outputs.tag }}
|
||
|
||
- name: Build & push frontend
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ./frontend
|
||
push: true
|
||
cache-from: type=gha,scope=frontend
|
||
cache-to: type=gha,mode=max,scope=frontend
|
||
tags: |
|
||
${{ env.IMAGE_FRONTEND }}:latest
|
||
${{ env.IMAGE_FRONTEND }}:${{ steps.meta.outputs.tag }}
|
||
|
||
- name: Deploy to VM via SSH
|
||
uses: appleboy/ssh-action@v1.0.3
|
||
env:
|
||
IMAGE_TAG: ${{ steps.meta.outputs.tag }}
|
||
with:
|
||
host: ${{ secrets.DEPLOY_HOST }}
|
||
username: ${{ secrets.DEPLOY_USER }}
|
||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||
envs: IMAGE_TAG
|
||
script: |
|
||
set -euo pipefail
|
||
cd /opt/gendesign
|
||
|
||
# Sync compose / Caddyfile / init scripts from the repo.
|
||
# --ff-only refuses if there are local commits on the VM (we don't expect any).
|
||
git fetch origin main
|
||
git reset --hard origin/main
|
||
|
||
# Sentry release tracking — записываем git-sha в .env.runtime
|
||
# (отдельный файл, чтобы не трогать ручной .env с секретами).
|
||
# backend/worker/beat подхватывают его через env_file (см. compose).
|
||
mkdir -p backend
|
||
printf 'SENTRY_RELEASE=%s\n' "$IMAGE_TAG" > backend/.env.runtime
|
||
|
||
# Создать external network если её нет (нужна Caddy для маршрута
|
||
# obsidian.gendsgn.ru → couchdb из отдельного obsidian-stack).
|
||
docker network inspect gendesign_shared >/dev/null 2>&1 \
|
||
|| docker network create gendesign_shared
|
||
|
||
export IMAGE_TAG="$IMAGE_TAG"
|
||
# Project name явно — `gendesign` (по имени папки auto), но
|
||
# фиксируем для consistency с obsidian-stack (-p gendesign-obsidian).
|
||
docker compose -p gendesign -f docker-compose.prod.yml pull
|
||
docker compose -p gendesign -f docker-compose.prod.yml up -d
|
||
|
||
# Caddyfile is bind-mounted; up -d won't re-read it. Reload explicitly.
|
||
# `|| true` so a temporary Caddy hiccup doesn't fail the whole deploy.
|
||
docker compose -p gendesign -f docker-compose.prod.yml exec -T caddy \
|
||
caddy reload --config /etc/caddy/Caddyfile || true
|
||
|
||
# Clean up disk: dangling layers + tagged images older than 72h that
|
||
# nothing is using anymore. Plus build cache (we never build on prod
|
||
# but old buildkit caches may linger from earlier compose builds).
|
||
docker image prune -af --filter "until=72h" || true
|
||
docker builder prune -af || true
|
||
|
||
sleep 8
|
||
curl -fsS http://localhost:8000/health | head -c 200 || true
|