diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..11e8596 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000..928c83b --- /dev/null +++ b/app.json @@ -0,0 +1,17 @@ +{ + "name": "GitHub Webhook Forwarder", + "description": "Forwards GitHub webhooks to dev instances of Pangeo Forge API.", + "image": "heroku/python", + "repository": "https://github.com/pangeo-forge/github-webhook-forwarder", + "keywords": [], + "addons": [], + "env": {}, + "environments": { + "test": { + "scripts": { + "test-setup": "python -m pip install -r dev-requirements.txt", + "test": "pytest test.py -v" + } + } + } + } diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..55b033e --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1 @@ +pytest \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..88ae44f --- /dev/null +++ b/main.py @@ -0,0 +1,68 @@ +import httpx +from fastapi import FastAPI, HTTPException, Request, status +from pydantic import BaseModel + +app = FastAPI() + + +class GitHubLabel(BaseModel): + name: str + + +@app.post("/", status_code=status.HTTP_200_OK) +async def forwarder(request: Request): + """ + + """ + + event = request.headers.get("X-GitHub-Event") + print(f"{event = }") + + request_json = await request.json() + request_bytes = await request.body() + + if event == "pull_request": + label_containing_obj = "pull_request" + elif event == "issue_comment": + label_containing_obj = "issue" + else: + raise NotImplementedError + + labels = [ + GitHubLabel(**label) + for label in request_json[label_containing_obj]["labels"] + ] + if not labels: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="No labels." + ) + + forward_to = [ + l.name.split("fwd:")[-1] + for l in labels + if l.name.startswith("fwd:") + ] + if not forward_to: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="No labels starting with 'fwd:'", + ) + + responses = {} + for recipient in forward_to: + print(f"Forwarding GitHub webhook to {recipient = }") + async with httpx.AsyncClient() as client: + r = await client.post( + f"https://{recipient}", + headers={ + "X-GitHub-Event": request.headers.get("X-GitHub-Event"), + "X-Hub-Signature-256": request.headers.get("X-Hub-Signature-256"), + "content-type": "application/json", + }, + data=request_bytes, + ) + print(f"{recipient = } responded with {r.status_code = }") + responses |= {recipient: r.status_code} + + return f"Forwarded to {responses}" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5205612 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +fastapi==0.87.0 +gunicorn==20.1.0 +httpx==0.23.3 +uvicorn==0.20.0 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..cd6f130 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.11.1 \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..d587138 --- /dev/null +++ b/test.py @@ -0,0 +1,2 @@ +def test_main(): + ...