diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c6806c..7d12e71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,31 +2,46 @@ name: CI on: push: - branches: [main, master, develop] + branches: [main] pull_request: - branches: [main, master, develop] + branches: [main] jobs: + # ── API: build + test ────────────────────────────────────────────────────── api: + name: API (dotnet build + test) runs-on: ubuntu-latest services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_DB: meezi_test + POSTGRES_USER: meezi + POSTGRES_PASSWORD: meezi_test_pass + ports: ["5432:5432"] + options: --health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10 redis: image: redis:7-alpine - ports: - - 6379:6379 + ports: ["6379:6379"] + options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10 steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: dotnet-version: "10.0.x" - name: Restore - run: dotnet restore src/Meezi.API/Meezi.API.csproj + run: dotnet restore - name: Build - run: dotnet build src/Meezi.API/Meezi.API.csproj --no-restore -c Release + run: dotnet build --no-restore -c Release - name: Test - run: dotnet test tests/Meezi.API.Tests/Meezi.API.Tests.csproj -c Release + run: dotnet test --no-build -c Release --logger "console;verbosity=minimal" + env: + ConnectionStrings__DefaultConnection: Host=localhost;Port=5432;Database=meezi_test;Username=meezi;Password=meezi_test_pass + ConnectionStrings__Redis: localhost:6379 - web: + # ── Dashboard: typecheck ─────────────────────────────────────────────────── + dashboard: + name: Dashboard (Next.js typecheck) runs-on: ubuntu-latest defaults: run: @@ -38,38 +53,26 @@ jobs: node-version: "20" cache: npm cache-dependency-path: web/dashboard/package-lock.json - - run: npm ci - - run: npm run build + - run: npm install --legacy-peer-deps --ignore-scripts + - run: npx tsc --noEmit env: NEXT_PUBLIC_API_URL: http://localhost:5080 - e2e: + # ── Finder: typecheck ────────────────────────────────────────────────────── + finder: + name: Finder (Next.js typecheck) runs-on: ubuntu-latest - continue-on-error: true defaults: run: - working-directory: web/dashboard + working-directory: web/finder steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm - cache-dependency-path: web/dashboard/package-lock.json - - run: npm ci - - run: npx playwright install chromium --with-deps - - name: E2E (API-only smoke; set PLAYWRIGHT_API_URL when API service available) - run: npm run test:e2e -- e2e/api-health.spec.ts + cache-dependency-path: web/finder/package-lock.json + - run: npm install --legacy-peer-deps --ignore-scripts + - run: npx tsc --noEmit env: - PLAYWRIGHT_API_URL: http://localhost:5080 - - flutter: - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - channel: stable - - run: flutter analyze mobile/meezi_app - - run: flutter analyze mobile/meezi_pos + NEXT_PUBLIC_API_URL: http://localhost:5080 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 801f967..4be60a8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,20 +1,71 @@ -name: Deploy +name: Deploy to Production on: push: - tags: - - "v*" + branches: [main] + +# Only one deploy runs at a time; newer push cancels in-progress one +concurrency: + group: production-deploy + cancel-in-progress: true jobs: - build-images: - runs-on: ubuntu-latest + deploy: + name: Build & deploy on server + runs-on: self-hosted # ← runs on YOUR Linux server + timeout-minutes: 30 + steps: - - uses: actions/checkout@v4 - - name: Build API image - run: docker build -f docker/api/Dockerfile -t meezi-api:${{ github.ref_name }} . - - name: Build Web image - run: docker build -f docker/web/Dockerfile -t meezi-web:${{ github.ref_name }} . - - name: Deploy note + - name: Checkout latest code + uses: actions/checkout@v4 + + # Copy .env if it exists outside the repo (first deploy won't have it) + - name: Ensure .env exists run: | - echo "Push images to your registry and deploy on Arvan per DEPLOY.md" - echo "Required secrets: registry credentials, connection strings (not in repo)" + if [ ! -f .env ]; then + echo "⚠️ No .env found — copy .env.example and fill in secrets!" + cp .env.example .env + fi + + # Build only the changed service images, skip rebuild if nothing changed + - name: Build Docker images + run: | + docker compose build --parallel \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + api web website finder + env: + DOCKER_BUILDKIT: 1 + COMPOSE_DOCKER_CLI_BUILD: 1 + + # Rolling restart — database and redis are untouched if already healthy + - name: Start / restart services + run: | + docker compose up -d \ + --remove-orphans \ + --no-deps \ + postgres redis api web website finder + + # Wait for API healthcheck before declaring success + - name: Wait for API health + run: | + echo "Waiting for API to become healthy..." + for i in $(seq 1 24); do + STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-api 2>/dev/null || echo "missing") + echo " attempt $i/24 — $STATUS" + if [ "$STATUS" = "healthy" ]; then + echo "✅ API is healthy" + exit 0 + fi + sleep 5 + done + echo "❌ API did not become healthy in time" + docker compose logs --tail=50 api + exit 1 + + - name: Show running containers + if: always() + run: docker compose ps + + - name: Prune old images + if: success() + run: docker image prune -f