Skip to content

Commit dd94e38

Browse files
authored
Merge branch 'main' into fix/bot-intermediate-assignment-guard
2 parents 9fc88dd + 8233144 commit dd94e38

File tree

4 files changed

+100
-35
lines changed

4 files changed

+100
-35
lines changed

.github/scripts/bot-gfi-assign-on-comment.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,23 +340,54 @@ module.exports = async ({ github, context }) => {
340340
console.log('[gfi-assign] Assignment completed successfully');
341341

342342
// Chain mentor assignment after successful GFI assignment
343+
let mentorAssignmentSucceeded = false;
343344
try {
344345
const assignMentor = require('./bot-mentor-assignment.js');
345346
await assignMentor({
346347
github,
347348
context,
348349
assignee: { login: requesterUsername, type: 'User' } // Pass freshly-assigned username
349350
});
351+
mentorAssignmentSucceeded = true;
350352
console.log('[gfi-assign] Mentor assignment chained successfully');
351353
} catch (error) {
352354
console.error('[gfi-assign] Mentor assignment failed but user assignment succeeded:', {
353355
message: error.message,
354356
status: error.status,
355-
issueNumber: context.payload.issue?.number,
357+
owner,
358+
repo,
359+
issueNumber,
356360
assignee: requesterUsername,
357361
});
358362
// Don't throw error - user assignment was successful
359363
}
364+
365+
// Chain CodeRabbit plan trigger after mentor assignment
366+
// Only trigger if mentor assignment succeeded to maintain the expected flow
367+
if (mentorAssignmentSucceeded) {
368+
try {
369+
const { triggerCodeRabbitPlan, hasExistingCodeRabbitPlan } = require('./coderabbit_plan_trigger.js');
370+
371+
// Check if CodeRabbit plan already exists to avoid duplicate comments
372+
const planExists = await hasExistingCodeRabbitPlan(github, owner, repo, issueNumber);
373+
if (planExists) {
374+
console.log('[gfi-assign] CodeRabbit plan already exists, skipping');
375+
} else {
376+
await triggerCodeRabbitPlan(github, owner, repo, issue);
377+
console.log('[gfi-assign] CodeRabbit plan chained successfully');
378+
}
379+
} catch (error) {
380+
console.error('[gfi-assign] CodeRabbit plan failed but user assignment succeeded:', {
381+
message: error.message,
382+
status: error.status,
383+
owner,
384+
repo,
385+
issueNumber,
386+
assignee: requesterUsername,
387+
});
388+
// Don't throw error - user assignment was successful
389+
}
390+
}
360391
} catch (error) {
361392
console.error('[gfi-assign] Error:', {
362393
message: error.message,
Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Script to trigger CodeRabbit plan for intermediate and advanced issues
22

3-
const marker = '<!-- CodeRabbit Plan Trigger -->';
3+
const CODERABBIT_MARKER = '<!-- CodeRabbit Plan Trigger -->';
44

5-
async function triggerCodeRabbitPlan(github, owner, repo, issue, marker) {
5+
async function triggerCodeRabbitPlan(github, owner, repo, issue, marker = CODERABBIT_MARKER) {
66
const comment = `${marker} @coderabbitai plan`;
77

88
try {
@@ -15,41 +15,64 @@ async function triggerCodeRabbitPlan(github, owner, repo, issue, marker) {
1515
console.log(`Triggered CodeRabbit plan for issue #${issue.number}`);
1616
return true;
1717
} catch (commentErr) {
18-
console.log(`Failed to trigger CodeRabbit plan for issue #${issue.number}:`, commentErr.message || commentErr);
18+
console.log('Failed to trigger CodeRabbit plan:', {
19+
message: commentErr?.message,
20+
status: commentErr?.status,
21+
owner,
22+
repo,
23+
issueNumber: issue?.number,
24+
});
1925
return false;
2026
}
2127
}
2228

2329
function hasBeginnerOrHigherLabel(issue, label) {
2430
// Check if issue has beginner, intermediate or advanced label (case-insensitive)
25-
26-
const allowed = ['beginner', 'intermediate', "advanced"];
31+
const allowed = ['beginner', 'intermediate', 'advanced'];
2732

2833
const hasAllowedLabel = issue.labels?.some(l => allowed.includes(l?.name?.toLowerCase()));
2934

30-
// Also check if newly added label is intermediate/advanced
31-
35+
// Also check if newly added label is beginner/intermediate/advanced
3236
const isNewLabelAllowed = allowed.includes(label?.name?.toLowerCase());
3337

3438
return hasAllowedLabel || isNewLabelAllowed;
35-
3639
}
3740

3841
async function hasExistingCodeRabbitPlan(github, owner, repo, issueNumber) {
3942
// Check for existing CodeRabbit plan comment (limited to first 500 comments)
40-
const comments = [];
41-
const iterator = github.paginate.iterator(github.rest.issues.listComments, {
42-
owner, repo, issue_number: issueNumber, per_page: 100
43-
});
44-
45-
let count = 0;
46-
for await (const { data: page } of iterator) {
47-
comments.push(...page);
48-
count += page.length;
49-
if (count >= 500) break; // Hard upper bound to prevent unbounded pagination
43+
// Uses marker-based detection to avoid false positives from quoted text
44+
try {
45+
const comments = [];
46+
const iterator = github.paginate.iterator(github.rest.issues.listComments, {
47+
owner,
48+
repo,
49+
issue_number: issueNumber,
50+
per_page: 100,
51+
});
52+
53+
let count = 0;
54+
for await (const { data: page } of iterator) {
55+
comments.push(...page);
56+
count += page.length;
57+
if (count >= 500) break; // Hard upper bound to prevent unbounded pagination
58+
}
59+
60+
// Check for marker-based comment OR @coderabbitai plan text
61+
return comments.some(c =>
62+
typeof c?.body === 'string' &&
63+
(c.body.includes(CODERABBIT_MARKER) || c.body.includes('@coderabbitai plan'))
64+
);
65+
} catch (error) {
66+
console.log('Failed to check existing CodeRabbit plan comments:', {
67+
message: error?.message,
68+
status: error?.status,
69+
owner,
70+
repo,
71+
issueNumber,
72+
});
73+
// Return false to allow plan trigger attempt (fail-open for better UX)
74+
return false;
5075
}
51-
52-
return comments.some(c => c.body?.includes('@coderabbitai plan'));
5376
}
5477

5578
function logSummary(owner, repo, issue) {
@@ -60,27 +83,42 @@ function logSummary(owner, repo, issue) {
6083
console.log(`Labels: ${issue.labels?.map(l => l.name).join(', ') || 'none'}`);
6184
}
6285

63-
module.exports = async ({ github, context }) => {
86+
// Main workflow handler (default export for workflow usage)
87+
async function main({ github, context }) {
6488
try {
6589
const { owner, repo } = context.repo;
6690
const { issue, label } = context.payload;
6791

6892
// Validations
6993
if (!issue?.number) return console.log('No issue in payload');
70-
94+
7195
if (!hasBeginnerOrHigherLabel(issue, label)) {
72-
return console.log('Issue does not have intermediate or advanced label');
96+
return console.log('Issue does not have beginner/intermediate/advanced label');
7397
}
7498

7599
if (await hasExistingCodeRabbitPlan(github, owner, repo, issue.number)) {
76100
return console.log(`CodeRabbit plan already triggered for #${issue.number}`);
77101
}
78102

79103
// Post CodeRabbit plan trigger
80-
await triggerCodeRabbitPlan(github, owner, repo, issue, marker);
104+
await triggerCodeRabbitPlan(github, owner, repo, issue, CODERABBIT_MARKER);
81105

82106
logSummary(owner, repo, issue);
83107
} catch (err) {
84-
console.log('❌ Error:', err.message);
108+
console.log('❌ Error:', {
109+
message: err?.message,
110+
status: err?.status,
111+
owner: context?.repo?.owner,
112+
repo: context?.repo?.repo,
113+
issueNumber: context?.payload?.issue?.number,
114+
});
85115
}
86-
};
116+
}
117+
118+
// Default export for workflow usage: await script({ github, context })
119+
module.exports = main;
120+
121+
// Named exports for reuse by other scripts (e.g., GFI assignment bot)
122+
module.exports.triggerCodeRabbitPlan = triggerCodeRabbitPlan;
123+
module.exports.hasExistingCodeRabbitPlan = hasExistingCodeRabbitPlan;
124+
module.exports.CODERABBIT_MARKER = CODERABBIT_MARKER;

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
1212
- Added comprehensive docstring to `FeeAssessmentMethod` enum explaining inclusive vs exclusive fee assessment methods with usage examples. (#1391)
1313
- Added comprehensive docstring to `TokenType` enum explaining fungible vs non-fungible tokens with practical use cases. (#1392)
1414
- Enable dry run support for office hours bot via `workflow_dispatch` trigger for testing without posting comments. (#1426)
15+
- Trigger CodeRabbit plan comment after Good First Issue assignment to provide AI-generated implementation guidance to new contributors. (#1432)
1516

1617
- Added a notification workflow that alerts the support team when an issue is labeled as a Good First Issue Candidate.[(#1296)]
1718
- Added comprehensive training documentation for the `Query` class, covering execution flow, payments, retries, and building child queries. (#1238)
@@ -100,6 +101,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
100101
- Added prompt for coderabbit to review `Query` and it's sub-classes.
101102

102103
### Changed
104+
- Refactored `file_info_query.py` to use `print(info)` instead of manual formatting (#1451)
103105

104106
- Enable CodeRabbit walkthrough mode by default to improve PR review visibility (#1439)
105107
- Move assignment guards to be directly inside the gfi and beginner auto assign

examples/file/file_info_query.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,9 @@ def query_file_info():
7373

7474
info = FileInfoQuery().set_file_id(file_id).execute(client)
7575

76+
# Use the SDK's built-in pretty print method
7677
print("\nFile Info:")
77-
print(f"File ID: {info.file_id}")
78-
print(f"Size: {info.size} bytes")
79-
print(f"Memo: {info.file_memo}")
80-
print(f"Expiration Time: {info.expiration_time}")
81-
print(f"Deleted: {info.is_deleted}")
82-
print(f"Total key(s): {len(info.keys)}")
83-
for i, key in enumerate(info.keys, 1):
84-
print(f"Key {i}: {key.to_string()}")
78+
print(info)
8579

8680

8781
if __name__ == "__main__":

0 commit comments

Comments
 (0)