mirror of
https://github.com/nethunterzist/trendyol-analiz
synced 2026-07-01 09:27:03 +00:00
Ne yaptık: - deploy job: Coolify response'undan deployment_uuid parse edip GITHUB_OUTPUT'a yazıyor. - verify job: artık uygulama UUID'si yerine spesifik deployment UUID'sini polluyor (queued → in_progress → finished/failed geçişleri). - Polling süresi 5 dk → 10 dk (max 60 × 10s). Neden yaptık: - Eski verify uygulama UUID'sini polling ediyordu. Yeni deploy başlamadan önceki eski "running:healthy" status'u yakalayıp 4 saniyede early-exit yapıyordu. Workflow yeşil görünür ama gerçek deploy'u doğrulamamış olur. - Yeni davranış: workflow gerçekten Coolify deploy'unun bitmesini bekliyor. Fail olursa workflow da fail eder, notify-failure job tetiklenir.
319 lines
13 KiB
YAML
319 lines
13 KiB
YAML
name: Deploy
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Zincir: validate → build-push (backend+frontend) → deploy → verify
|
||
# Trendyol-analiz tertemiz auto-deploy — SellerX deploy-frontend.yml pattern'i
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
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:
|
||
# ───────────────────────────────────────────────────────────────────────────
|
||
# 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']
|
||
});
|
||
}
|