name: Deploy # ───────────────────────────────────────────────────────────────────────────── # Zincir: validate → build-push (backend+frontend) → deploy → verify # Trendyol-analiz tertemiz auto-deploy — SellerX deploy-frontend.yml pattern'i # ───────────────────────────────────────────────────────────────────────────── # K8s pipeline (Gitea mirror → Gitea Actions → Gitea registry → ArgoCD) parallel # çalışır — Coolify pipeline'ı DURDURMAZ. # ───────────────────────────────────────────────────────────────────────────── on: push: branches: [main] workflow_dispatch: env: REGISTRY: ghcr.io BACKEND_IMAGE: ${{ github.repository }}/backend FRONTEND_IMAGE: ${{ github.repository }}/frontend PYTHON_VERSION: '3.13' NODE_VERSION: '20' jobs: # ─────────────────────────────────────────────────────────────────────────── # K8s MIRROR SYNC — Coolify pipeline'dan BAĞIMSIZ, validate ile PARALEL. # GitHub push → Gitea mirror sync → Gitea Actions → Gitea registry → ArgoCD # Başarısız olursa Coolify pipeline DURMUYOR. # ─────────────────────────────────────────────────────────────────────────── sync-gitea: name: Sync Gitea Mirror (K8s Pipeline) runs-on: ubuntu-latest steps: - name: Trigger Gitea mirror sync run: | HTTP=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST "https://git.novasis.tr/api/v1/repos/admin/trendyol-analiz/mirror-sync" \ -H "Authorization: token ${{ secrets.GITEA_MIRROR_TOKEN }}") echo "Mirror sync HTTP: $HTTP" if [ "$HTTP" -ge 400 ]; then echo "Mirror sync failed (HTTP $HTTP) — K8s pipeline delayed, Coolify unaffected" exit 0 fi echo "Mirror sync triggered — waiting 30s for sync to complete..." sleep 30 - name: Dispatch Gitea Actions build (deploy-backend.yaml) run: | HTTP=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST "https://git.novasis.tr/api/v1/repos/admin/trendyol-analiz/actions/workflows/deploy-backend.yaml/dispatches" \ -H "Authorization: token ${{ secrets.GITEA_MIRROR_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"ref":"main"}') echo "Gitea backend dispatch HTTP: $HTTP" if [ "$HTTP" -ge 400 ]; then echo "Gitea backend dispatch failed — trigger manually" fi - name: Dispatch Gitea Actions build (deploy-frontend.yaml) run: | HTTP=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST "https://git.novasis.tr/api/v1/repos/admin/trendyol-analiz/actions/workflows/deploy-frontend.yaml/dispatches" \ -H "Authorization: token ${{ secrets.GITEA_MIRROR_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"ref":"main"}') echo "Gitea frontend dispatch HTTP: $HTTP" if [ "$HTTP" -ge 400 ]; then echo "Gitea frontend dispatch failed — trigger manually" fi # ─────────────────────────────────────────────────────────────────────────── # ADIM 1a — Backend hızlı sağlık testi (pytest) # ─────────────────────────────────────────────────────────────────────────── validate-backend: name: 🐍 Backend Test runs-on: ubuntu-latest services: postgres: image: postgres:15-alpine env: POSTGRES_DB: trendyol_db POSTGRES_USER: postgres POSTGRES_PASSWORD: testpassword ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 defaults: run: working-directory: backend steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' cache-dependency-path: backend/requirements.txt - name: Install deps run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-asyncio httpx || true - name: Run tests run: pytest -q || echo "No tests / skipped" env: DATABASE_URL: postgresql://postgres:testpassword@localhost:5432/trendyol_db # ─────────────────────────────────────────────────────────────────────────── # ADIM 1b — Frontend lint + build (Vite) # ─────────────────────────────────────────────────────────────────────────── validate-frontend: name: ⚛️ Frontend Lint & Build runs-on: ubuntu-latest defaults: run: working-directory: admin-panel steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' cache-dependency-path: admin-panel/package-lock.json - run: npm ci - run: npm run lint || true - name: Production build run: npm run build env: VITE_API_URL: https://trendyol-api.194.187.253.230.sslip.io # ─────────────────────────────────────────────────────────────────────────── # ADIM 2a — Backend image build & push (GHCR) # ─────────────────────────────────────────────────────────────────────────── build-push-backend: name: 🐳 Build & Push Backend needs: [validate-backend, validate-frontend] runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }} tags: | type=sha,prefix= type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: backend/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha,scope=backend cache-to: type=gha,mode=max,scope=backend # ─────────────────────────────────────────────────────────────────────────── # ADIM 2b — Frontend image build & push (GHCR) # ─────────────────────────────────────────────────────────────────────────── build-push-frontend: name: 🐳 Build & Push Frontend needs: [validate-backend, validate-frontend] runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }} tags: | type=sha,prefix= type=raw,value=latest - uses: docker/build-push-action@v5 with: context: ./admin-panel push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | VITE_API_URL=https://trendyol-api.194.187.253.230.sslip.io cache-from: type=gha,scope=frontend cache-to: type=gha,mode=max,scope=frontend # ─────────────────────────────────────────────────────────────────────────── # ADIM 3 — Coolify deploy webhook # Deployment UUID'yi output'a yaz → verify job bu spesifik deploy'u izleyecek # ─────────────────────────────────────────────────────────────────────────── deploy: name: 🚀 Deploy to Coolify needs: [build-push-backend, build-push-frontend] runs-on: ubuntu-latest outputs: deployment_uuid: ${{ steps.trigger.outputs.deployment_uuid }} steps: - name: Trigger Coolify deploy id: trigger run: | response=$(curl -s -w "\n%{http_code}" \ -X POST "${{ secrets.COOLIFY_BASE_URL }}/api/v1/deploy?uuid=${{ secrets.COOLIFY_TRENDYOL_UUID }}&force=true" \ -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}") http_code=$(echo "$response" | tail -1) body=$(echo "$response" | head -n -1) echo "Response: $body (HTTP $http_code)" if [ "$http_code" -ge 400 ]; then echo "❌ Coolify deploy trigger failed!" exit 1 fi # Deployment UUID'yi parse et DEPLOY_UUID=$(echo "$body" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['deployments'][0]['deployment_uuid'])") echo "deployment_uuid=$DEPLOY_UUID" >> "$GITHUB_OUTPUT" echo "✅ Coolify deploy queued — deployment_uuid: $DEPLOY_UUID" # ─────────────────────────────────────────────────────────────────────────── # ADIM 4 — Verify deployment # Spesifik deployment_uuid'yi poll et: queued → in_progress → finished/failed # ─────────────────────────────────────────────────────────────────────────── verify: name: ✅ Verify Deployment needs: deploy runs-on: ubuntu-latest steps: - name: Coolify deployment status polling run: | DEPLOY_UUID="${{ needs.deploy.outputs.deployment_uuid }}" DEPLOY_URL="${{ secrets.COOLIFY_BASE_URL }}/api/v1/deployments/$DEPLOY_UUID" MAX=60 # 60 × 10s = 10 dk INTERVAL=10 echo "🔍 Polling deployment $DEPLOY_UUID" for i in $(seq 1 $MAX); do STATUS=$(curl -s --max-time 10 \ -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}" \ "$DEPLOY_URL" 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin).get('status','?'))" 2>/dev/null || echo "unknown") echo "[$i/$MAX] $STATUS" case "$STATUS" in finished|success) echo "✅ Coolify deployment finished" exit 0 ;; failed|error|cancelled) echo "❌ Coolify deployment status: $STATUS" echo "Panel: ${{ secrets.COOLIFY_BASE_URL }}" exit 1 ;; esac sleep $INTERVAL done echo "⚠️ Timeout — deployment hala '$STATUS' (10 dk geçti) — public URL health check'e geçiliyor" - name: Public URL health check run: | MAX=15 INTERVAL=10 URL="https://trendyol.194.187.253.230.sslip.io" for i in $(seq 1 $MAX); do CODE=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 10 "$URL" 2>/dev/null || echo "000") echo "[$i/$MAX] $URL → HTTP $CODE" if [ "$CODE" = "200" ] || [ "$CODE" = "302" ] || [ "$CODE" = "307" ]; then echo "✅ Frontend canlı" exit 0 fi sleep $INTERVAL done echo "❌ Public URL erişilemez — Coolify panelini kontrol et" exit 1 # ─────────────────────────────────────────────────────────────────────────── # Hata bildirimi — GitHub Issue otomatik aç # ─────────────────────────────────────────────────────────────────────────── notify-failure: name: 📢 Notify on Failure needs: [validate-backend, validate-frontend, build-push-backend, build-push-frontend, deploy, verify] runs-on: ubuntu-latest if: failure() permissions: issues: write steps: - uses: actions/github-script@v7 with: script: | const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; const shortSha = context.sha.substring(0, 7); const title = `🚨 Deploy başarısız — Run #${context.runNumber}`; const body = [ `## ❌ Production deploy başarısız`, ``, `| Alan | Değer |`, `|------|-------|`, `| **Branch** | \`${context.ref.replace('refs/heads/', '')}\` |`, `| **Commit** | \`${shortSha}\` |`, `| **Run** | [#${context.runNumber}](${runUrl}) |`, ``, `[Logları incele](${runUrl})` ].join('\n'); const { data: issues } = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo, labels: 'deploy-failure', state: 'open' }); if (issues.length > 0) { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issues[0].number, body }); } else { await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title, body, labels: ['deploy-failure'] }); }