CI/CD Pipeline
CI Workflow (ci.yml)
Her push ve PR'da otomatik çalışır:
1. backend-lint
ruff check app/— Lintingruff 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ırpytest 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— ESLintnpx 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.ts—getWsUrl()SSR/localhost/prod/env overridefrontend/src/lib/websocket/client.test.ts— mixed-content guard + ctor exceptionfrontend/src/lib/websocket/context.test.tsx— Provider defansif init + cleanupfrontend/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_URLartı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:
- SSH setup — webfactory/ssh-agent
- rsync — Dosya senkronizasyonu (exclude: .git, node_modules, .env, certs)
- .env oluştur — GitHub Secrets'tan
- TLS kontrol — SSL sertifika varlığı
- Infrastructure start — postgres, redis, emqx, minio
- Migration —
alembic upgrade ${MIGRATION_TARGET:-head}(PR #277 sonrası varsayılanhead) - Image build — backend, frontend (15dk timeout), modbus-poller
- Container start —
docker compose up -d - Health check — Backend, frontend, nginx HTTPS
- Code audit — Claude CLI ile post-deploy analiz
- 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):
| Job | Timeout (üst sınır) | Tipik süre (warm cache) |
|---|---|---|
| backend build | 60 dk | < 5 dk |
| frontend build | 60 dk | < 8 dk |
| modbus-poller build | 45 dk | < 3 dk |
| whatsapp-bridge build | 45 dk | < 2 dk |
CAX (ARM64) — Hetzner Geçişi
| Kriter | CPX (x86) | CAX (ARM64) |
|---|---|---|
| Fiyat (eşdeğer kapasite) | Yüksek | ~%30 daha düşük |
| RAM (eşdeğer fiyat) | 4GB | 8GB |
| TimescaleDB performans | Stabil | Stabil (testler geçti) |
| Build platform | linux/amd64,linux/arm64 | linux/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ış
- CI runner'da image build → ghcr.io'ya push
- Deploy adımı sadece
docker pull+docker compose up -dçalıştırır - 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:
| Tag | Anlam |
|---|---|
:latest | En son başarılı build |
:previous | Bir ö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)
headPR #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:
workflow_dispatchinput — manuel deploy tetiklerkenMIGRATION_TARGET=0037_add_ocpp_tablesver.- Repo env değişkeni —
vars.MIGRATION_TARGETset edilirse${MIGRATION_TARGET:-head}fallback'i devreden çıkar.
Hangi durumda override:
| Senaryo | Hedef | Gerekçe |
|---|---|---|
| Hot-fix, yeni migration'ı staging'de test etmek istiyorsun | Bir önceki revision (örn. 0037_add_ocpp_tables) | Yeni migration'ı sadece prod'a değil staging'de önce uygula |
| Rollback sonrası alembic_version stuck | Spesifik revision id | Manuel kontrolü garanti et |
| Production'da rutin deploy | head (varsayılan) | Otomatik akış — 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.