Dry up schemas and ignore vercel PR comments (#206)

This commit is contained in:
Chris Estreich 2025-07-05 09:34:28 -08:00 committed by GitHub
parent 014261464f
commit f5eae4d77b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 142 additions and 97 deletions

View file

@ -1,36 +1,12 @@
import { NextResponse } from 'next/server';
import { z } from 'zod';
import type { JobPayload } from '@roo-code-cloud/db';
import { createAndEnqueueJob, fetchGitHubAPI } from './utils';
const githubIssueCommentWebhookSchema = z.object({
action: z.string(),
issue: z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
pull_request: z.object({ url: z.string() }).optional(),
}),
comment: z.object({
id: z.number(),
body: z.string(),
html_url: z.string(),
user: z.object({ login: z.string() }),
}),
repository: z.object({
full_name: z.string(),
}),
});
const githubPullRequestWebhookSchema = z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
head: z.object({ ref: z.string() }),
base: z.object({ ref: z.string() }),
});
import {
githubIssueCommentWebhookSchema,
githubPullRequestSchema,
} from './types';
import { createAndEnqueueJob, fetchGitHubAPI, isRoomoteMention } from './utils';
export async function handleIssueCommentEvent(body: string) {
const data = githubIssueCommentWebhookSchema.parse(JSON.parse(body));
@ -40,13 +16,12 @@ export async function handleIssueCommentEvent(body: string) {
return NextResponse.json({ message: 'action_ignored' });
}
if (!comment.body.includes('@roomote') || comment.user.login === 'roomote') {
if (!isRoomoteMention(comment)) {
return NextResponse.json({ message: 'no_roomote_mention' });
}
console.log('🗄️ Issue Comment Webhook ->', data);
// Handle PR comments (when comment is on a pull request)
if (issue.pull_request) {
const response = await fetchGitHubAPI(issue.pull_request.url);
@ -61,9 +36,7 @@ export async function handleIssueCommentEvent(body: string) {
// Example:
// https://api.github.com/repos/RooCodeInc/Roo-Code/pulls/4796
const pull_request = githubPullRequestWebhookSchema.parse(
await response.json(),
);
const pull_request = githubPullRequestSchema.parse(await response.json());
console.log(`🗄️ Pull Request -> ${issue.pull_request.url}`, pull_request);
@ -72,8 +45,8 @@ export async function handleIssueCommentEvent(body: string) {
prNumber: pull_request.number,
prTitle: pull_request.title,
prBody: pull_request.body || '',
prBranch: pull_request.head.ref,
baseRef: pull_request.base.ref,
prBranch: pull_request.head?.ref || '',
baseRef: pull_request.base?.ref || '',
commentId: comment.id,
commentBody: comment.body,
commentAuthor: comment.user.login,
@ -93,7 +66,6 @@ export async function handleIssueCommentEvent(body: string) {
});
}
// Handle issue comments (when comment is on a regular issue)
const type = 'github.issue.comment.respond' as const;
const payload: JobPayload<typeof type> = {

View file

@ -1,23 +1,10 @@
import { NextResponse } from 'next/server';
import { z } from 'zod';
import type { JobPayload } from '@roo-code-cloud/db';
import { githubIssueWebhookSchema } from './types';
import { createAndEnqueueJob } from './utils';
const githubIssueWebhookSchema = z.object({
action: z.string(),
issue: z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
labels: z.array(z.object({ name: z.string() })),
}),
repository: z.object({
full_name: z.string(),
}),
});
export async function handleIssueEvent(body: string) {
const data = githubIssueWebhookSchema.parse(JSON.parse(body));
const { action, repository, issue } = data;
@ -33,7 +20,7 @@ export async function handleIssueEvent(body: string) {
issue: issue.number,
title: issue.title,
body: issue.body || '',
labels: issue.labels.map(({ name }) => name),
labels: issue.labels?.map(({ name }) => name) || [],
};
const { jobId, enqueuedJobId } = await createAndEnqueueJob(

View file

@ -1,23 +1,10 @@
import { NextResponse } from 'next/server';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
import { db, cloudJobs } from '@roo-code-cloud/db/server';
import { SlackNotifier } from '@/lib/slack';
const githubPullRequestWebhookSchema = z.object({
action: z.string(),
pull_request: z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
html_url: z.string(),
}),
repository: z.object({
full_name: z.string(),
}),
});
import { githubPullRequestWebhookSchema } from './types';
export async function handlePullRequestEvent(body: string) {
const data = githubPullRequestWebhookSchema.parse(JSON.parse(body));
@ -50,6 +37,7 @@ export async function handlePullRequestEvent(body: string) {
// Filter jobs to find the one matching this repo and issue.
const job = jobs.find((j) => {
const payload = j.payload as { repo: string; issue: number };
return (
payload.repo === repository.full_name && payload.issue === issueNumber
);

View file

@ -1,35 +1,9 @@
import { NextResponse } from 'next/server';
import { z } from 'zod';
import type { JobPayload } from '@roo-code-cloud/db';
import { createAndEnqueueJob } from './utils';
export const githubPullRequestReviewCommentWebhookSchema = z.object({
action: z.string(),
comment: z.object({
id: z.number(),
body: z.string(),
html_url: z.string(),
user: z.object({
login: z.string(),
}),
}),
pull_request: z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
head: z.object({
ref: z.string(),
}),
base: z.object({
ref: z.string(),
}),
}),
repository: z.object({
full_name: z.string(),
}),
});
import { githubPullRequestReviewCommentWebhookSchema } from './types';
import { createAndEnqueueJob, isRoomoteMention } from './utils';
export async function handlePullRequestReviewCommentEvent(body: string) {
const data = githubPullRequestReviewCommentWebhookSchema.parse(
@ -41,7 +15,7 @@ export async function handlePullRequestReviewCommentEvent(body: string) {
return NextResponse.json({ message: 'action_ignored' });
}
if (!comment.body.includes('@roomote') || comment.user.login === 'roomote') {
if (!isRoomoteMention(comment)) {
return NextResponse.json({ message: 'no_roomote_mention' });
}
@ -52,8 +26,8 @@ export async function handlePullRequestReviewCommentEvent(body: string) {
prNumber: pull_request.number,
prTitle: pull_request.title,
prBody: pull_request.body || '',
prBranch: pull_request.head.ref,
baseRef: pull_request.base.ref,
prBranch: pull_request.head?.ref || '',
baseRef: pull_request.base?.ref || '',
commentId: comment.id,
commentBody: comment.body,
commentAuthor: comment.user.login,

View file

@ -0,0 +1,119 @@
import { z } from 'zod';
/**
* GithubComment
*/
export const githubCommentSchema = z.object({
id: z.number(),
body: z.string(),
html_url: z.string(),
user: z.object({
login: z.string(),
}),
});
export type GithubComment = z.infer<typeof githubCommentSchema>;
/**
* GithubPullRequest
*/
export const githubPullRequestSchema = z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
head: z
.object({
ref: z.string(),
})
.optional(),
base: z
.object({
ref: z.string(),
})
.optional(),
html_url: z.string(),
});
export type GithubPullRequest = z.infer<typeof githubPullRequestSchema>;
/**
* GithubIssue
*/
export const githubIssueSchema = z.object({
number: z.number(),
title: z.string(),
body: z.string().nullable(),
pull_request: z.object({ url: z.string() }).optional(),
labels: z.array(z.object({ name: z.string() })).optional(),
});
export type GithubIssue = z.infer<typeof githubIssueSchema>;
/**
* GithubRepository
*/
export const githubRepositorySchema = z.object({
full_name: z.string(),
});
export type GithubRepository = z.infer<typeof githubRepositorySchema>;
/**
* GithubPullRequestReviewCommentWebhook
*/
export const githubPullRequestReviewCommentWebhookSchema = z.object({
action: z.string(),
comment: githubCommentSchema,
pull_request: githubPullRequestSchema,
repository: githubRepositorySchema,
});
export type GithubPullRequestReviewCommentWebhook = z.infer<
typeof githubPullRequestReviewCommentWebhookSchema
>;
/**
* GithubIssueCommentWebhook
*/
export const githubIssueCommentWebhookSchema = z.object({
action: z.string(),
issue: githubIssueSchema,
comment: githubCommentSchema,
repository: githubRepositorySchema,
});
export type GithubIssueCommentWebhook = z.infer<
typeof githubIssueCommentWebhookSchema
>;
/**
* GithubPullRequestWebhook
*/
export const githubPullRequestWebhookSchema = z.object({
action: z.string(),
pull_request: githubPullRequestSchema,
repository: githubRepositorySchema,
});
export type GithubPullRequestWebhook = z.infer<
typeof githubPullRequestWebhookSchema
>;
/**
* GithubIssueWebhook
*/
export const githubIssueWebhookSchema = z.object({
action: z.string(),
issue: githubIssueSchema,
repository: githubRepositorySchema,
});
export type GithubIssueWebhook = z.infer<typeof githubIssueWebhookSchema>;

View file

@ -8,6 +8,7 @@ import {
} from '@roo-code-cloud/db/server';
import { enqueue } from '@/lib';
import type { GithubComment } from './types';
export function verifySignature(
body: string,
@ -57,3 +58,7 @@ export async function fetchGitHubAPI(url: string, options: RequestInit = {}) {
return fetch(url, { ...options, headers });
}
export const isRoomoteMention = (comment: GithubComment) =>
comment.body.includes('@roomote') &&
!['roomote', 'vercel'].includes(comment.user.login);