name: CI/CD on: push: branches: [main] pull_request: branches: [main] # Only one deploy at a time; a newer push cancels an in-progress one concurrency: group: meezi-cicd-${{ github.ref }} cancel-in-progress: true # ───────────────────────────────────────────────────────────────────────────── # CI — runs on every push AND every PR # ───────────────────────────────────────────────────────────────────────────── jobs: # ── Main API ──────────────────────────────────────────────────────────────── api-build: name: "CI · API (dotnet build + test)" runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Setup .NET 10 uses: actions/setup-dotnet@v4 with: dotnet-version: "10.0.x" - name: Restore run: dotnet restore src/Meezi.API/Meezi.API.csproj - name: Build run: dotnet build src/Meezi.API/Meezi.API.csproj --no-restore -c Release - name: Test run: dotnet test --no-build -c Release --logger "console;verbosity=minimal" # ── Admin API ─────────────────────────────────────────────────────────────── admin-api-build: name: "CI · Admin API (dotnet build)" runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Setup .NET 10 uses: actions/setup-dotnet@v4 with: dotnet-version: "10.0.x" - name: Restore run: dotnet restore src/Meezi.Admin.API/Meezi.Admin.API.csproj - name: Build run: dotnet build src/Meezi.Admin.API/Meezi.Admin.API.csproj --no-restore -c Release # ── Dashboard ─────────────────────────────────────────────────────────────── dashboard-check: name: "CI · Dashboard (tsc)" runs-on: self-hosted defaults: run: working-directory: web/dashboard steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - run: npm install --legacy-peer-deps --ignore-scripts - run: npx tsc --noEmit env: NEXT_PUBLIC_API_URL: http://localhost:5080 # ── Admin Web ─────────────────────────────────────────────────────────────── admin-web-check: name: "CI · Admin Web (tsc)" runs-on: self-hosted defaults: run: working-directory: web/admin steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - run: npm install --legacy-peer-deps --ignore-scripts - run: npx tsc --noEmit env: NEXT_PUBLIC_ADMIN_API_URL: http://localhost:5081 # ── Website ───────────────────────────────────────────────────────────────── website-check: name: "CI · Website (tsc)" runs-on: self-hosted defaults: run: working-directory: web/website steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - run: npm install --legacy-peer-deps --ignore-scripts - run: npx tsc --noEmit env: MEEZI_API_URL: http://localhost:5080 # ── Finder ────────────────────────────────────────────────────────────────── finder-check: name: "CI · Finder (tsc)" runs-on: self-hosted defaults: run: working-directory: web/finder steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - run: npm install --legacy-peer-deps --ignore-scripts - run: npx tsc --noEmit env: NEXT_PUBLIC_API_URL: http://localhost:5080 # ───────────────────────────────────────────────────────────────────────────── # DEPLOY — only on push to main, only if ALL CI jobs pass # ───────────────────────────────────────────────────────────────────────────── deploy: name: "Deploy · all services" runs-on: self-hosted needs: - api-build - admin-api-build - dashboard-check - admin-web-check - website-check - finder-check if: github.event_name == 'push' && github.ref == 'refs/heads/main' timeout-minutes: 40 steps: - uses: actions/checkout@v4 # Write .env from Gitea secret # Set it at: Gitea repo → Settings → Secrets → Actions → Add Secret # Name: ENV_FILE # Value: your full .env file content - name: Write .env run: printf '%s' "$ENV_FILE" > .env env: ENV_FILE: ${{ secrets.ENV_FILE }} # ── Build main services ───────────────────────────────────────────────── - name: Build main images (api, web, website, finder) run: | docker compose build --parallel api web website finder env: DOCKER_BUILDKIT: 1 COMPOSE_DOCKER_CLI_BUILD: 1 # ── Build admin services (separate compose file) ──────────────────────── - name: Build admin images (admin-api, admin-web) run: | docker compose \ -f docker-compose.yml \ -f docker-compose.admin.yml \ build --parallel admin-api admin-web env: DOCKER_BUILDKIT: 1 COMPOSE_DOCKER_CLI_BUILD: 1 # ── Start / restart main services ─────────────────────────────────────── - name: Start main services run: | docker compose up -d \ --remove-orphans \ --no-deps \ postgres redis api web website finder # ── Start / restart admin services ────────────────────────────────────── - name: Start admin services run: | docker compose \ -f docker-compose.yml \ -f docker-compose.admin.yml \ up -d \ --no-deps \ admin-api admin-web # ── Health checks ──────────────────────────────────────────────────────── - name: Wait for main API healthy run: | echo "Waiting for meezi-api..." for i in $(seq 1 24); do STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-api 2>/dev/null || echo "missing") echo " [$i/24] $STATUS" [ "$STATUS" = "healthy" ] && echo "✅ meezi-api healthy" && break [ "$i" = "24" ] && echo "❌ meezi-api timeout" && docker compose logs --tail=40 api && exit 1 sleep 5 done - name: Wait for admin API healthy run: | echo "Waiting for meezi-admin-api..." for i in $(seq 1 24); do STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-admin-api 2>/dev/null || echo "missing") echo " [$i/24] $STATUS" [ "$STATUS" = "healthy" ] && echo "✅ meezi-admin-api healthy" && break [ "$i" = "24" ] && echo "❌ meezi-admin-api timeout" && docker compose -f docker-compose.yml -f docker-compose.admin.yml logs --tail=40 admin-api && exit 1 sleep 5 done - name: Show all running containers if: always() run: docker compose -f docker-compose.yml -f docker-compose.admin.yml ps - name: Prune old images if: success() run: docker image prune -f