name: CI / Lint + Deploy on: push: branches: ["main", "master"] pull_request: branches: ["main", "master"] workflow_dispatch: jobs: # ───────────────────────────────────────────────────────────────────────────── # 1. CI — syntax + import smoke test # ───────────────────────────────────────────────────────────────────────────── ci: name: Syntax & Import Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip # Install CPU-only torch to keep CI fast pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install -r requirements.txt - name: Compile-check all Python modules run: | python -m py_compile \ scripts/train_chestmnist.py \ backend/main.py \ frontend/app.py \ model_utils.py - name: Import smoke test (no model files needed) run: | python - <<'PY' import ast, pathlib for f in ["scripts/train_chestmnist.py", "backend/main.py", "frontend/app.py", "model_utils.py"]: ast.parse(pathlib.Path(f).read_text()) print(f" {f} OK") print("All imports parseable.") PY # ───────────────────────────────────────────────────────────────────────────── # 2. Deploy — push code to Hugging Face Space (on main branch only) # ───────────────────────────────────────────────────────────────────────────── deploy: name: Deploy to Hugging Face Spaces if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') && github.event_name == 'push' needs: ci runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install huggingface_hub run: pip install huggingface_hub - name: Push Space to Hugging Face Hub env: HF_TOKEN: ${{ secrets.HF_TOKEN }} HF_SPACE_REPO: ${{ secrets.HF_SPACE_REPO }} GITHUB_SHA: ${{ github.sha }} run: | python - <<'PY' import os from huggingface_hub import HfApi token = os.environ["HF_TOKEN"] space_repo = os.environ["HF_SPACE_REPO"] # e.g. "your-username/pneumoops" sha_tag = f"build-{os.environ['GITHUB_SHA'][:7]}" api = HfApi(token=token) api.create_repo(repo_id=space_repo, repo_type="space", space_sdk="docker", exist_ok=True) api.upload_folder( repo_id=space_repo, repo_type="space", folder_path=".", commit_message=f"Deploy {sha_tag}", ignore_patterns=[ ".git/*", "__pycache__/*", "*.pyc", ".venv/*", "venv/*", "data/hf_cache/*", ], ) try: api.create_tag(repo_id=space_repo, repo_type="space", tag=sha_tag) except Exception: pass # tag already exists print(f"Deployed {sha_tag} → https://huggingface.co/spaces/{space_repo}") PY - name: Upload model artifacts to HF Model Hub # Only runs if HF_MODEL_REPO secret is set if: ${{ env.HF_MODEL_REPO != '' }} env: HF_TOKEN: ${{ secrets.HF_TOKEN }} HF_MODEL_REPO: ${{ secrets.HF_MODEL_REPO }} GITHUB_SHA: ${{ github.sha }} run: | python - <<'PY' import os from huggingface_hub import HfApi from pathlib import Path token = os.environ["HF_TOKEN"] model_repo = os.environ["HF_MODEL_REPO"] sha_tag = f"build-{os.environ['GITHUB_SHA'][:7]}" api = HfApi(token=token) api.create_repo(repo_id=model_repo, repo_type="model", exist_ok=True) model_dir = Path("models/chestmnist_mobilenetv3") if model_dir.exists(): api.upload_folder( repo_id=model_repo, repo_type="model", folder_path=str(model_dir), commit_message=f"Upload model {sha_tag}", ) print(f"Model artifacts uploaded → https://huggingface.co/models/{model_repo}") PY # ───────────────────────────────────────────────────────────────────────────── # 3. Deploy — push backend+frontend to Railway (on main branch only) # ───────────────────────────────────────────────────────────────────────────── deploy-railway: name: Deploy to Railway if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') && github.event_name == 'push' needs: ci runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Railway CLI run: npm install -g @railway/cli - name: Deploy to Railway env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} run: railway up --service pneumoops-backend --detach