aboutsummaryrefslogtreecommitdiff
path: root/.github/workflows
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-08-31 12:46:29 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-08-31 12:46:29 +0300
commit4dd9aa5ed4fb8a86ee798d1d5e3bc46ce5fabac2 (patch)
tree5b4074cb0734c02e946745ef3e3aeda252ef34a4 /.github/workflows
parent2290257acb2085ce6842ba5c7e3ca50c3ba64f02 (diff)
ci: trigger ai reviews and add maintainer
Diffstat (limited to '.github/workflows')
-rw-r--r--.github/workflows/pr-gate.yml66
1 files changed, 27 insertions, 39 deletions
diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml
index 92b4c09b..4866dc88 100644
--- a/.github/workflows/pr-gate.yml
+++ b/.github/workflows/pr-gate.yml
@@ -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;
}