Ana içeriğe geç

CI/CD Pipeline

CI Workflow (ci.yml)

Her push ve PR'da otomatik çalışır:

1. backend-lint

  • ruff check app/ — Linting
  • ruff format --check app/ — Format kontrolü
  • mypy app/ --ignore-missing-imports — Type check

2. backend-test

  • PostgreSQL 15 + Redis 7 servisler başlatılır
  • alembic upgrade head — Migration'lar uygulanır
  • pytest tests/ -v --cov=app — Testler çalıştırılır

3. firmware-test

  • pio test -e native — PlatformIO unit testleri

4. frontend-lint

  • npm run lint — ESLint
  • npx tsc --noEmit — TypeScript kontrolü

5. frontend-test (PR #276 sonrası eklendi — BLOCKING)

  • npm test — Vitest, 43 test
  • Kapsam (WS refactor release gate):
    • frontend/src/config/site.test.tsgetWsUrl() SSR/localhost/prod/env override
    • frontend/src/lib/websocket/client.test.ts — mixed-content guard + ctor exception
    • frontend/src/lib/websocket/context.test.tsx — Provider defansif init + cleanup
    • frontend/src/lib/websocket/WebSocketErrorBoundary.test.tsx — boundary fallback
  • Yeşil olmazsa merge engellenir.

6. frontend-build

  • npm run build — Production build
  • Önemli: NEXT_PUBLIC_WS_URL artık build-arg olarak verilmiyor (PR #276). Detay: Frontend Kritik Notlar.

7. docker-build (sadece PR)

  • Docker Buildx ile multi-platform build (push yok)

Deploy Workflow (deploy.yml)

CI başarılı olunca otomatik tetiklenir:

  1. SSH setup — webfactory/ssh-agent
  2. rsync — Dosya senkronizasyonu (exclude: .git, node_modules, .env, certs)
  3. .env oluştur — GitHub Secrets'tan
  4. TLS kontrol — SSL sertifika varlığı
  5. Infrastructure start — postgres, redis, emqx, minio
  6. Migrationalembic upgrade ${MIGRATION_TARGET:-head} (PR #277 sonrası varsayılan head)
  7. Image build — backend, frontend (15dk timeout), modbus-poller
  8. Container startdocker compose up -d
  9. Health check — Backend, frontend, nginx HTTPS
  10. Code audit — Claude CLI ile post-deploy analiz
  11. Monitoring cron — health-check, container-monitor kurulumu

Release Workflow (release.yml)

Git tag push (v*) ile tetiklenir:

  • GitHub release + changelog oluşturma
  • ghcr.io'ya Docker image push (tag + latest)

ARM64 Build

Prod sunucusu (Zeus, Hetzner CAX) ARM64. CI/CD pipeline 2026-05-19 itibarıyla yalnız linux/arm64 build üretir; tüm tüketiciler (prod + dev'lerin Apple Silicon makineleri) ARM64. amd64 ihtiyacı doğarsa geri dönüş 1 PR / 4 satır / 5 dk — QEMU + buildx + GHA cache infrastructure aynen yerinde duruyor.

CI Konfigürasyonu

- uses: docker/setup-qemu-action@v3
with:
platforms: arm64

- uses: docker/setup-buildx-action@v3

- uses: docker/build-push-action@v6
with:
platforms: linux/arm64
push: true
cache-from: type=gha
cache-to: type=gha,mode=max

Build Timeout

Multi-arch döneminde (2026-05 öncesi) QEMU amd64 emülasyonu frontend build'i 25-30 dakikaya çıkarıyordu. ARM64-only geçişle native hız geri kazanıldı; timeout değerleri üst sınır güvenliği olarak korunuyor (cache cold start veya runner state pollution için marj):

JobTimeout (üst sınır)Tipik süre (warm cache)
backend build60 dk< 5 dk
frontend build60 dk< 8 dk
modbus-poller build45 dk< 3 dk
whatsapp-bridge build45 dk< 2 dk

CAX (ARM64) — Hetzner Geçişi

KriterCPX (x86)CAX (ARM64)
Fiyat (eşdeğer kapasite)Yüksek~%30 daha düşük
RAM (eşdeğer fiyat)4GB8GB
TimescaleDB performansStabilStabil (testler geçti)
Build platformlinux/amd64,linux/arm64linux/arm64 (multi-arch kaldırıldı 2026-05-19)

CAX'e geçiş özellikle 4GB → 8GB RAM upgrade'i için tercih edildi (TimescaleDB OOM-kill çözümü).


Build CI Runner Migration

Daha önce frontend/backend image build'leri deploy SSH session içinde yapılıyordu. Bu yaklaşımın sorunları:

  • SSH connection timeout → silent build failure
  • Sunucuda CPU/RAM kullanımı build sırasında piklenir, prod servisler etkilenir
  • Build cache sunucu disk'inde tutulur → disk şişmesi
  • Hata yakalama zayıf (output buffer kesilebilir)

Yeni Akış

  1. CI runner'da image build → ghcr.io'ya push
  2. Deploy adımı sadece docker pull + docker compose up -d çalıştırır
  3. Build hataları CI'da yakalanır, deploy hiç başlamaz
- name: Build & push backend
uses: docker/build-push-action@v6
with:
context: ./backend
platforms: linux/arm64
push: true
tags: |
ghcr.io/gucluceyhan/zeus-backend:latest
ghcr.io/gucluceyhan/zeus-backend:${{ github.sha }}

Tag Rotation + Rollback

Her başarılı build üç tag ile push'lanır:

TagAnlam
:latestEn son başarılı build
:previousBir önceki başarılı build (rollback hedefi)
:<sha>Commit-spesifik tag (immutable referans)

Rollback Akışı

Production'da bir sorun yakalandığında:

# Hızlı rollback (önceki sürüme dön)
docker pull ghcr.io/gucluceyhan/zeus-backend:previous
docker tag ghcr.io/gucluceyhan/zeus-backend:previous \
ghcr.io/gucluceyhan/zeus-backend:latest
docker compose up -d backend

:previous tag'i her başarılı deploy'da otomatik güncellenir (bir önceki :latest rotate edilir). Manuel rollback için :<sha> tag'i de kullanılabilir:

docker pull ghcr.io/gucluceyhan/zeus-backend:abc1234

Post-deploy Health Check

Deploy sonrası 90 saniye boyunca her 5 saniyede bir container sağlığı kontrol edilir. Bu süre içinde container healthy olmazsa otomatik rollback tetiklenir.

TIMEOUT=90
INTERVAL=5
ELAPSED=0

while [ $ELAPSED -lt $TIMEOUT ]; do
STATUS=$(docker inspect --format='{{.State.Status}}' backend)
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' backend)

if [ "$STATUS" = "running" ] && [ "$HEALTH" = "healthy" ]; then
echo "Deploy başarılı (${ELAPSED}s)"
exit 0
fi

sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
done

echo "Deploy başarısız — rollback başlatılıyor"
docker tag ghcr.io/gucluceyhan/zeus-backend:previous \
ghcr.io/gucluceyhan/zeus-backend:latest
docker compose up -d backend
exit 1

Health check Dockerfile'da tanımlanır (her servis için ayrı): HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1


MIGRATION_TARGET — Politika (PR #277 ile güncellendi)

Yeni varsayılan: head

PR #277 ile deploy.yml:244 satırındaki MIGRATION_TARGET varsayılanı merge_sofar_mqtt'ten head'e taşındı. Önceden 0037 ve 0038 gibi yeni revisionlar otomatik deploy'a giremiyordu — workflow_dispatch override veya manuel SSH apply gerektiriyordu. Bu durum production drift riski (kod yeni şemayı bekler, DB eski şemada kalır) yaratıyordu.

Mevcut akış

# deploy.sh içinde — CI workflow tarafından sunucuya yazılıyor
MIGRATION_TARGET="${MIGRATION_TARGET:-head}"
$DOCKER_COMPOSE run --rm backend alembic upgrade "${MIGRATION_TARGET}"

head güvenle kullanılabilir çünkü single-head invariant CI guard hâlâ aktif (migration-sanity job, aşağıda). Multiple heads durumunda deploy başlamadan PR/merge engellenir.

Override politikası — ne zaman kullanılır

MIGRATION_TARGET'ı spesifik revision'a pin'lemek için iki yöntem:

  1. workflow_dispatch input — manuel deploy tetiklerken MIGRATION_TARGET=0037_add_ocpp_tables ver.
  2. Repo env değişkenivars.MIGRATION_TARGET set edilirse ${MIGRATION_TARGET:-head} fallback'i devreden çıkar.

Hangi durumda override:

SenaryoHedefGerekçe
Hot-fix, yeni migration'ı staging'de test etmek istiyorsunBir önceki revision (örn. 0037_add_ocpp_tables)Yeni migration'ı sadece prod'a değil staging'de önce uygula
Rollback sonrası alembic_version stuckSpesifik revision idManuel kontrolü garanti et
Production'da rutin deployhead (varsayılan)Otomatik akış — override yapma
Production'da gereksiz override yapma

Production'da MIGRATION_TARGET override etmek migration drift'in başlangıç noktasıdır. head varsayılanı ve migration-sanity guard'ı ile birlikte güvenli. Ancak yeni bir migration emergency rollback gerektiriyorsa kısa süreliğine spesifik revision'a pin'leyip ardından kaldırın.


migration-sanity Job (single-head invariant)

CI pipeline'ında pre-deploy doğrulama olarak çalışır. Multiple heads varsa deploy adımı tetiklenmez.

migration-sanity:
runs-on: ubuntu-latest
steps:
- run: |
HEADS=$(alembic heads | wc -l)
if [ "$HEADS" -ne 1 ]; then
echo "ERROR: Multiple alembic heads — merge migration gerekli"
alembic heads
exit 1
fi

Bu guard tek satır head garantisi sağlar. Detay: Migration Rehberi — Multiple Heads.