fester/backend/integrations/forgejo.py

69 lines
2.3 KiB
Python

"""
Forgejo integration — webhook handler for push events.
Kicks off a build when a push event arrives. The actual webhook endpoint
should live in main.py (or a dedicated webhooks router); this function
encapsulates the "convert push → BuildBody → start build" logic.
"""
from typing import Any, Dict, Optional
def on_push_event(project: str, repo: str, ref: str = "main",
runner=None) -> Dict[str, Any]:
"""Handle a Forgejo push webhook.
Args:
project: project name (e.g. "linux-tool")
repo: full clone URL
ref: git ref that was pushed (e.g. "main", "refs/tags/v1.0")
runner: BuildRunner instance (if None, returns the BuildBody
that would have been dispatched)
Returns:
dict with build_id + status, OR the BuildBody if no runner.
"""
# Build a project spec for the engine
body = {
"cmd": f"git fetch && git checkout {ref} && make -j$(nproc)",
"dir": f"/home/user/builds/{project}",
"project": project,
"arch": "x86_64",
"target": "linux-gnu",
"toolchain": "gcc",
}
if runner is None:
return {"status": "no_runner", "body": body}
# Dispatch through the runner (async, but we can't await from sync)
import asyncio
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
if loop:
# We're inside an async context — schedule the build
task = loop.create_task(runner.start(_BuildBodyShim(**body)))
return {"status": "scheduled", "body": body}
else:
# Sync context — run in a new loop (blocks)
async def _start():
return await runner.start(_BuildBodyShim(**body))
build_id = asyncio.run(_start())
return {"status": "started", "build_id": build_id, "body": body}
class _BuildBodyShim:
"""Tiny shim so we can pass a body-like object to BuildRunner.start
without importing pydantic (which would create a circular import)."""
def __init__(self, cmd, dir, project=None, arch="x86_64",
target="linux-gnu", toolchain="gcc"):
self.cmd = cmd
self.dir = dir
self.project = project
self.arch = arch
self.target = target
self.toolchain = toolchain