149 lines
5.3 KiB
Python
149 lines
5.3 KiB
Python
import re
|
||
import os
|
||
import sys
|
||
import logging
|
||
import requests
|
||
from flask import Flask, request, jsonify
|
||
|
||
# Настройка логирования
|
||
logging.basicConfig(level=logging.INFO, format='%(message)s', handlers=[logging.StreamHandler(sys.stdout)])
|
||
logger = logging.getLogger(__name__)
|
||
|
||
app = Flask(__name__)
|
||
|
||
# Конфигурация
|
||
GITEA_URL = os.getenv('GITEA_URL', 'https://gitea-copy.teamlead.online')
|
||
GITEA_TOKEN = os.getenv('GITEA_TOKEN', '68dbda863bb4c51cb3ef1f8c4e8a69f7221240f5')
|
||
|
||
# Поиск задач вида ABC-123
|
||
TASK_PATTERN = re.compile(r'([A-Z]+-\d+)', re.IGNORECASE)
|
||
|
||
def find_tasks(text):
|
||
"""Находит ключи задач в тексте"""
|
||
return set(TASK_PATTERN.findall(text or ''))
|
||
|
||
def get_compare(owner, repo, base, head):
|
||
"""Запрашивает сравнение двух коммитов через API"""
|
||
url = f"{GITEA_URL}/api/v1/repos/{owner}/{repo}/compare/{base}...{head}"
|
||
headers = {'Authorization': f'token {GITEA_TOKEN}'} if GITEA_TOKEN else {}
|
||
|
||
try:
|
||
resp = requests.get(url, headers=headers)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
logger.info(f"[API] Found {data.get('total_commits', 0)} commits")
|
||
return data.get('commits', [])
|
||
except Exception as e:
|
||
logger.info(f"[API] Error: {e}")
|
||
return None
|
||
|
||
@app.route('/webhook', methods=['POST'])
|
||
def webhook():
|
||
event = request.headers.get('X-Gitea-Event')
|
||
logger.info(f"\n{'='*70}\n[WEBHOOK] Event: {event}")
|
||
|
||
if event not in ['push', 'delete']:
|
||
return jsonify({'status': 'ignored'}), 200
|
||
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({'error': 'No data'}), 400
|
||
|
||
ref = data.get('ref', '')
|
||
logger.info(f"[WEBHOOK] Ref: {ref}")
|
||
|
||
# Удаление ветки
|
||
if event == 'delete':
|
||
branch = ref.replace('refs/heads/', '')
|
||
logger.info(f"[BRANCH DELETED] {branch}")
|
||
tasks = find_tasks(branch)
|
||
if tasks:
|
||
logger.info(f" [TASKS IN DELETED BRANCH] {', '.join(tasks)}")
|
||
return jsonify({'status': 'deleted'}), 200
|
||
|
||
# Только ветки
|
||
if not ref.startswith('refs/heads/'):
|
||
return jsonify({'status': 'ignored'}), 200
|
||
|
||
branch = ref.replace('refs/heads/', '')
|
||
before, after = data.get('before'), data.get('after')
|
||
repo = data.get('repository', {})
|
||
owner = repo.get('owner', {}).get('username')
|
||
repo_name = repo.get('name')
|
||
|
||
logger.info(f"[PUSH] {owner}/{repo_name}:{branch}")
|
||
logger.info(f" before: {before[:8]}, after: {after[:8]}")
|
||
|
||
# Создание ветки
|
||
if before == '0'*40:
|
||
logger.info(f"[BRANCH CREATED] {branch}")
|
||
tasks = find_tasks(branch)
|
||
if tasks:
|
||
logger.info(f" [TASKS IN BRANCH] {', '.join(tasks)}")
|
||
process_commits(data.get('commits', []), branch)
|
||
return jsonify({'status': 'created'}), 200
|
||
|
||
# Удаление ветки (через push с after=000)
|
||
if after == '0'*40:
|
||
logger.info(f"[BRANCH DELETED] {branch}")
|
||
return jsonify({'status': 'deleted'}), 200
|
||
|
||
# Обычный push - ищем новые и удаленные коммиты
|
||
logger.info("-"*70)
|
||
|
||
# Новые коммиты
|
||
new_commits = get_compare(owner, repo_name, before, after)
|
||
if new_commits:
|
||
logger.info(f"[NEW] {len(new_commits)} commits:")
|
||
for c in new_commits[:10]:
|
||
sha = c.get('sha', '')[:8]
|
||
msg = c.get('commit', {}).get('message', '')[:50]
|
||
logger.info(f" + {sha}: {msg}")
|
||
|
||
# Удаленные коммиты
|
||
removed_commits = get_compare(owner, repo_name, after, before)
|
||
if removed_commits:
|
||
logger.info(f"[REMOVED] {len(removed_commits)} commits:")
|
||
for c in removed_commits[:10]:
|
||
sha = c.get('sha', '')[:8]
|
||
msg = c.get('commit', {}).get('message', '')[:50]
|
||
tasks = find_tasks(msg)
|
||
task_info = f" [TASKS: {', '.join(tasks)}]" if tasks else ""
|
||
logger.info(f" - {sha}: {msg}{task_info}")
|
||
|
||
return jsonify({'status': 'ok'}), 200
|
||
|
||
def process_commits(commits, branch):
|
||
"""Обрабатывает коммиты и ищет задачи"""
|
||
if not commits:
|
||
return
|
||
|
||
logger.info(f"\n[COMMITS] {len(commits)} commits:")
|
||
all_tasks = find_tasks(branch)
|
||
|
||
for i, c in enumerate(commits, 1):
|
||
sha = c.get('id', '')[:8]
|
||
msg = c.get('message', '')
|
||
tasks = find_tasks(msg)
|
||
all_tasks.update(tasks)
|
||
logger.info(f" {i}. {sha}: {msg[:60]}" + (f" [TASKS: {', '.join(tasks)}]" if tasks else ""))
|
||
|
||
if all_tasks:
|
||
logger.info(f"\n[SUMMARY] Found tasks: {', '.join(sorted(all_tasks))}")
|
||
|
||
@app.route('/', methods=['GET'])
|
||
def index():
|
||
return f"""
|
||
<h1>Gitea Webhook Handler</h1>
|
||
<p>Webhook URL: <code>/webhook</code></p>
|
||
<p>Gitea API: <code>{GITEA_URL}</code></p>
|
||
<p>Token: {'[SET]' if GITEA_TOKEN else '[NOT SET]'}</p>
|
||
"""
|
||
|
||
if __name__ == '__main__':
|
||
logger.info(f"\n{'='*70}\nGITEA WEBHOOK HANDLER\n{'='*70}")
|
||
logger.info(f"Server: http://0.0.0.0:5000")
|
||
logger.info(f"Webhook: https://gitea-copy.teamlead.online:5000/webhook")
|
||
logger.info(f"Gitea API: {GITEA_URL}")
|
||
logger.info(f"{'='*70}\n")
|
||
app.run(host='0.0.0.0', port=5000, debug=False) |