feat: PR-Agent GitHub App server — sanitized source + Nix flake + CI deploy
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""PR-Agent GitHub App + manifest callback server"""
|
||||
import os, sys, json
|
||||
|
||||
# Configurable paths (systemd Nix deployment keeps secrets outside the store)
|
||||
APP_DIR = os.environ.get("PR_AGENT_APP_DIR", "/var/lib/pr-agent-server")
|
||||
private_key_path = os.environ.get(
|
||||
"PRIVATE_KEY_PATH", os.path.join(APP_DIR, "private-key.pem")
|
||||
)
|
||||
omni_key_path = os.environ.get(
|
||||
"OMNIROUTE_KEY_PATH", os.path.join(APP_DIR, "omniroute_key")
|
||||
)
|
||||
|
||||
with open(private_key_path) as f:
|
||||
private_key = f.read()
|
||||
|
||||
os.environ["GITHUB__DEPLOYMENT_TYPE"] = "app"
|
||||
os.environ["GITHUB__APP_ID"] = os.environ.get("GITHUB_APP_ID", "4319749")
|
||||
os.environ["GITHUB__PRIVATE_KEY"] = private_key
|
||||
os.environ["GITHUB__WEBHOOK_SECRET"] = os.environ.get("GITHUB_WEBHOOK_SECRET", "")
|
||||
|
||||
with open(omni_key_path) as f:
|
||||
omni_key = f.read().strip()
|
||||
os.environ["OPENAI__API_BASE"] = os.environ.get(
|
||||
"OPENAI_API_BASE", "https://omniroute.imrnes.team/v1"
|
||||
)
|
||||
os.environ["OPENAI__KEY"] = omni_key
|
||||
os.environ["CONFIG__MODEL"] = os.environ.get("PR_AGENT_MODEL", "openai/claude-opus-4-8")
|
||||
os.environ["CONFIG__FALLBACK_MODELS"] = os.environ.get(
|
||||
"PR_AGENT_FALLBACK_MODELS",
|
||||
'["openai/auto/best-coding","openai/auto/claude-sonnet"]',
|
||||
)
|
||||
os.environ["CONFIG__CUSTOM_MODEL_MAX_TOKENS"] = os.environ.get(
|
||||
"PR_AGENT_MAX_TOKENS", "128000"
|
||||
)
|
||||
os.environ["GITHUB_APP__PR_COMMANDS"] = os.environ.get(
|
||||
"PR_AGENT_PR_COMMANDS",
|
||||
'["/review --pr_reviewer.require_score_review=true --pr_reviewer.require_security_review=true","/describe","/improve"]',
|
||||
)
|
||||
|
||||
sys.path.insert(0, APP_DIR)
|
||||
from pr_agent.servers.github_app import app as pr_agent_app, router as pr_router
|
||||
from fastapi import FastAPI, Request
|
||||
import uvicorn
|
||||
import httpx
|
||||
|
||||
from starlette.middleware import Middleware
|
||||
from starlette_context.middleware import RawContextMiddleware
|
||||
|
||||
app = FastAPI(middleware=[Middleware(RawContextMiddleware)])
|
||||
app.include_router(pr_router)
|
||||
|
||||
|
||||
@app.get("/setup/callback")
|
||||
@app.post("/setup/callback")
|
||||
async def callback(request: Request):
|
||||
params = dict(request.query_params)
|
||||
if "code" in params:
|
||||
code = params["code"]
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.post(
|
||||
f"https://api.github.com/app-manifests/{code}/conversions",
|
||||
headers={"Accept": "application/vnd.github.v3+json"},
|
||||
)
|
||||
if resp.status_code == 201:
|
||||
data = resp.json()
|
||||
creds = {
|
||||
"app_id": data.get("id"),
|
||||
"pem": data.get("pem"),
|
||||
"webhook_secret": data.get("webhook_secret"),
|
||||
"slug": data.get("slug"),
|
||||
}
|
||||
with open(os.path.join(APP_DIR, "credentials_callback.json"), "w") as f:
|
||||
json.dump(creds, f, indent=2)
|
||||
return {
|
||||
"status": "success",
|
||||
"app_id": creds["app_id"],
|
||||
"slug": creds["slug"],
|
||||
}
|
||||
return {"status": "ok", "message": "callback received"}
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = int(os.environ.get("PORT", "3000"))
|
||||
print(f"PR-Agent GitHub App server starting...")
|
||||
print(f" App ID: {os.environ.get('GITHUB_APP_ID', '')}")
|
||||
print(f" Model: {os.environ.get('PR_AGENT_MODEL', 'openai/claude-opus-4-8')} via omniroute")
|
||||
print(f" Endpoint: /api/v1/github_webhooks")
|
||||
print(f" Port: {port}")
|
||||
uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
|
||||
Reference in New Issue
Block a user