Dev #28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| # Triggers: | |
| # - every push to any branch (so feature branches get fast feedback) | |
| # - every PR targeting main or dev (gate for merge) | |
| on: | |
| push: | |
| branches: ["**"] | |
| pull_request: | |
| branches: [main, dev] | |
| # Avoid wasting CI minutes: cancel in-progress runs when a new commit lands on the same branch. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ──────────────────────────────────────────────────────────── | |
| # 1. Lint — fast, fails the rest if style is broken | |
| # ──────────────────────────────────────────────────────────── | |
| lint: | |
| name: Lint (ruff) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Install ruff | |
| run: pip install ruff==0.2.0 | |
| - name: Run ruff | |
| run: ruff check . | |
| # ──────────────────────────────────────────────────────────── | |
| # 2. Tests — full pytest suite with coverage | |
| # ──────────────────────────────────────────────────────────── | |
| test: | |
| name: Tests (pytest) | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Install CPU-only PyTorch | |
| # Doing this separately keeps the image small and avoids CUDA wheels. | |
| run: | | |
| pip install --upgrade pip | |
| pip install torch==2.2.0 --index-url https://download.pytorch.org/whl/cpu | |
| - name: Install project dependencies | |
| run: pip install -r requirements.txt | |
| - name: Prepare data | |
| # data_prep.py validates scenario CSVs and writes derived features. | |
| # Many tests depend on its outputs being present. `all` processes | |
| # weekday, weekend, and holiday_rush in one go. | |
| run: python data_prep.py --scenario all | |
| - name: Run pytest with coverage | |
| run: | | |
| pytest tests/ \ | |
| --cov=. \ | |
| --cov-report=term-missing \ | |
| --cov-report=xml \ | |
| --junitxml=pytest-results.xml \ | |
| -v | |
| - name: Upload coverage report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: coverage.xml | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: pytest-results | |
| path: pytest-results.xml | |
| # ──────────────────────────────────────────────────────────── | |
| # 3. Build Docker images — verifies the Dockerfiles actually build | |
| # ──────────────────────────────────────────────────────────── | |
| build-images: | |
| name: Build Docker images | |
| runs-on: ubuntu-latest | |
| needs: test | |
| # Only on push to main/dev — saves CI time on feature branches. | |
| if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build training image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.train | |
| push: false | |
| tags: food-rescue-train:ci-${{ github.sha }} | |
| cache-from: type=gha,scope=train | |
| cache-to: type=gha,scope=train,mode=max | |
| - name: Build serving image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.serve | |
| push: false | |
| tags: food-rescue-serve:ci-${{ github.sha }} | |
| cache-from: type=gha,scope=serve | |
| cache-to: type=gha,scope=serve,mode=max | |
| # ──────────────────────────────────────────────────────────── | |
| # 4. Smoke test the serving image — runs /health | |
| # ──────────────────────────────────────────────────────────── | |
| smoke-test-serve: | |
| name: Smoke test serving image | |
| runs-on: ubuntu-latest | |
| needs: build-images | |
| if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build serving image (load locally) | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.serve | |
| load: true | |
| tags: food-rescue-serve:smoke | |
| cache-from: type=gha,scope=serve | |
| - name: Run container and check /health | |
| run: | | |
| # Start the API in the background; it will boot in degraded mode | |
| # since no policy is mounted and MLflow is not available in CI. | |
| # FOOD_RESCUE_DISABLE_MLFLOW_REGISTRY=1 prevents hanging on MLflow connection. | |
| docker run -d --name api-smoke \ | |
| -e FOOD_RESCUE_DISABLE_MLFLOW_REGISTRY=1 \ | |
| -p 8000:8000 food-rescue-serve:smoke | |
| # Give uvicorn ~10s to come up | |
| for i in $(seq 1 30); do | |
| if curl -fsS http://localhost:8000/health > /dev/null; then | |
| echo "API is up after ${i}s" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| curl -fsS http://localhost:8000/health | |
| docker logs api-smoke | |
| docker stop api-smoke |