From d880314defeb4e096f4beea33f21273c9b9b6431 Mon Sep 17 00:00:00 2001 From: igny8 Date: Sun, 9 Nov 2025 10:55:08 +0000 Subject: [PATCH] Add Gitea webhook endpoint for push events --- backend/igny8_core/modules/system/urls.py | 4 +- backend/igny8_core/modules/system/views.py | 78 ++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/backend/igny8_core/modules/system/urls.py b/backend/igny8_core/modules/system/urls.py index ac819959..4ba7bdb9 100644 --- a/backend/igny8_core/modules/system/urls.py +++ b/backend/igny8_core/modules/system/urls.py @@ -3,7 +3,7 @@ URL patterns for system module. """ from django.urls import path, include from rest_framework.routers import DefaultRouter -from .views import AIPromptViewSet, AuthorProfileViewSet, StrategyViewSet, system_status, get_request_metrics +from .views import AIPromptViewSet, AuthorProfileViewSet, StrategyViewSet, system_status, get_request_metrics, gitea_webhook from .integration_views import IntegrationSettingsViewSet from .settings_views import ( SystemSettingsViewSet, AccountSettingsViewSet, UserSettingsViewSet, @@ -51,6 +51,8 @@ urlpatterns = [ path('status/', system_status, name='system-status'), # Request metrics endpoint path('request-metrics//', get_request_metrics, name='request-metrics'), + # Gitea webhook endpoint + path('webhook/', gitea_webhook, name='gitea-webhook'), # Integration settings routes - exact match to reference plugin workflow # IMPORTANT: More specific paths must come BEFORE less specific ones # GET: Task progress - MUST come before other settings paths diff --git a/backend/igny8_core/modules/system/views.py b/backend/igny8_core/modules/system/views.py index 90e0bb57..95749b40 100644 --- a/backend/igny8_core/modules/system/views.py +++ b/backend/igny8_core/modules/system/views.py @@ -482,3 +482,81 @@ def get_request_metrics(request, request_id): return Response({'error': 'Metrics not found or expired'}, status=http_status.HTTP_404_NOT_FOUND) return Response(metrics) + + +@api_view(['POST']) +@permission_classes([AllowAny]) +def gitea_webhook(request): + """ + Webhook endpoint to receive push events from Gitea. + Handles automatic deployment when code is pushed to the repository. + """ + import json + import subprocess + import os + + try: + # Parse webhook payload + payload = json.loads(request.body) + event_type = request.headers.get('X-Gitea-Event', 'push') + + logger.info(f"[Webhook] Received {event_type} event from Gitea") + + # Only process push events + if event_type != 'push': + return Response({ + 'status': 'ignored', + 'message': f'Event type {event_type} is not processed' + }, status=http_status.HTTP_200_OK) + + # Extract repository information + repository = payload.get('repository', {}) + repo_name = repository.get('name', '') + repo_full_name = repository.get('full_name', '') + ref = payload.get('ref', '') + + # Only process pushes to main branch + if ref != 'refs/heads/main': + logger.info(f"[Webhook] Ignoring push to {ref}, only processing main branch") + return Response({ + 'status': 'ignored', + 'message': f'Push to {ref} ignored, only main branch is processed' + }, status=http_status.HTTP_200_OK) + + # Get commit information + commits = payload.get('commits', []) + commit_count = len(commits) + pusher = payload.get('pusher', {}).get('username', 'unknown') + + logger.info(f"[Webhook] Processing push: {commit_count} commit(s) by {pusher} to {repo_full_name}") + + # Trigger deployment + # The post-receive hook in Gitea will handle the actual deployment + # This webhook is for logging and potential additional actions + + # You can add additional deployment logic here if needed + # For example, triggering a rebuild, restarting services, etc. + + return Response({ + 'status': 'success', + 'message': 'Webhook received and processed', + 'repository': repo_full_name, + 'branch': ref, + 'commits': commit_count, + 'pusher': pusher, + 'event': event_type + }, status=http_status.HTTP_200_OK) + + except json.JSONDecodeError as e: + logger.error(f"[Webhook] Invalid JSON payload: {e}") + return Response({ + 'status': 'error', + 'message': 'Invalid JSON payload' + }, status=http_status.HTTP_400_BAD_REQUEST) + + except Exception as e: + logger.error(f"[Webhook] Error processing webhook: {e}", exc_info=True) + return Response({ + 'status': 'error', + 'message': str(e) + }, status=http_status.HTTP_500_INTERNAL_SERVER_ERROR)