Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 29 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,33 @@ on:
pull_request:
branches: [ main ]

# Explicit permissions for security (CodeQL requirement)
permissions:
contents: read

jobs:
# Detect which paths changed
changes:
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
backend:
- 'backend/**'
- 'railway.json'
frontend:
- 'frontend/**'

test-backend:
name: Backend Tests
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -49,6 +73,8 @@ jobs:

test-frontend:
name: Frontend Tests
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -76,12 +102,12 @@ jobs:
security-scan:
name: Security Scan
runs-on: ubuntu-latest
continue-on-error: true # Don't fail build on security warnings
continue-on-error: true

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for TruffleHog
fetch-depth: 0

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
Expand All @@ -93,24 +119,8 @@ jobs:

- name: Check for secrets
uses: trufflesecurity/trufflehog@main
continue-on-error: true # Don't fail on false positives
continue-on-error: true
with:
path: ./
base: main
head: HEAD

lint:
name: Lint Code
runs-on: ubuntu-latest
continue-on-error: true # Don't fail build on style issues

steps:
- uses: actions/checkout@v4

- name: Lint Python
uses: py-actions/flake8@v2
continue-on-error: true
with:
path: "backend/services"
max-line-length: "120"
ignore: "E501,W503"
52 changes: 52 additions & 0 deletions backend/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Shared dependencies and service instances.
All route modules import from here to avoid circular imports.
"""
from fastapi import HTTPException, Depends
from dotenv import load_dotenv

# Load env vars first
load_dotenv()

from services.indexer_optimized import OptimizedCodeIndexer
from services.repo_manager import RepositoryManager
from services.cache import CacheService
from services.dependency_analyzer import DependencyAnalyzer
from services.style_analyzer import StyleAnalyzer
from services.performance_metrics import PerformanceMetrics
from services.rate_limiter import RateLimiter, APIKeyManager
from services.supabase_service import get_supabase_service
from services.input_validator import InputValidator, CostController

# Service instances (singleton pattern)
indexer = OptimizedCodeIndexer()
cache = CacheService()
repo_manager = RepositoryManager()
dependency_analyzer = DependencyAnalyzer()
style_analyzer = StyleAnalyzer()
metrics = PerformanceMetrics()

# Rate limiting and API key management
rate_limiter = RateLimiter(redis_client=cache.redis if cache.redis else None)
api_key_manager = APIKeyManager(get_supabase_service().client)
cost_controller = CostController(get_supabase_service().client)


def get_repo_or_404(repo_id: str, user_id: str) -> dict:
"""
Get repository with ownership verification.
Returns 404 if not found or user doesn't own it.
"""
repo = repo_manager.get_repo_for_user(repo_id, user_id)
if not repo:
raise HTTPException(status_code=404, detail="Repository not found")
return repo


def verify_repo_access(repo_id: str, user_id: str) -> None:
"""
Verify user has access to repository.
Raises 404 if no access.
"""
if not repo_manager.verify_ownership(repo_id, user_id):
raise HTTPException(status_code=404, detail="Repository not found")
Loading