Skip to content

Commit cc1b15f

Browse files
authored
Merge pull request #502 from TencentCloudBase/feature/issue-auto-processor-pr-fix
fix(github): 🩹 harden issue auto processor PR reporting
2 parents b1d7ca9 + 4a00960 commit cc1b15f

3 files changed

Lines changed: 53 additions & 3 deletions

File tree

.github/workflows/issue-auto-processor-simple.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ jobs:
193193
.filter((issue) => new Date(issue.created_at).getTime() <= cutoffMs)
194194
.filter((issue) => {
195195
const labels = (issue.labels || []).map((label) => typeof label === 'string' ? label : label.name);
196-
return !labels.includes('ai-processed') && !labels.includes('ai-processing') && !labels.includes('no-ai');
196+
return !labels.includes('ai-processed') && !labels.includes('ai-processing') && !labels.includes('ai-failed') && !labels.includes('no-ai');
197197
})
198198
.slice(0, maxIssues);
199199
@@ -354,6 +354,7 @@ jobs:
354354
local title=""
355355
local issue_url=""
356356
local pr_url=""
357+
local pr_output=""
357358
local is_bug="false"
358359
local requested_action=""
359360
local command=""
@@ -460,16 +461,19 @@ jobs:
460461
write_file_from_env /tmp/pr-body.md
461462
462463
set +e
463-
pr_url=$(gh pr create --base "$DEFAULT_BRANCH" --head "$branch" --title "fix: 🤖 attempt fix for issue #$number" --body-file /tmp/pr-body.md)
464+
pr_output=$(gh pr create --base "$DEFAULT_BRANCH" --head "$branch" --title "fix: 🤖 attempt fix for issue #$number" --body-file /tmp/pr-body.md 2>&1)
464465
exit_code=$?
465466
set -e
467+
pr_url=$(printf '%s' "$pr_output" | node scripts/issue-auto-processor.cjs extract-pr-url)
466468
if [ $exit_code -ne 0 ] || ! has_nonempty_text "$pr_url"; then
467469
fail_with_comment "$number" '## 🤖 AI Fix Attempt Failed' 'Automation created and pushed a fix branch, but PR creation did not return a valid URL. Please inspect the workflow logs before retrying.'
468470
return 0
469471
fi
470472
fi
471473
472-
write_issue_comment_file '## 🤖 AI Fix Attempt' "I created a PR for this bug: $pr_url\n\nPlease review the generated changes before merging." ''
474+
local comment_body=""
475+
comment_body=$(printf 'I created a PR for this bug: %s\n\nPlease review the generated changes before merging.' "$pr_url")
476+
write_issue_comment_file '## 🤖 AI Fix Attempt' "$comment_body" ''
473477
post_file_comment "$number" /tmp/issue-comment.md
474478
update_issue_labels "$number" remove:ai-processing add:ai-processed add:ai-fix remove:ai-failed
475479
cleanup_repo

scripts/issue-auto-processor.cjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const BUG_TEXT_PATTERNS = [
1111
/\b(bug|error|errors|crash|crashes|broken|breaks|fail|fails|failed|failing|not working)\b/i,
1212
/||||||||||/,
1313
];
14+
const GITHUB_PULL_REQUEST_URL_PATTERN = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+/i;
1415

