open-swe/agent/utils/github_app.py
Aran Yogesh b735afb445
feat: add GitHub PR comment trigger and reply support [closes OPE-1] (#1014)
* feat: add GitHub PR comment trigger and reply support

* refactor: improve readability of GitHub integration

* linting

* fix: resolve github token from thread metadata and improve PR trigger flow

* fix: fall back to OAuth for GitHub webhook when no token in thread metadata

* give me commit message github integeration working without a breaking

* auth.py refactor

* fix: validate cached GitHub token before use to handle expiry

* liniting

* ci unitest formatting

* feat: post PR comments as GitHub App bot instead of user OAuth token

* resolved comments

* slack resolveed comments

* Refactor docstring and comments in get_slack_repo_config

Removed unnecessary comments and cleaned up docstring formatting.

* cr

* cr

* cr

* cr

---------

Co-authored-by: bracesproul <braceasproul@gmail.com>
2026-03-09 17:14:13 -07:00

56 lines
1.9 KiB
Python

"""GitHub App installation token generation."""
from __future__ import annotations
import logging
import os
import time
import httpx
import jwt
logger = logging.getLogger(__name__)
GITHUB_APP_ID = os.environ.get("GITHUB_APP_ID", "")
GITHUB_APP_PRIVATE_KEY = os.environ.get("GITHUB_APP_PRIVATE_KEY", "")
GITHUB_APP_INSTALLATION_ID = os.environ.get("GITHUB_APP_INSTALLATION_ID", "")
def _generate_app_jwt() -> str:
"""Generate a short-lived JWT signed with the GitHub App private key."""
now = int(time.time())
payload = {
"iat": now - 60, # issued 60s ago to account for clock skew
"exp": now + 540, # expires in 9 minutes (max is 10)
"iss": GITHUB_APP_ID,
}
private_key = GITHUB_APP_PRIVATE_KEY.replace("\\n", "\n")
return jwt.encode(payload, private_key, algorithm="RS256")
async def get_github_app_installation_token() -> str | None:
"""Exchange the GitHub App JWT for an installation access token.
Returns:
Installation access token string, or None if unavailable.
"""
if not GITHUB_APP_ID or not GITHUB_APP_PRIVATE_KEY or not GITHUB_APP_INSTALLATION_ID:
logger.debug("GitHub App env vars not fully configured, skipping app token")
return None
try:
app_jwt = _generate_app_jwt()
async with httpx.AsyncClient() as client:
response = await client.post(
f"https://api.github.com/app/installations/{GITHUB_APP_INSTALLATION_ID}/access_tokens",
headers={
"Authorization": f"Bearer {app_jwt}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
)
response.raise_for_status()
return response.json().get("token")
except Exception:
logger.exception("Failed to get GitHub App installation token")
return None