name: Deploy on: push: branches: [main] workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Log in to GHCR uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 with: context: . push: true no-cache: true tags: ghcr.io/singi-labs/sifa-web:latest,ghcr.io/singi-labs/sifa-web:${{ github.sha }} - name: Deploy to VPS if: env.VPS_HOST != '' env: VPS_HOST: ${{ secrets.VPS_HOST }} uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1 with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} script: | cd /opt/sifa docker compose pull sifa-web docker compose up -d --force-recreate --no-deps sifa-web docker image prune -af - name: Verify deployment health if: env.VPS_HOST != '' env: VPS_HOST: ${{ secrets.VPS_HOST }} uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1 with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} script: | # Wait for web container to be reachable for i in $(seq 1 10); do status=$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/ 2>/dev/null || echo "000") if [ "$status" = "200" ]; then echo "Web healthy after $((i * 3))s" break fi echo "Waiting for web... (attempt $i, status: $status)" sleep 3 done if [ "$status" != "200" ]; then echo "FATAL: Web did not become healthy within 30s" exit 1 fi # Verify API is reachable from web's perspective api_status=$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:3100/api/health/ready 2>/dev/null || echo "000") if [ "$api_status" != "200" ]; then echo "WARNING: API readiness check failed (status: $api_status)" exit 1 fi echo "API readiness confirmed" # Warm ISR cache for heavy pages curl -sf -o /dev/null http://127.0.0.1:3000/events/atmosphereconf-2026 || true