mirror of
https://github.com/opsre/LiteOps.git
synced 2026-02-20 14:01:01 +08:00
229 lines
8.8 KiB
Python
229 lines
8.8 KiB
Python
import json
|
||
import logging
|
||
from django.http import JsonResponse
|
||
from django.views import View
|
||
from django.utils.decorators import method_decorator
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from ..models import BuildTask, BuildHistory, User
|
||
from ..utils.builder import Builder
|
||
import threading
|
||
|
||
logger = logging.getLogger('apps')
|
||
|
||
def execute_auto_build(task, branch, commit_id, commit_message, commit_author):
|
||
"""执行自动构建任务"""
|
||
try:
|
||
# 生成构建号
|
||
from django.db.models import F
|
||
from ..views.build import generate_id
|
||
|
||
# 更新任务构建号并获取新的构建号
|
||
task.last_build_number = F('last_build_number') + 1
|
||
task.total_builds = F('total_builds') + 1
|
||
task.building_status = 'building'
|
||
task.save()
|
||
|
||
# 重新获取任务以获取更新后的构建号
|
||
task.refresh_from_db()
|
||
build_number = task.last_build_number
|
||
|
||
# 创建构建历史记录
|
||
history = BuildHistory.objects.create(
|
||
history_id=generate_id(),
|
||
task=task,
|
||
build_number=build_number,
|
||
branch=branch,
|
||
commit_id=commit_id,
|
||
status='pending',
|
||
requirement=f"自动构建: {commit_message[:200]} (by {commit_author})", # 使用提交信息作为构建需求
|
||
parameter_values=get_default_parameter_values(task.parameters), # 使用默认参数值
|
||
operator=None # 自动构建没有操作人
|
||
)
|
||
|
||
builder = Builder(task, build_number, commit_id, history)
|
||
builder.execute()
|
||
|
||
except Exception as e:
|
||
logger.error(f"自动构建执行失败: {str(e)}", exc_info=True)
|
||
finally:
|
||
# 无论构建成功、失败或异常,都将构建状态重置为空闲
|
||
from django.db import transaction
|
||
with transaction.atomic():
|
||
BuildTask.objects.filter(task_id=task.task_id).update(building_status='idle')
|
||
logger.info(f"任务 [{task.task_id}] 自动构建状态已重置为空闲")
|
||
|
||
def get_default_parameter_values(parameters):
|
||
"""获取参数的默认值"""
|
||
if not parameters:
|
||
return {}
|
||
|
||
default_values = {}
|
||
for param in parameters:
|
||
param_name = param.get('name')
|
||
default_list = param.get('default_values', [])
|
||
if param_name and default_list:
|
||
default_values[param_name] = default_list
|
||
|
||
return default_values
|
||
|
||
def is_branch_matched(branch_name, branch_list):
|
||
"""检查分支是否在配置的分支列表中"""
|
||
return branch_name in branch_list
|
||
|
||
@method_decorator(csrf_exempt, name='dispatch')
|
||
class GitLabWebhookView(View):
|
||
"""GitLab Webhook处理视图"""
|
||
|
||
def post(self, request, task_id):
|
||
"""处理GitLab Push Events"""
|
||
try:
|
||
# 验证token
|
||
token = request.GET.get('token')
|
||
if not token:
|
||
logger.warning(f"Webhook请求缺少token: task_id={task_id}")
|
||
return JsonResponse({
|
||
'error': 'Missing token'
|
||
}, status=401)
|
||
|
||
# 查找对应的构建任务
|
||
try:
|
||
task = BuildTask.objects.get(task_id=task_id, webhook_token=token)
|
||
except BuildTask.DoesNotExist:
|
||
logger.warning(f"Webhook token验证失败: task_id={task_id}, token={token}")
|
||
return JsonResponse({
|
||
'error': 'Invalid task or token'
|
||
}, status=404)
|
||
|
||
# 检查任务是否启用自动构建
|
||
if not task.auto_build_enabled:
|
||
logger.info(f"任务[{task_id}]未启用自动构建,忽略webhook")
|
||
return JsonResponse({
|
||
'message': 'Auto build is not enabled for this task'
|
||
})
|
||
|
||
# 检查任务状态
|
||
if task.status == 'disabled':
|
||
logger.info(f"任务[{task_id}]已禁用,忽略webhook")
|
||
return JsonResponse({
|
||
'message': 'Task is disabled'
|
||
})
|
||
|
||
# 是否有正在进行的构建
|
||
if task.building_status == 'building':
|
||
logger.info(f"任务[{task_id}]正在构建中,忽略webhook")
|
||
return JsonResponse({
|
||
'message': 'Build is already in progress'
|
||
})
|
||
|
||
# 解析webhook数据
|
||
try:
|
||
webhook_data = json.loads(request.body)
|
||
except json.JSONDecodeError:
|
||
logger.error(f"Webhook数据解析失败: task_id={task_id}")
|
||
return JsonResponse({
|
||
'error': 'Invalid JSON data'
|
||
}, status=400)
|
||
|
||
# 是否是push事件
|
||
event_name = request.headers.get('X-Gitlab-Event', '')
|
||
if event_name != 'Push Hook':
|
||
logger.info(f"忽略非Push事件: {event_name}, task_id={task_id}")
|
||
return JsonResponse({
|
||
'message': f'Ignored event: {event_name}'
|
||
})
|
||
|
||
# 提取分支信息
|
||
ref = webhook_data.get('ref', '')
|
||
if not ref.startswith('refs/heads/'):
|
||
logger.info(f"忽略非分支推送: {ref}, task_id={task_id}")
|
||
return JsonResponse({
|
||
'message': f'Ignored non-branch push: {ref}'
|
||
})
|
||
|
||
branch = ref.replace('refs/heads/', '')
|
||
|
||
# 检查分支是否在自动构建配置中
|
||
if not is_branch_matched(branch, task.auto_build_branches):
|
||
logger.info(f"分支[{branch}]不在自动构建配置中,忽略webhook: task_id={task_id}")
|
||
return JsonResponse({
|
||
'message': f'Branch {branch} is not configured for auto build'
|
||
})
|
||
|
||
# 提取提交信息
|
||
commits = webhook_data.get('commits', [])
|
||
if not commits:
|
||
logger.warning(f"Webhook数据中没有提交信息: task_id={task_id}")
|
||
return JsonResponse({
|
||
'error': 'No commits found in webhook data'
|
||
}, status=400)
|
||
|
||
# 使用最新的提交
|
||
latest_commit = commits[-1]
|
||
commit_id = latest_commit.get('id', '')
|
||
commit_message = latest_commit.get('message', '').strip()
|
||
commit_author = latest_commit.get('author', {}).get('name', 'Unknown')
|
||
|
||
if not commit_id:
|
||
logger.error(f"提交ID为空: task_id={task_id}")
|
||
return JsonResponse({
|
||
'error': 'Commit ID is empty'
|
||
}, status=400)
|
||
|
||
env_type = task.environment.type if task.environment else None
|
||
if env_type not in ['development', 'testing']:
|
||
logger.warning(f"环境类型[{env_type}]不支持自动构建: task_id={task_id}")
|
||
return JsonResponse({
|
||
'message': f'Environment type {env_type} does not support auto build'
|
||
})
|
||
|
||
logger.info(f"触发自动构建: task_id={task_id}, branch={branch}, commit={commit_id[:8]}, author={commit_author}")
|
||
|
||
# 在新线程中执行自动构建
|
||
build_thread = threading.Thread(
|
||
target=execute_auto_build,
|
||
args=(task, branch, commit_id, commit_message, commit_author)
|
||
)
|
||
build_thread.start()
|
||
|
||
return JsonResponse({
|
||
'message': 'Auto build triggered successfully',
|
||
'task_id': task_id,
|
||
'branch': branch,
|
||
'commit_id': commit_id[:8],
|
||
'commit_message': commit_message[:100]
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"Webhook处理失败: {str(e)}", exc_info=True)
|
||
return JsonResponse({
|
||
'error': f'Internal server error: {str(e)}'
|
||
}, status=500)
|
||
|
||
def get(self, request, task_id):
|
||
"""用于测试webhook配置"""
|
||
try:
|
||
token = request.GET.get('token')
|
||
if not token:
|
||
return JsonResponse({
|
||
'error': 'Missing token'
|
||
}, status=401)
|
||
|
||
try:
|
||
task = BuildTask.objects.get(task_id=task_id, webhook_token=token)
|
||
except BuildTask.DoesNotExist:
|
||
return JsonResponse({
|
||
'error': 'Invalid task or token'
|
||
}, status=404)
|
||
|
||
return JsonResponse({
|
||
'message': 'Webhook configuration is valid',
|
||
'task_name': task.name,
|
||
'auto_build_enabled': task.auto_build_enabled,
|
||
'auto_build_branches': task.auto_build_branches
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"Webhook测试失败: {str(e)}", exc_info=True)
|
||
return JsonResponse({
|
||
'error': f'Internal server error: {str(e)}'
|
||
}, status=500) |