ci: trigger ai reviews and add maintainer

This commit is contained in:
Ogulcan Celik
2026-08-31 12:46:29 +03:00
parent 2290257acb
commit 4dd9aa5ed4
2 changed files with 28 additions and 39 deletions
+1
View File
@@ -1,4 +1,5 @@
# GitHub usernames with Herdr maintainer authority, one per line.
# Maintainers must also use the canonical repository and have write access.
ogulcancelik
Pimpmuckl
kangal-bot
+27 -39
View File
@@ -2,7 +2,7 @@ name: PR Gate
on:
pull_request_target:
types: [opened, closed, reopened]
types: [opened, closed, reopened, ready_for_review]
concurrency:
group: pr-gate-${{ github.event.pull_request.number }}
@@ -27,7 +27,7 @@ jobs:
49699333, // dependabot[bot]
41898282, // github-actions[bot]
]);
const REVIEW_LABEL = 'ai-review';
const REVIEW_TRIGGER_MARKER = '<!-- herdr:ai-review-trigger -->';
const COMMENT_MARKER = '<!-- herdr:pr-gate -->';
const pullNumber = context.payload.pull_request.number;
@@ -85,16 +85,6 @@ jobs:
return ['admin', 'maintain', 'write'].includes(await getPermission(username));
}
async function currentLabels() {
const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullNumber,
per_page: 100,
});
return new Set(labels.map(label => label.name));
}
async function hasVerifiedRecovery() {
const events = await github.paginate(github.rest.issues.listEventsForTimeline, {
owner: context.repo.owner,
@@ -108,28 +98,31 @@ jobs:
await isVerifiedMaintainer(latestStateEvent.actor?.login);
}
async function addReviewLabel() {
if ((await currentLabels()).has(REVIEW_LABEL)) return;
await github.rest.issues.addLabels({
async function requestAiReviews() {
if (pr.draft) {
core.info(`PR #${pullNumber} is a draft; deferring AI reviews until ready`);
return;
}
const marker = REVIEW_TRIGGER_MARKER;
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullNumber,
labels: [REVIEW_LABEL],
per_page: 100,
});
}
async function removeReviewLabel() {
if (!(await currentLabels()).has(REVIEW_LABEL)) return;
try {
await github.request('DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}', {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullNumber,
name: REVIEW_LABEL,
});
} catch (error) {
if (error.status !== 404) throw error;
if (comments.some(comment =>
comment.user?.id === KANGAL_USER_ID && comment.body?.includes(marker))) {
core.info(`AI reviews already requested for PR #${pullNumber}`);
return;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullNumber,
body: [marker, '@coderabbitai review', '@greptileai'].join('\n'),
});
}
async function upsertGateComment(message) {
@@ -163,10 +156,9 @@ jobs:
async function closePullRequest(reason) {
if (await hasVerifiedRecovery()) {
core.info(`PR #${pullNumber} was recovered by a verified maintainer; leaving it open`);
await addReviewLabel();
await requestAiReviews();
return;
}
await removeReviewLabel();
const message = [
`Hi @${prAuthor}, thanks for your interest in contributing.`,
'',
@@ -185,7 +177,7 @@ jobs:
await upsertGateComment(message);
if (await hasVerifiedRecovery()) {
core.info(`PR #${pullNumber} was recovered while the gate was running; leaving it open`);
await addReviewLabel();
await requestAiReviews();
return;
}
await github.rest.pulls.update({
@@ -196,26 +188,22 @@ jobs:
});
}
if (pr.state === 'closed') {
await removeReviewLabel();
return;
}
if (pr.state === 'closed') return;
if (CI_ONLY_PR_AUTHOR_IDS.has(pr.user.id)) {
core.info(`Leaving CI-only bot PR open without automated AI review: ${prAuthor}`);
await removeReviewLabel();
return;
}
if (await isVerifiedMaintainer(prAuthor)) {
core.info(`${prAuthor} is a verified maintainer`);
await addReviewLabel();
await requestAiReviews();
return;
}
if (approvedContributors.has(prAuthor.toLowerCase())) {
core.info(`${prAuthor} is in the approved contributors list`);
await addReviewLabel();
await requestAiReviews();
return;
}