Skip to content

Commit ddaa795

Browse files
committed
add support for GHES to the review agent
1 parent 2dfafda commit ddaa795

File tree

1 file changed

+55
-10
lines changed
  • packages/web/src/app/api/(server)/webhook

1 file changed

+55
-10
lines changed

packages/web/src/app/api/(server)/webhook/route.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,22 @@ import { createLogger } from "@sourcebot/shared";
1313

1414
const 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+
1722
if (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+
4586
function 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

5394
export 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

Comments
 (0)