@@ -13,19 +13,22 @@ import { createLogger } from "@sourcebot/shared";
1313
1414const logger = createLogger ( 'github-webhook' ) ;
1515
16- let githubApp : App | undefined ;
16+ const DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com" ;
17+ type GitHubAppBaseOptions = Omit < ConstructorParameters < typeof App > [ 0 ] , "Octokit" > ;
18+
19+ let githubAppBaseOptions : GitHubAppBaseOptions | undefined ;
20+ const githubAppCache = new Map < string , App > ( ) ;
21+
1722if ( env . GITHUB_REVIEW_AGENT_APP_ID && env . GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env . GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH ) {
1823 try {
1924 const privateKey = fs . readFileSync ( env . GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH , "utf8" ) ;
2025
21- const throttledOctokit = Octokit . plugin ( throttling ) ;
22- githubApp = new App ( {
26+ githubAppBaseOptions = {
2327 appId : env . GITHUB_REVIEW_AGENT_APP_ID ,
24- privateKey : privateKey ,
28+ privateKey,
2529 webhooks : {
2630 secret : env . GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET ,
2731 } ,
28- Octokit : throttledOctokit ,
2932 throttle : {
3033 onRateLimit : ( retryAfter : number , options : Required < EndpointDefaults > , octokit : Octokit , retryCount : number ) => {
3134 if ( retryCount > 3 ) {
@@ -35,13 +38,51 @@ if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET
3538
3639 return true ;
3740 } ,
38- }
39- } ) ;
41+ } ,
42+ } ;
4043 } catch ( error ) {
4144 logger . error ( `Error initializing GitHub app: ${ error } ` ) ;
4245 }
4346}
4447
48+ const normalizeGithubApiBaseUrl = ( baseUrl ?: string ) => {
49+ if ( ! baseUrl ) {
50+ return DEFAULT_GITHUB_API_BASE_URL ;
51+ }
52+
53+ return baseUrl . replace ( / \/ + $ / , "" ) ;
54+ } ;
55+
56+ const resolveGithubApiBaseUrl = ( headers : Record < string , string > ) => {
57+ const enterpriseHost = headers [ "x-github-enterprise-host" ] ;
58+ if ( enterpriseHost ) {
59+ return normalizeGithubApiBaseUrl ( `https://${ enterpriseHost } /api/v3` ) ;
60+ }
61+
62+ return DEFAULT_GITHUB_API_BASE_URL ;
63+ } ;
64+
65+ const getGithubAppForBaseUrl = ( baseUrl : string ) => {
66+ if ( ! githubAppBaseOptions ) {
67+ return undefined ;
68+ }
69+
70+ const normalizedBaseUrl = normalizeGithubApiBaseUrl ( baseUrl ) ;
71+ const cachedApp = githubAppCache . get ( normalizedBaseUrl ) ;
72+ if ( cachedApp ) {
73+ return cachedApp ;
74+ }
75+
76+ const OctokitWithBaseUrl = Octokit . plugin ( throttling ) . defaults ( { baseUrl : normalizedBaseUrl } ) ;
77+ const app = new App ( {
78+ ...githubAppBaseOptions ,
79+ Octokit : OctokitWithBaseUrl ,
80+ } ) ;
81+
82+ githubAppCache . set ( normalizedBaseUrl , app ) ;
83+ return app ;
84+ } ;
85+
4586function isPullRequestEvent ( eventHeader : string , payload : unknown ) : payload is WebhookEventDefinition < "pull-request-opened" > | WebhookEventDefinition < "pull-request-synchronize" > {
4687 return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload . action === "string" && ( payload . action === "opened" || payload . action === "synchronize" ) ;
4788}
@@ -52,12 +93,16 @@ function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is
5293
5394export const POST = async ( request : NextRequest ) => {
5495 const body = await request . json ( ) ;
55- const headers = Object . fromEntries ( request . headers . entries ( ) ) ;
96+ const headers = Object . fromEntries ( Array . from ( request . headers . entries ( ) , ( [ key , value ] ) => [ key . toLowerCase ( ) , value ] ) ) ;
5697
57- const githubEvent = headers [ 'x-github-event' ] || headers [ 'X-GitHub-Event' ] ;
98+ const githubEvent = headers [ 'x-github-event' ] ;
5899 if ( githubEvent ) {
59100 logger . info ( 'GitHub event received:' , githubEvent ) ;
60101
102+ const githubApiBaseUrl = resolveGithubApiBaseUrl ( headers ) ;
103+ logger . debug ( 'Using GitHub API base URL for event' , { githubApiBaseUrl } ) ;
104+ const githubApp = getGithubAppForBaseUrl ( githubApiBaseUrl ) ;
105+
61106 if ( ! githubApp ) {
62107 logger . warn ( 'Received GitHub webhook event but GitHub app env vars are not set' ) ;
63108 return Response . json ( { status : 'ok' } ) ;
@@ -113,4 +158,4 @@ export const POST = async (request: NextRequest) => {
113158 }
114159
115160 return Response . json ( { status : 'ok' } ) ;
116- }
161+ }
0 commit comments