mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
add support for GHES to the review agent
This commit is contained in:
parent
2dfafdae41
commit
c84d1e3ab5
2 changed files with 56 additions and 10 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed incorrect shutdown of PostHog SDK in the worker. [#609](https://github.com/sourcebot-dev/sourcebot/pull/609)
|
- Fixed incorrect shutdown of PostHog SDK in the worker. [#609](https://github.com/sourcebot-dev/sourcebot/pull/609)
|
||||||
- Fixed race condition in job schedulers. [#607](https://github.com/sourcebot-dev/sourcebot/pull/607)
|
- Fixed race condition in job schedulers. [#607](https://github.com/sourcebot-dev/sourcebot/pull/607)
|
||||||
|
- Fixed review agent so that it works with GHES instances [#611](https://github.com/sourcebot-dev/sourcebot/pull/611)
|
||||||
|
|
||||||
## [4.9.1] - 2025-11-07
|
## [4.9.1] - 2025-11-07
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,22 @@ import { createLogger } from "@sourcebot/shared";
|
||||||
|
|
||||||
const logger = createLogger('github-webhook');
|
const logger = createLogger('github-webhook');
|
||||||
|
|
||||||
let githubApp: App | undefined;
|
const DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com";
|
||||||
|
type GitHubAppBaseOptions = Omit<ConstructorParameters<typeof App>[0], "Octokit">;
|
||||||
|
|
||||||
|
let githubAppBaseOptions: GitHubAppBaseOptions | undefined;
|
||||||
|
const githubAppCache = new Map<string, App>();
|
||||||
|
|
||||||
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
||||||
try {
|
try {
|
||||||
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
||||||
|
|
||||||
const throttledOctokit = Octokit.plugin(throttling);
|
githubAppBaseOptions = {
|
||||||
githubApp = new App({
|
|
||||||
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
||||||
privateKey: privateKey,
|
privateKey,
|
||||||
webhooks: {
|
webhooks: {
|
||||||
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
||||||
},
|
},
|
||||||
Octokit: throttledOctokit,
|
|
||||||
throttle: {
|
throttle: {
|
||||||
onRateLimit: (retryAfter: number, options: Required<EndpointDefaults>, octokit: Octokit, retryCount: number) => {
|
onRateLimit: (retryAfter: number, options: Required<EndpointDefaults>, octokit: Octokit, retryCount: number) => {
|
||||||
if (retryCount > 3) {
|
if (retryCount > 3) {
|
||||||
|
|
@ -35,13 +38,51 @@ if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error initializing GitHub app: ${error}`);
|
logger.error(`Error initializing GitHub app: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeGithubApiBaseUrl = (baseUrl?: string) => {
|
||||||
|
if (!baseUrl) {
|
||||||
|
return DEFAULT_GITHUB_API_BASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseUrl.replace(/\/+$/, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveGithubApiBaseUrl = (headers: Record<string, string>) => {
|
||||||
|
const enterpriseHost = headers["x-github-enterprise-host"];
|
||||||
|
if (enterpriseHost) {
|
||||||
|
return normalizeGithubApiBaseUrl(`https://${enterpriseHost}/api/v3`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_GITHUB_API_BASE_URL;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGithubAppForBaseUrl = (baseUrl: string) => {
|
||||||
|
if (!githubAppBaseOptions) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedBaseUrl = normalizeGithubApiBaseUrl(baseUrl);
|
||||||
|
const cachedApp = githubAppCache.get(normalizedBaseUrl);
|
||||||
|
if (cachedApp) {
|
||||||
|
return cachedApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OctokitWithBaseUrl = Octokit.plugin(throttling).defaults({ baseUrl: normalizedBaseUrl });
|
||||||
|
const app = new App({
|
||||||
|
...githubAppBaseOptions,
|
||||||
|
Octokit: OctokitWithBaseUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
githubAppCache.set(normalizedBaseUrl, app);
|
||||||
|
return app;
|
||||||
|
};
|
||||||
|
|
||||||
function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
|
function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
|
||||||
return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
|
return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
|
||||||
}
|
}
|
||||||
|
|
@ -52,12 +93,16 @@ function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is
|
||||||
|
|
||||||
export const POST = async (request: NextRequest) => {
|
export const POST = async (request: NextRequest) => {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const headers = Object.fromEntries(request.headers.entries());
|
const headers = Object.fromEntries(Array.from(request.headers.entries(), ([key, value]) => [key.toLowerCase(), value]));
|
||||||
|
|
||||||
const githubEvent = headers['x-github-event'] || headers['X-GitHub-Event'];
|
const githubEvent = headers['x-github-event'];
|
||||||
if (githubEvent) {
|
if (githubEvent) {
|
||||||
logger.info('GitHub event received:', githubEvent);
|
logger.info('GitHub event received:', githubEvent);
|
||||||
|
|
||||||
|
const githubApiBaseUrl = resolveGithubApiBaseUrl(headers);
|
||||||
|
logger.debug('Using GitHub API base URL for event', { githubApiBaseUrl });
|
||||||
|
const githubApp = getGithubAppForBaseUrl(githubApiBaseUrl);
|
||||||
|
|
||||||
if (!githubApp) {
|
if (!githubApp) {
|
||||||
logger.warn('Received GitHub webhook event but GitHub app env vars are not set');
|
logger.warn('Received GitHub webhook event but GitHub app env vars are not set');
|
||||||
return Response.json({ status: 'ok' });
|
return Response.json({ status: 'ok' });
|
||||||
|
|
@ -113,4 +158,4 @@ export const POST = async (request: NextRequest) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json({ status: 'ok' });
|
return Response.json({ status: 'ok' });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue