Spaces:
Running
Running
| ## | |
| ## .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 | |