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: api-build: name: "CI · 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 dependencies run: dotnet restore - name: Build (Release) run: dotnet build --no-restore -c Release - name: Run tests run: dotnet test --no-build -c Release --logger "console;verbosity=minimal" # ── Dashboard typecheck ──────────────────────────────────────────────────── 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" - name: Install dependencies run: npm install --legacy-peer-deps --ignore-scripts - name: TypeScript check run: npx tsc --noEmit env: NEXT_PUBLIC_API_URL: http://localhost:5080 # ── Finder typecheck ─────────────────────────────────────────────────────── 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" - name: Install dependencies run: npm install --legacy-peer-deps --ignore-scripts - name: TypeScript check 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 · docker compose" runs-on: self-hosted needs: [api-build, dashboard-check, finder-check] # Skip deploy on PRs — only run when pushing directly to main if: github.event_name == 'push' && github.ref == 'refs/heads/main' timeout-minutes: 30 steps: - uses: actions/checkout@v4 # Write .env from a single Gitea secret called ENV_FILE # How to set it: Gitea repo → Settings → Secrets → Add secret # Name: ENV_FILE # Value: paste your entire .env file content - name: Write .env run: printf '%s' "$ENV_FILE" > .env env: ENV_FILE: ${{ secrets.ENV_FILE }} # Build all service images in parallel using BuildKit - name: Build Docker images run: | docker compose build --parallel api web website finder env: DOCKER_BUILDKIT: 1 COMPOSE_DOCKER_CLI_BUILD: 1 # Rolling restart — postgres/redis stay untouched if already healthy - name: Start / restart services run: | docker compose up -d \ --remove-orphans \ --no-deps \ postgres redis api web website finder # Poll until API container reports healthy (max 2 min) - name: Wait for API to become healthy run: | echo "Waiting for meezi-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 " [$i/24] $STATUS" [ "$STATUS" = "healthy" ] && echo "✅ Healthy" && exit 0 sleep 5 done echo "❌ API did not become healthy — last 50 log lines:" docker compose logs --tail=50 api exit 1 - name: Show running containers if: always() run: docker compose ps # Remove dangling images to keep disk clean - name: Prune old images if: success() run: docker image prune -f