GrantForge Bot
Deploy to Hugging Face
afd56bc
##
## .github/workflows/ci.yml β€” GrantForge AI CI/CD
##
## Uruchamia siΔ™ przy:
## - push na 'main' (deploy do Render)
## - PR do 'main' (testy + lint bez deploy)
##
## Kroki:
## 1. Lint (ruff) + type check (mypy)
## 2. Testy jednostkowe (pytest)
## 3. DeepEval RAG faithfulness (tylko main)
## 4. Build Docker image + push do ghcr.io
## 5. Trigger deploy na Render.com (webhook)
##
name: CI/CD β€” GrantForge AI
on:
push:
branches: [main, staging]
pull_request:
branches: [main, staging]
env:
PYTHON_VERSION: "3.11.9"
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/grantforge-api
LANGCHAIN_TRACING_V2: "true"
LANGCHAIN_PROJECT: "grantforge-production"
jobs:
# ─────────────────────────────────────────────────────────────────────────────
# JOB 1: Lint + Type Check
# ─────────────────────────────────────────────────────────────────────────────
lint:
name: πŸ” Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: backend/requirements.txt
- name: Install lint tools
run: pip install ruff mypy
- name: Ruff lint (backend)
run: python -m ruff check backend/ --select E,W,F --ignore E501,E402,W291,W293,F841,E722,E701,E712,E731,E741,F811,F401,F541
- name: Mypy type check (core modules)
run: |
cd backend
mypy core/ --ignore-missing-imports --no-strict-optional || true
# ─────────────────────────────────────────────────────────────────────────────
# JOB 2: Backend Tests
# ─────────────────────────────────────────────────────────────────────────────
test-backend:
name: πŸ§ͺ Backend Tests (pytest)
runs-on: ubuntu-latest
needs: lint
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
BIELIK_MODE: disabled
DATABASE_URL: sqlite:///./test.db
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: backend/requirements.txt
- name: Install backend dependencies
run: |
sudo apt-get update && sudo apt-get install -y libcairo2-dev pkg-config python3-dev
cd backend
pip install --upgrade pip
pip uninstall -y pinecone-plugin-inference
pip install -r requirements.txt
pip install pytest pytest-asyncio httpx
- name: Run unit tests with coverage
run: |
cd backend
python -m pytest tests/ -v --tb=short -x \
--ignore=tests/test_deepeval_rag.py \
--cov=endpoints --cov-report=term-missing --cov-fail-under=50 \
2>&1 | tail -50
- name: Check API server imports
run: |
cd backend
python -c "import server; print('βœ… server.py OK')"
# ─────────────────────────────────────────────────────────────────────────────
# JOB 3: DeepEval RAG Quality (tylko na push do main)
# ─────────────────────────────────────────────────────────────────────────────
deepeval:
name: 🎯 DeepEval RAG Faithfulness
runs-on: ubuntu-latest
needs: test-backend
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
continue-on-error: true # Nie blokuje deploy przy braku Pinecone key
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
PINECONE_API_KEY: ${{ secrets.PINECONE_API_KEY }}
PINECONE_INDEX_NAME: ${{ secrets.PINECONE_INDEX_NAME }}
LANGCHAIN_API_KEY: ${{ secrets.LANGCHAIN_API_KEY }}
LANGCHAIN_TRACING_V2: "true"
LANGCHAIN_PROJECT: grantforge-ci
BIELIK_MODE: disabled
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: backend/requirements.txt
- name: Install dependencies + DeepEval
run: |
sudo apt-get update && sudo apt-get install -y libcairo2-dev pkg-config python3-dev
cd backend
pip install --upgrade pip
pip uninstall -y pinecone-plugin-inference
pip install -r requirements.txt deepeval
- name: Run DeepEval RAG tests
run: |
cd backend
python scripts/run_eval.py || true
# 'true' β€” nie blokuje deploy przy niskim quality score (tylko raport)
# ─────────────────────────────────────────────────────────────────────────────
# JOB 4: Frontend Build Check
# ─────────────────────────────────────────────────────────────────────────────
build-frontend:
name: πŸ—οΈ Frontend Build (Vite)
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: |
cd frontend-react
rm -rf node_modules package-lock.json
npm install
- name: TypeScript check
run: |
cd frontend-react
npx tsc --noEmit || true
- name: Build
run: |
cd frontend-react
npm run build
env:
VITE_CLERK_PUBLISHABLE_KEY: ${{ secrets.VITE_CLERK_PUBLISHABLE_KEY }}
VITE_STRIPE_PRICE_ID_PRO: ${{ secrets.VITE_STRIPE_PRICE_ID_PRO }}
VITE_API_URL: "/api"
VITE_APP_VERSION: "1.3.0"
# ─────────────────────────────────────────────────────────────────────────────
# JOB 5: Docker Build + Push (tylko main)
# ─────────────────────────────────────────────────────────────────────────────
docker:
name: 🐳 Docker Build & Push
runs-on: ubuntu-latest
needs: [test-backend, build-frontend]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=sha-,format=short
type=raw,value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ─────────────────────────────────────────────────────────────────────────────
# JOB 6: Deploy do Hugging Face Spaces (tylko main)
# ─────────────────────────────────────────────────────────────────────────────
deploy-huggingface:
name: πŸš€ Deploy to Hugging Face
runs-on: ubuntu-latest
needs: [docker] # deepeval jest continue-on-error, nie blokuje deploy
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push to Hugging Face Spaces
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
git config --global user.email "bot@grantforge.ai"
git config --global user.name "GrantForge Bot"
# Usuwamy starą historię, aby pozbyć się starych blobów binarnych
rm -rf .git
git init -b main
# Konfigurujemy Git LFS dla plikΓ³w binarnych (wymΓ³g Hugging Face)
git lfs install
echo "*.ttf filter=lfs diff=lfs merge=lfs -text" > .gitattributes
echo "*.png filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.jpg filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.ico filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.pdf filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
# Tworzymy nowy, czysty commit z caΕ‚Δ… aplikacjΔ…
git add .gitattributes
git add .
git commit -m "Deploy to Hugging Face"
# Wypychamy na HF (wypchnie rΓ³wnieΕΌ obiekty LFS)
git remote add huggingface https://Bogdan555:${HF_TOKEN}@huggingface.co/spaces/Bogdan555/grantforge-api
git push -f huggingface main