- docker-compose.yml with all services - .env.example with placeholder secrets - Landing page HTML - Gitea→OpenProject webhook script - Comprehensive README with architecture docs
99 lines
3.2 KiB
Python
Executable File
99 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Gitea → OpenProject Webhook Handler
|
|
Parses commit messages for WP-XXX patterns and adds comments to Work Packages
|
|
"""
|
|
import os
|
|
import re
|
|
import json
|
|
import base64
|
|
import urllib.request
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
|
|
OPENPROJECT_URL = os.getenv("OPENPROJECT_URL", "https://project.toppyr.de")
|
|
OPENPROJECT_API_KEY = os.getenv("OPENPROJECT_API_KEY", "")
|
|
GITEA_URL = os.getenv("GITEA_URL", "https://git.toppyr.de")
|
|
WP_PATTERN = re.compile(r"WP-(\d+)", re.IGNORECASE)
|
|
|
|
class WebhookHandler(BaseHTTPRequestHandler):
|
|
def do_POST(self):
|
|
content_length = int(self.headers.get("Content-Length", 0))
|
|
body = self.rfile.read(content_length)
|
|
|
|
try:
|
|
payload = json.loads(body)
|
|
self.process_push(payload)
|
|
self.send_response(200)
|
|
self.end_headers()
|
|
self.wfile.write(b"OK")
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
self.send_response(500)
|
|
self.end_headers()
|
|
self.wfile.write(str(e).encode())
|
|
|
|
def process_push(self, payload):
|
|
repo_name = payload.get("repository", {}).get("full_name", "unknown")
|
|
commits = payload.get("commits", [])
|
|
|
|
for commit in commits:
|
|
message = commit.get("message", "")
|
|
sha = commit.get("id", "")[:8]
|
|
author = commit.get("author", {}).get("name", "Unknown")
|
|
url = commit.get("url", "")
|
|
|
|
# Find all WP-XXX references
|
|
matches = WP_PATTERN.findall(message)
|
|
for wp_id in matches:
|
|
self.add_openproject_comment(
|
|
wp_id=wp_id,
|
|
repo=repo_name,
|
|
sha=sha,
|
|
message=message,
|
|
author=author,
|
|
url=url
|
|
)
|
|
|
|
def add_openproject_comment(self, wp_id, repo, sha, message, author, url):
|
|
if not OPENPROJECT_API_KEY:
|
|
print(f"No API key - would comment on WP-{wp_id}")
|
|
return
|
|
|
|
comment = f"""**Git Commit** [{sha}]({url})
|
|
**Repository:** {repo}
|
|
**Author:** {author}
|
|
|
|
```
|
|
{message}
|
|
```
|
|
"""
|
|
api_url = f"{OPENPROJECT_URL}/api/v3/work_packages/{wp_id}/activities"
|
|
data = json.dumps({"comment": {"raw": comment}}).encode()
|
|
|
|
# Basic auth with apikey as username
|
|
auth = base64.b64encode(f"apikey:{OPENPROJECT_API_KEY}".encode()).decode()
|
|
|
|
req = urllib.request.Request(
|
|
api_url,
|
|
data=data,
|
|
headers={
|
|
"Content-Type": "application/json",
|
|
"Authorization": f"Basic {auth}"
|
|
},
|
|
method="POST"
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req) as resp:
|
|
print(f"Added comment to WP-{wp_id}: {resp.status}")
|
|
except Exception as e:
|
|
print(f"Failed to comment on WP-{wp_id}: {e}")
|
|
|
|
def log_message(self, format, *args):
|
|
print(f"[Webhook] {args[0]}")
|
|
|
|
if __name__ == "__main__":
|
|
port = int(os.getenv("PORT", 9000))
|
|
print(f"Starting webhook handler on port {port}")
|
|
HTTPServer(("0.0.0.0", port), WebhookHandler).serve_forever()
|