1516
function normalizeMultilineText(value) {
1617
if (typeof value !== 'string') {
@@ -154,6 +155,16 @@ function extractResultText(rawOutput) {
154155
return normalizeMultilineText(rawOutput);
155156
}
156157

158+
function extractPullRequestUrl(rawOutput) {
159+
const normalized = normalizeMultilineText(rawOutput);
160+
if (!normalized) {
161+
return '';
162+
}
163+
164+
const match = normalized.match(GITHUB_PULL_REQUEST_URL_PATTERN);
165+
return match ? match[0] : '';
166+
}
167+
157168
function parseIssueCommentCommand({ body, authorAssociation, hasPullRequest }) {
158169
if (hasPullRequest) {
159170
return null;
@@ -293,6 +304,12 @@ function runCli() {
293304
return;
294305
}
295306

307+
if (command === 'extract-pr-url') {
308+
const rawOutput = fs.readFileSync(0, 'utf8');
309+
process.stdout.write(extractPullRequestUrl(rawOutput));
310+
return;
311+
}
312+
296313
const issueFile = process.argv[3];
297314
if (!issueFile) {
298315
throw new Error(`Missing issue JSON file for command: ${command}`);
@@ -327,6 +344,7 @@ module.exports = {
327344
buildAnalysisPrompt,
328345
buildBugPrompt,
329346
extractResultText,
347+
extractPullRequestUrl,
330348
isBugIssue,
331349
parseIssueCommentCommand,
332350
};

tests/issue-auto-processor.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import issueAutoProcessorHelpers from '../scripts/issue-auto-processor.cjs';
66

77
const {
88
extractResultText,
9+
extractPullRequestUrl,
910
parseIssueCommentCommand,
1011
buildAnalysisPrompt,
1112
isBugIssue,
@@ -41,6 +42,24 @@ test('extractResultText falls back to plain text output', () => {
4142
expect(extractResultText('plain text output')).toBe('plain text output');
4243
});
4344

45+
test('extractPullRequestUrl returns the PR URL from plain gh output', () => {
46+
expect(
47+
extractPullRequestUrl('https://github.com/TencentCloudBase/CloudBase-MCP/pull/499'),
48+
).toBe('https://github.com/TencentCloudBase/CloudBase-MCP/pull/499');
49+
});
50+
51+
test('extractPullRequestUrl finds the first PR URL in noisy gh output', () => {
52+
const output = [
53+
'warning: something noisy on stderr',
54+
'https://github.com/TencentCloudBase/CloudBase-MCP/pull/499',
55+
'created pull request successfully',
56+
].join('\n');
57+
58+
expect(extractPullRequestUrl(output)).toBe(
59+
'https://github.com/TencentCloudBase/CloudBase-MCP/pull/499',
60+
);
61+
});
62+
4463
test('parseIssueCommentCommand only accepts maintainer slash commands', () => {
4564
expect(
4665
parseIssueCommentCommand({
@@ -119,6 +138,9 @@ test('workflow hardens fix path with git identity and PR creation guards', () =>
119138
expect(raw).toContain('git config user.email "41898282+github-actions[bot]@users.noreply.github.com"');
120139
expect(raw).toContain("fail_with_comment \"$number\" '## 🤖 AI Fix Attempt Failed' 'Automation created a branch diff, but git commit failed before a PR could be opened. Please inspect the workflow logs before retrying.'");
121140
expect(raw).toContain("fail_with_comment \"$number\" '## 🤖 AI Fix Attempt Failed' 'Automation created and pushed a fix branch, but PR creation did not return a valid URL. Please inspect the workflow logs before retrying.'");
141+
expect(raw).toContain('pr_output=$(gh pr create --base "$DEFAULT_BRANCH" --head "$branch" --title "fix: 🤖 attempt fix for issue #$number" --body-file /tmp/pr-body.md 2>&1)');
142+
expect(raw).toContain("pr_url=$(printf '%s' \"$pr_output\" | node scripts/issue-auto-processor.cjs extract-pr-url)");
143+
expect(raw).not.toContain('I created a PR for this bug: $pr_url\\n\\nPlease review the generated changes before merging.');
122144
});
123145

124146
test('workflow creates fix branches from the default branch baseline', () => {
@@ -130,6 +152,12 @@ test('workflow creates fix branches from the default branch baseline', () => {
130152
expect(raw).toContain('gh pr create --base "$DEFAULT_BRANCH" --head "$branch"');
131153
});
132154

155+
test('workflow scheduled collection does not retry ai-failed issues automatically', () => {
156+
const raw = fs.readFileSync(WORKFLOW_FILE, 'utf8');
157+
158+
expect(raw).toContain("!labels.includes('ai-failed')");
159+
});
160+
133161
test('workflow isolates batch iteration from CLI stdin consumption', () => {
134162
const raw = fs.readFileSync(WORKFLOW_FILE, 'utf8');
135163

0 commit comments

Comments
 (0)