Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import {
} from "../github/backfill";
import { contributorRepoStatsFromGittensor, fetchGittensorContributorSnapshot, fetchOfficialGittensorMiner, type GittensorContributorSnapshot, type OfficialGittensorMinerDetection } from "../gittensor/api";
import { createOrUpdateCheckRun, createOrUpdateGateCheckRun, createOrUpdatePendingGateCheckRun, createOrUpdateSkippedGateCheckRun, getInstallationId } from "../github/app";
import { createOrUpdateAgentCommandComment, createOrUpdatePrIntelligenceComment } from "../github/comments";
import { createOrUpdateAgentCommandComment, createOrUpdatePrIntelligenceComment, PR_PANEL_COMMENT_MARKER } from "../github/comments";
import {
buildMaintainerQueueDigest,
buildPublicAgentCommandComment,
Expand Down Expand Up @@ -122,6 +122,7 @@ import {
buildQueueHealth,
buildRoleContext,
detectGittensorContributor,
PR_PANEL_RETRIGGER_MARKER,
} from "../signals/engine";
import { decidePublicSurface } from "../signals/settings-preview";
import type { LocalBranchAnalysisInput } from "../signals/local-branch";
Expand Down Expand Up @@ -659,6 +660,19 @@ async function processGitHubWebhook(env: Env, deliveryId: string, eventName: str
return;
}

if (eventName === "issue_comment" && (await maybeProcessPrPanelRetrigger(env, deliveryId, payload))) {
await recordWebhookEvent(env, {
deliveryId,
eventName,
action: payload.action,
installationId: payload.installation?.id,
repositoryFullName: payload.repository?.full_name,
payloadHash: "processed",
status: "processed",
});
return;
}

if (eventName === "issue_comment" && (await maybeProcessGittensoryMentionCommand(env, deliveryId, payload))) {
await recordWebhookEvent(env, {
deliveryId,
Expand Down Expand Up @@ -1071,6 +1085,103 @@ async function recordGithubProductUsage(
}).catch(() => undefined);
}

async function maybeProcessPrPanelRetrigger(env: Env, deliveryId: string, payload: GitHubWebhookPayload): Promise<boolean> {
const comment = payload.comment;
if (payload.action !== "edited" || !comment || !isCheckedPrPanelRetrigger(comment.body)) return false;
if (!isGittensoryPanelBotComment(env, comment.user)) return false;

const repoFullName = payload.repository?.full_name;
const issue = payload.issue;
const installationId = getInstallationId(payload);
const actor = payload.sender?.login ?? null;
const targetKey = repoFullName && issue ? `${repoFullName}#${issue.number}` : repoFullName;
if (payload.sender?.type === "Bot" || /\[bot\]$/i.test(actor ?? "")) {
await recordPrPanelRetriggerSkip(env, deliveryId, repoFullName, targetKey, actor, "bot_author");
return true;
}
if (!repoFullName || !issue?.pull_request || !installationId) {
await recordPrPanelRetriggerSkip(env, deliveryId, repoFullName, targetKey, actor, "missing_repo_pr_or_installation");
return true;
}
const pr = await getPullRequest(env, repoFullName, issue.number);
if (!pr) {
await recordPrPanelRetriggerSkip(env, deliveryId, repoFullName, targetKey, actor, "cached_pr_missing");
return true;
}

const [repo, settings, otherOpenPullRequests] = await Promise.all([
getRepository(env, repoFullName),
getRepositorySettings(env, repoFullName),
listOtherOpenPullRequests(env, repoFullName, pr.number),
]);
const advisory = buildPullRequestAdvisory(repo, pr, {
otherOpenPullRequests,
requireLinkedIssue: settings.requireLinkedIssue || settings.linkedIssueGateMode !== "off",
});
await persistAdvisory(env, advisory);
await recordAuditEvent(env, {
eventType: "github_app.pr_panel_retriggered",
actor,
targetKey: `${repoFullName}#${pr.number}`,
outcome: "completed",
metadata: { deliveryId, repoFullName, commentId: comment.id },
});
await maybePublishPrPublicSurface(env, installationId, repoFullName, pr, repo, settings, advisory, {
deliveryId,
action: "manual_retrigger",
});
await recordGithubProductUsage(env, "pr_panel_retriggered", {
actor,
repoFullName,
targetKey: `${repoFullName}#${pr.number}`,
outcome: "completed",
metadata: { commentId: comment.id },
});
return true;
}

function isCheckedPrPanelRetrigger(body: string | null | undefined): boolean {
if (!body?.includes(PR_PANEL_COMMENT_MARKER) || !body.includes(PR_PANEL_RETRIGGER_MARKER)) return false;
return checkedMarkerRegex(PR_PANEL_RETRIGGER_MARKER).test(body);
}

function checkedMarkerRegex(marker: string): RegExp {
return new RegExp(`(?:^|\\n)\\s*[-*]\\s*\\[[xX]\\]\\s*${escapeRegExp(marker)}`);
}

function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function isGittensoryPanelBotComment(env: Env, user: NonNullable<GitHubWebhookPayload["comment"]>["user"] | undefined): boolean {
return user?.type === "Bot" && user.login?.toLowerCase() === `${env.GITHUB_APP_SLUG}[bot]`.toLowerCase();
}

async function recordPrPanelRetriggerSkip(
env: Env,
deliveryId: string,
repoFullName: string | null | undefined,
targetKey: string | null | undefined,
actor: string | null,
reason: string,
): Promise<void> {
await recordAuditEvent(env, {
eventType: "github_app.pr_panel_retrigger_skipped",
actor,
targetKey,
outcome: "completed",
detail: reason,
metadata: { deliveryId, ...(repoFullName ? { repoFullName } : {}) },
});
await recordGithubProductUsage(env, "pr_panel_retrigger_skipped", {
actor,
repoFullName,
targetKey,
outcome: "skipped",
metadata: { reason },
});
}

async function maybeProcessGittensoryMentionCommand(env: Env, deliveryId: string, payload: GitHubWebhookPayload): Promise<boolean> {
const command = parseGittensoryMentionCommand(payload.comment?.body);
if (!command) return false;
Expand Down
4 changes: 4 additions & 0 deletions src/signals/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3513,6 +3513,8 @@ export function buildPublicReadinessScore(args: {
};
}

export const PR_PANEL_RETRIGGER_MARKER = "<!-- gittensory-rerun-review:v1 -->";

export function buildPublicPrIntelligenceComment(args: {
repo: RepositoryRecord | null;
pr: PullRequestRecord;
Expand Down Expand Up @@ -3671,6 +3673,8 @@ export function buildPublicPrIntelligenceComment(args: {
"",
"</details>",
"",
`- [ ] ${PR_PANEL_RETRIGGER_MARKER} Re-run Gittensory review`,
"",
"---",
footer,
].join("\n");
Expand Down
Loading