Skip to content

Commit 4d2694f

Browse files
natifridmanclaude
andcommitted
fix(amber): Push to existing PR instead of creating duplicates
When an issue already has an open PR, Amber now: - Detects the existing PR via gh pr list search - Checks out the existing branch instead of creating a new one - Pushes additional commits to the existing PR - Adds comments to both PR and issue about the update This prevents duplicate PRs like ambient-code#450, ambient-code#442, ambient-code#441, ambient-code#438. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent cd9577b commit 4d2694f

File tree

1 file changed

+104
-19
lines changed

1 file changed

+104
-19
lines changed

.github/workflows/amber-issue-handler.yml

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,70 @@ jobs:
180180
181181
echo "prompt_file=amber-prompt.md" >> $GITHUB_OUTPUT
182182
183-
- name: Create feature branch
183+
- name: Check for existing PR
184+
id: check-existing-pr
185+
env:
186+
ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }}
187+
GH_TOKEN: ${{ github.token }}
188+
run: |
189+
# Check if there's already an open PR for this issue
190+
EXISTING_PR=$(gh pr list --state open --search "Closes #${ISSUE_NUMBER}" --json number,headRefName --jq '.[0]' 2>/dev/null || echo "")
191+
192+
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
193+
PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number')
194+
EXISTING_BRANCH=$(echo "$EXISTING_PR" | jq -r '.headRefName')
195+
echo "existing_pr=true" >> $GITHUB_OUTPUT
196+
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
197+
echo "existing_branch=$EXISTING_BRANCH" >> $GITHUB_OUTPUT
198+
echo "Found existing PR #$PR_NUMBER on branch $EXISTING_BRANCH"
199+
else
200+
echo "existing_pr=false" >> $GITHUB_OUTPUT
201+
echo "No existing PR found for issue #${ISSUE_NUMBER}"
202+
fi
203+
204+
- name: Create or checkout feature branch
184205
id: create-branch
185206
env:
186207
ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }}
187208
ISSUE_TITLE: ${{ steps.issue-details.outputs.issue_title }}
209+
EXISTING_PR: ${{ steps.check-existing-pr.outputs.existing_pr }}
210+
EXISTING_BRANCH: ${{ steps.check-existing-pr.outputs.existing_branch }}
188211
run: |
189-
# Improved sanitization (Issue #10) - handles special chars, spaces, consecutive dashes
190-
SANITIZED_TITLE=$(echo "$ISSUE_TITLE" \
191-
| tr '[:upper:]' '[:lower:]' \
192-
| sed 's/[^a-z0-9-]/-/g' \
193-
| sed 's/--*/-/g' \
194-
| sed 's/^-//' \
195-
| sed 's/-$//' \
196-
| cut -c1-50)
197-
198-
BRANCH_NAME="amber/issue-${ISSUE_NUMBER}-${SANITIZED_TITLE}"
199-
200212
git config user.name "Amber Agent"
201213
git config user.email "amber@ambient-code.ai"
202-
git checkout -b "$BRANCH_NAME"
214+
215+
if [ "$EXISTING_PR" == "true" ] && [ -n "$EXISTING_BRANCH" ]; then
216+
# Checkout existing branch and pull latest changes
217+
echo "Checking out existing branch: $EXISTING_BRANCH"
218+
git fetch origin "$EXISTING_BRANCH"
219+
git checkout -B "$EXISTING_BRANCH" "origin/$EXISTING_BRANCH"
220+
BRANCH_NAME="$EXISTING_BRANCH"
221+
else
222+
# Create new branch with sanitized title
223+
# Improved sanitization (Issue #10) - handles special chars, spaces, consecutive dashes
224+
SANITIZED_TITLE=$(echo "$ISSUE_TITLE" \
225+
| tr '[:upper:]' '[:lower:]' \
226+
| sed 's/[^a-z0-9-]/-/g' \
227+
| sed 's/--*/-/g' \
228+
| sed 's/^-//' \
229+
| sed 's/-$//' \
230+
| cut -c1-50)
231+
232+
BRANCH_NAME="amber/issue-${ISSUE_NUMBER}-${SANITIZED_TITLE}"
233+
234+
# Check if branch exists remotely
235+
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
236+
echo "Branch $BRANCH_NAME exists remotely, checking out"
237+
git fetch origin "$BRANCH_NAME"
238+
git checkout -B "$BRANCH_NAME" "origin/$BRANCH_NAME"
239+
else
240+
echo "Creating new branch: $BRANCH_NAME"
241+
git checkout -b "$BRANCH_NAME"
242+
fi
243+
fi
203244
204245
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
205-
echo "Created branch: $BRANCH_NAME"
246+
echo "Using branch: $BRANCH_NAME"
206247
207248
- name: Read prompt file
208249
id: read-prompt
@@ -297,6 +338,8 @@ jobs:
297338
RUN_ID: ${{ github.run_id }}
298339
GITHUB_SERVER_URL: ${{ github.server_url }}
299340
GITHUB_REPOSITORY: ${{ github.repository }}
341+
EXISTING_PR: ${{ steps.check-existing-pr.outputs.existing_pr }}
342+
EXISTING_PR_NUMBER: ${{ steps.check-existing-pr.outputs.pr_number }}
300343
uses: actions/github-script@v8
301344
with:
302345
script: |
@@ -308,18 +351,24 @@ jobs:
308351
const runId = process.env.RUN_ID;
309352
const serverUrl = process.env.GITHUB_SERVER_URL;
310353
const repository = process.env.GITHUB_REPOSITORY;
354+
const existingPr = process.env.EXISTING_PR === 'true';
355+
const existingPrNumber = process.env.EXISTING_PR_NUMBER;
311356
312357
// Safely get git diff (no shell injection risk with execFile)
313358
const { stdout: diff } = await execFileAsync('git', ['diff', 'HEAD~1', '--stat']);
314359
360+
const nextSteps = existingPr
361+
? `- Review that changes match the issue description\n- Verify no scope creep or unintended modifications\n- Changes pushed to existing PR #${existingPrNumber}`
362+
: `- Review that changes match the issue description\n- Verify no scope creep or unintended modifications\n- A PR will be created shortly for formal review`;
363+
315364
await github.rest.issues.createComment({
316365
owner: context.repo.owner,
317366
repo: context.repo.repo,
318367
issue_number: issueNumber,
319-
body: `## Amber Change Summary\n\nThe following files were modified:\n\n\`\`\`\n${diff}\n\`\`\`\n\n**Next Steps:**\n- Review that changes match the issue description\n- Verify no scope creep or unintended modifications\n- A PR will be created shortly for formal review\n\n---\n🔍 [View AI decision process](${serverUrl}/${repository}/actions/runs/${runId}) (logs available for 90 days)`
368+
body: `## Amber Change Summary\n\nThe following files were modified:\n\n\`\`\`\n${diff}\n\`\`\`\n\n**Next Steps:**\n${nextSteps}\n\n---\n🔍 [View AI decision process](${serverUrl}/${repository}/actions/runs/${runId}) (logs available for 90 days)`
320369
});
321370
322-
- name: Create Pull Request
371+
- name: Create or Update Pull Request
323372
if: steps.check-changes.outputs.has_changes == 'true'
324373
env:
325374
BRANCH_NAME: ${{ steps.check-changes.outputs.branch_name }}
@@ -330,6 +379,8 @@ jobs:
330379
GITHUB_REPOSITORY: ${{ github.repository }}
331380
RUN_ID: ${{ github.run_id }}
332381
GITHUB_SERVER_URL: ${{ github.server_url }}
382+
EXISTING_PR: ${{ steps.check-existing-pr.outputs.existing_pr }}
383+
EXISTING_PR_NUMBER: ${{ steps.check-existing-pr.outputs.pr_number }}
333384
uses: actions/github-script@v8
334385
with:
335386
script: |
@@ -341,6 +392,8 @@ jobs:
341392
const repository = process.env.GITHUB_REPOSITORY;
342393
const runId = process.env.RUN_ID;
343394
const serverUrl = process.env.GITHUB_SERVER_URL;
395+
const existingPr = process.env.EXISTING_PR === 'true';
396+
const existingPrNumber = process.env.EXISTING_PR_NUMBER ? parseInt(process.env.EXISTING_PR_NUMBER) : null;
344397
345398
// Helper function for retrying API calls with exponential backoff
346399
// Retries on: 5xx errors, network errors (no status), JSON parse errors
@@ -368,8 +421,40 @@ jobs:
368421
throw new Error('retryWithBackoff: max retries exceeded');
369422
}
370423
371-
// Create PR with error handling (Issue #3)
372424
try {
425+
// If PR already exists, just add a comment about the new push
426+
if (existingPr && existingPrNumber) {
427+
console.log(`PR #${existingPrNumber} already exists, adding update comment`);
428+
429+
// Add comment to PR about the new commit
430+
await github.rest.issues.createComment({
431+
owner: context.repo.owner,
432+
repo: context.repo.repo,
433+
issue_number: existingPrNumber,
434+
body: `🤖 **Amber pushed additional changes**
435+
436+
- **Commit:** ${commitSha.substring(0, 7)}
437+
- **Action Type:** ${actionType}
438+
439+
New changes have been pushed to this PR. Please review the updated code.
440+
441+
---
442+
🔍 [View AI decision process](${serverUrl}/${repository}/actions/runs/${runId}) (logs available for 90 days)`
443+
});
444+
445+
// Also notify on the issue
446+
await github.rest.issues.createComment({
447+
owner: context.repo.owner,
448+
repo: context.repo.repo,
449+
issue_number: issueNumber,
450+
body: `🤖 Amber pushed additional changes to the existing PR #${existingPrNumber}.\n\n---\n🔍 [View AI decision process](${serverUrl}/${repository}/actions/runs/${runId}) (logs available for 90 days)`
451+
});
452+
453+
console.log(`Updated existing PR #${existingPrNumber}`);
454+
return;
455+
}
456+
457+
// Create new PR
373458
const pr = await github.rest.pulls.create({
374459
owner: context.repo.owner,
375460
repo: context.repo.repo,
@@ -423,8 +508,8 @@ jobs:
423508
424509
console.log('Created PR:', pr.data.html_url);
425510
} catch (error) {
426-
console.error('Failed to create PR:', error);
427-
core.setFailed(`PR creation failed: ${error.message}`);
511+
console.error('Failed to create/update PR:', error);
512+
core.setFailed(`PR creation/update failed: ${error.message}`);
428513
429514
// Notify on issue about failure
430515
await github.rest.issues.createComment({

0 commit comments

Comments
 (0)