Skip to content

Auto-Merge: auto/line-basic/bokeh #358

Auto-Merge: auto/line-basic/bokeh

Auto-Merge: auto/line-basic/bokeh #358

Workflow file for this run

name: "Bot: Auto-Merge"
run-name: "Auto-Merge: ${{ github.event.inputs.pr_number || github.event.pull_request.head.ref || 'push' }}"
on:
pull_request:
types: [labeled, closed]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to auto-merge'
required: true
type: number
push:
branches: [main]
paths:
- 'plots/**'
jobs:
# ============================================================================
# Job 1: Enable auto-merge when ai-approved label is added or dispatched
# ============================================================================
auto-merge:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
- name: Check conditions
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Handle workflow_dispatch trigger
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
PR_NUM="${{ github.event.inputs.pr_number }}"
PR_DATA=$(gh pr view "$PR_NUM" --repo ${{ github.repository }} --json headRefName,labels)
BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName')
HAS_APPROVED=$(echo "$PR_DATA" | jq -r '.labels[].name' | grep -c "ai-approved" || echo "0")
if [[ ! "$BRANCH" =~ ^auto/ ]]; then
echo "::notice::Skipping: Branch '$BRANCH' is not auto/*"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ "$HAS_APPROVED" == "0" ]]; then
echo "::notice::Skipping: PR does not have ai-approved label"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "pr_number=$PR_NUM" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
echo "should_run=true" >> $GITHUB_OUTPUT
exit 0
fi
# Handle pull_request labeled trigger
ACTION="${{ github.event.action }}"
LABEL="${{ github.event.label.name }}"
BRANCH="${{ github.event.pull_request.head.ref }}"
if [[ "$ACTION" != "labeled" ]]; then
echo "::notice::Skipping: Action is '$ACTION', not 'labeled'"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ "$LABEL" != "ai-approved" ]]; then
echo "::notice::Skipping: Label is '$LABEL', not 'ai-approved'"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ ! "$BRANCH" =~ ^auto/ ]]; then
echo "::notice::Skipping: Branch '$BRANCH' is not auto/*"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
echo "should_run=true" >> $GITHUB_OUTPUT
- name: Checkout repository
if: steps.check.outputs.should_run == 'true'
uses: actions/checkout@v6
- name: Extract info from branch
if: steps.check.outputs.should_run == 'true'
id: extract
run: |
BRANCH="${{ steps.check.outputs.branch }}"
# Format: auto/{spec-id}/{library}
SPEC_ID=$(echo "$BRANCH" | cut -d'/' -f2)
LIBRARY=$(echo "$BRANCH" | cut -d'/' -f3)
echo "spec_id=$SPEC_ID" >> $GITHUB_OUTPUT
echo "library=$LIBRARY" >> $GITHUB_OUTPUT
- name: Get sub-issue from PR body
if: steps.check.outputs.should_run == 'true'
id: sub_issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUM="${{ steps.check.outputs.pr_number }}"
PR_BODY=$(gh pr view "$PR_NUM" --json body -q '.body' 2>/dev/null || echo "")
SUB_ISSUE=$(echo "$PR_BODY" | grep -oP '\*\*Sub-Issue:\*\* #\K\d+' | head -1 || echo "")
echo "number=$SUB_ISSUE" >> $GITHUB_OUTPUT
- name: React with rocket emoji
if: steps.check.outputs.should_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api repos/${{ github.repository }}/issues/${{ steps.check.outputs.pr_number }}/reactions \
-f content=rocket
- name: Update sub-issue label
if: steps.check.outputs.should_run == 'true' && steps.sub_issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue edit ${{ steps.sub_issue.outputs.number }} \
--remove-label "reviewing" \
--remove-label "ai-rejected" \
--add-label "ai-approved"
- name: Merge PR directly
if: steps.check.outputs.should_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr merge ${{ steps.check.outputs.pr_number }} \
--repo ${{ github.repository }} \
--squash \
--delete-branch
- name: Close sub-issue after merge
if: steps.check.outputs.should_run == 'true' && steps.sub_issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SUB_ISSUE="${{ steps.sub_issue.outputs.number }}"
LIBRARY="${{ steps.extract.outputs.library }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
PR_NUM="${{ steps.check.outputs.pr_number }}"
echo "Closing sub-issue #$SUB_ISSUE after merge"
# Update labels
gh issue edit $SUB_ISSUE \
--remove-label "ai-approved" \
--remove-label "reviewing" \
--remove-label "generating" \
--remove-label "testing" \
--add-label "merged" 2>/dev/null || true
# Post completion comment (only if not already posted)
EXISTING_MERGED=$(gh issue view $SUB_ISSUE --json comments -q '[.comments[].body | select(startswith("## Merged"))] | length' 2>/dev/null || echo "0")
if [ "$EXISTING_MERGED" = "0" ]; then
gh issue comment $SUB_ISSUE --body "## Merged
**$LIBRARY** implementation for \`$SPEC_ID\` has been merged!
- **PR:** #$PR_NUM
---
:rocket: *Auto-merged by pyplots CI*"
fi
# Close sub-issue (ignore if already closed)
gh issue close $SUB_ISSUE --reason completed 2>/dev/null || true
echo "Sub-issue #$SUB_ISSUE closed"
- name: Get main issue number
if: steps.check.outputs.should_run == 'true'
id: main_issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUM="${{ steps.check.outputs.pr_number }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
PR_BODY=$(gh pr view "$PR_NUM" --json body -q '.body' 2>/dev/null || echo "")
# Extract main issue from PR body
MAIN_ISSUE=$(echo "$PR_BODY" | grep -oP '\*\*Parent Issue:\*\* #\K\d+' | head -1 || echo "")
# Fallback: search for main issue
if [ -z "$MAIN_ISSUE" ]; then
MAIN_ISSUE=$(gh issue list --label plot-request --search "$SPEC_ID in:title" --json number -q '.[0].number' 2>/dev/null || echo "")
fi
echo "number=$MAIN_ISSUE" >> $GITHUB_OUTPUT
echo "Main issue: #$MAIN_ISSUE"
- name: Check if all libraries are done
if: steps.check.outputs.should_run == 'true' && steps.main_issue.outputs.number != ''
id: check_complete
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MAIN_ISSUE="${{ steps.main_issue.outputs.number }}"
# Get all sub-issues for this main issue
SUB_ISSUES=$(gh issue list --label "plot-request:impl" --search "Parent Issue.*#$MAIN_ISSUE" --state all --json number,labels -q '.' 2>/dev/null || echo "[]")
# Count statuses (use 'any' to avoid double-counting issues with multiple labels)
TOTAL=$(echo "$SUB_ISSUES" | jq 'length')
MERGED=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "merged"))] | length')
NOT_FEASIBLE=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "not-feasible"))] | length')
DONE=$((MERGED + NOT_FEASIBLE))
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "merged=$MERGED" >> $GITHUB_OUTPUT
echo "not_feasible=$NOT_FEASIBLE" >> $GITHUB_OUTPUT
if [ "$DONE" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
echo "all_done=true" >> $GITHUB_OUTPUT
else
echo "all_done=false" >> $GITHUB_OUTPUT
fi
echo "Progress: $DONE/$TOTAL complete ($MERGED merged, $NOT_FEASIBLE not feasible)"
- name: Create and merge feature-to-main PR
if: steps.check.outputs.should_run == 'true' && steps.check_complete.outputs.all_done == 'true' && steps.main_issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
MAIN_ISSUE="${{ steps.main_issue.outputs.number }}"
MERGED="${{ steps.check_complete.outputs.merged }}"
NOT_FEASIBLE="${{ steps.check_complete.outputs.not_feasible }}"
# Check if main issue already completed
LABELS=$(gh issue view "$MAIN_ISSUE" --json labels -q '.labels[].name' 2>/dev/null || echo "")
if echo "$LABELS" | grep -q "completed"; then
echo "Main issue #$MAIN_ISSUE already completed"
exit 0
fi
# Check if feature branch exists
if ! gh api repos/${{ github.repository }}/branches/plot/$SPEC_ID &>/dev/null; then
echo "Feature branch plot/$SPEC_ID does not exist, skipping"
exit 0
fi
# Check if PR already exists
EXISTING_PR=$(gh pr list --base main --head "plot/$SPEC_ID" --json number -q '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists for plot/$SPEC_ID -> main"
gh pr merge "$EXISTING_PR" --squash --delete-branch --auto 2>/dev/null || true
FEATURE_PR="$EXISTING_PR"
else
# Create PR from feature branch to main
FEATURE_PR=$(gh pr create \
--base main \
--head "plot/$SPEC_ID" \
--title "feat: add $SPEC_ID implementation ($MERGED libraries)" \
--body "## Summary
Adds \`$SPEC_ID\` plot implementation.
### Libraries
- **Merged:** $MERGED
- **Not Feasible:** $NOT_FEASIBLE
### Links
- **Spec:** \`specs/$SPEC_ID.md\`
- **Parent Issue:** #$MAIN_ISSUE (closes on merge)
---
:robot: *Auto-generated by pyplots CI*" | sed 's|.*/||')
echo "Created PR #$FEATURE_PR"
# Enable auto-merge
gh pr merge "$FEATURE_PR" --squash --delete-branch --auto 2>/dev/null || true
fi
# Post completion to main issue
if [ "$NOT_FEASIBLE" -gt 0 ]; then
STATUS_MSG="$MERGED libraries ready, $NOT_FEASIBLE not feasible"
else
STATUS_MSG="All $MERGED libraries ready"
fi
gh issue comment "$MAIN_ISSUE" --body "## :tada: All Libraries Complete!
**Status:** $STATUS_MSG
**Feature PR:** #$FEATURE_PR (auto-merge enabled)
---
:rocket: *pyplots CI*"
# Add completed label
gh issue edit "$MAIN_ISSUE" --add-label "completed" 2>/dev/null || true
# ============================================================================
# Job 2: Post-merge summary (per library) - only for PR close events
# ============================================================================
post-merge-summary:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: read
steps:
- name: Check conditions
id: check
run: |
ACTION="${{ github.event.action }}"
MERGED="${{ github.event.pull_request.merged }}"
BRANCH="${{ github.event.pull_request.head.ref }}"
if [[ "$ACTION" != "closed" ]]; then
echo "::notice::Skipping: Action is '$ACTION', not 'closed'"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ "$MERGED" != "true" ]]; then
echo "::notice::Skipping: PR was closed but not merged"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ ! "$BRANCH" =~ ^auto/ ]]; then
echo "::notice::Skipping: Branch '$BRANCH' is not auto/*"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "should_run=true" >> $GITHUB_OUTPUT
- name: Checkout code
if: steps.check.outputs.should_run == 'true'
uses: actions/checkout@v6
- name: Extract spec ID, library, and issues
if: steps.check.outputs.should_run == 'true'
id: extract
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
PR_NUM="${{ github.event.pull_request.number }}"
PR_BODY=$(gh pr view "$PR_NUM" --json body -q '.body' 2>/dev/null || echo "")
# Format: auto/{spec-id}/{library}
SPEC_ID=$(echo "$BRANCH" | cut -d'/' -f2)
LIBRARY=$(echo "$BRANCH" | cut -d'/' -f3)
# Extract issues from PR body
SUB_ISSUE=$(echo "$PR_BODY" | grep -oP '\*\*Sub-Issue:\*\* #\K\d+' | head -1 || echo "")
MAIN_ISSUE=$(echo "$PR_BODY" | grep -oP '\*\*Parent Issue:\*\* #\K\d+' | head -1 || echo "")
# Fallback: search for main issue
if [ -z "$MAIN_ISSUE" ]; then
MAIN_ISSUE=$(gh issue list --label plot-request --search "$SPEC_ID in:title" --json number -q '.[0].number' || echo "")
fi
echo "spec_id=$SPEC_ID" >> $GITHUB_OUTPUT
echo "library=$LIBRARY" >> $GITHUB_OUTPUT
echo "sub_issue=$SUB_ISSUE" >> $GITHUB_OUTPUT
echo "main_issue=$MAIN_ISSUE" >> $GITHUB_OUTPUT
echo "Spec: $SPEC_ID, Library: $LIBRARY, Sub: #$SUB_ISSUE, Main: #$MAIN_ISSUE"
- name: Find implementation file
if: steps.check.outputs.should_run == 'true'
id: files
run: |
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
LIBRARY="${{ steps.extract.outputs.library }}"
# Find implementation file for this library
IMPL_FILE=$(find plots/$LIBRARY -name "default.py" -path "*/$SPEC_ID/*" 2>/dev/null | head -1 || echo "")
echo "impl_file=$IMPL_FILE" >> $GITHUB_OUTPUT
- name: Update sub-issue to merged
if: steps.check.outputs.should_run == 'true' && steps.extract.outputs.sub_issue != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SUB_ISSUE="${{ steps.extract.outputs.sub_issue }}"
LIBRARY="${{ steps.extract.outputs.library }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
PR_NUM="${{ github.event.pull_request.number }}"
# Check if already merged (skip if so)
LABELS=$(gh issue view "$SUB_ISSUE" --json labels -q '.labels[].name' 2>/dev/null || echo "")
if echo "$LABELS" | grep -q "merged"; then
echo "Sub-issue #$SUB_ISSUE already merged, skipping"
exit 0
fi
# Update labels
gh issue edit $SUB_ISSUE \
--remove-label "ai-approved" \
--remove-label "reviewing" \
--remove-label "generating" \
--remove-label "testing" \
--add-label "merged" 2>/dev/null || true
# Post completion comment (only if not already posted)
EXISTING_MERGED=$(gh issue view $SUB_ISSUE --json comments -q '[.comments[].body | select(startswith("## Merged"))] | length' 2>/dev/null || echo "0")
if [ "$EXISTING_MERGED" = "0" ]; then
gh issue comment $SUB_ISSUE --body "## Merged
**$LIBRARY** implementation for \`$SPEC_ID\` has been merged!
- **PR:** #$PR_NUM
---
:rocket: *Auto-merged by pyplots CI*"
fi
# Close sub-issue
gh issue close $SUB_ISSUE --reason completed 2>/dev/null || true
- name: Check if all libraries are done
if: steps.check.outputs.should_run == 'true'
id: check_complete
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MAIN_ISSUE="${{ steps.extract.outputs.main_issue }}"
if [ -z "$MAIN_ISSUE" ]; then
echo "all_done=false" >> $GITHUB_OUTPUT
exit 0
fi
# Get all sub-issues for this main issue
SUB_ISSUES=$(gh issue list --search "**Parent Issue:** #$MAIN_ISSUE in:body" --json number,labels -q '.')
# Count statuses (use 'any' to avoid double-counting issues with multiple labels)
TOTAL=$(echo "$SUB_ISSUES" | jq 'length')
MERGED=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "merged"))] | length')
NOT_FEASIBLE=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "not-feasible"))] | length')
DONE=$((MERGED + NOT_FEASIBLE))
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "merged=$MERGED" >> $GITHUB_OUTPUT
echo "not_feasible=$NOT_FEASIBLE" >> $GITHUB_OUTPUT
if [ "$DONE" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
echo "all_done=true" >> $GITHUB_OUTPUT
else
echo "all_done=false" >> $GITHUB_OUTPUT
fi
echo "Progress: $DONE/$TOTAL complete ($MERGED merged, $NOT_FEASIBLE not feasible)"
- name: Post progress to main issue
if: steps.check.outputs.should_run == 'true' && steps.extract.outputs.main_issue != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MAIN_ISSUE="${{ steps.extract.outputs.main_issue }}"
LIBRARY="${{ steps.extract.outputs.library }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
PR_NUM="${{ github.event.pull_request.number }}"
MERGED="${{ steps.check_complete.outputs.merged }}"
NOT_FEASIBLE="${{ steps.check_complete.outputs.not_feasible }}"
TOTAL="${{ steps.check_complete.outputs.total }}"
gh issue comment $MAIN_ISSUE --body "## :white_check_mark: $LIBRARY Merged
**$LIBRARY** implementation for \`$SPEC_ID\` has been merged!
**Progress:** $MERGED merged, $NOT_FEASIBLE not feasible, $((TOTAL - MERGED - NOT_FEASIBLE)) pending
---
:robot: *PR #$PR_NUM*"
- name: Create and merge feature-to-main PR
if: steps.check.outputs.should_run == 'true' && steps.check_complete.outputs.all_done == 'true' && steps.extract.outputs.main_issue != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
MAIN_ISSUE="${{ steps.extract.outputs.main_issue }}"
MERGED="${{ steps.check_complete.outputs.merged }}"
NOT_FEASIBLE="${{ steps.check_complete.outputs.not_feasible }}"
# Check if feature branch exists
if ! gh api repos/${{ github.repository }}/branches/plot/$SPEC_ID &>/dev/null; then
echo "Feature branch plot/$SPEC_ID does not exist, skipping"
exit 0
fi
# Check if PR already exists
EXISTING_PR=$(gh pr list --base main --head "plot/$SPEC_ID" --json number -q '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists for plot/$SPEC_ID -> main"
if gh pr merge "$EXISTING_PR" --squash --delete-branch; then
FEATURE_PR="$EXISTING_PR"
else
echo "::error::Failed to merge existing PR #$EXISTING_PR"
exit 1
fi
else
# Create and merge feature PR using reusable script
.github/scripts/create-feature-pr.sh "$SPEC_ID" "$MAIN_ISSUE" "$MERGED" "$NOT_FEASIBLE"
fi
# Post completion to main issue
gh issue comment "$MAIN_ISSUE" --body "## :tada: All Complete!
All library implementations for \`$SPEC_ID\` have been merged to main!
### Final Status
- **Merged:** $MERGED libraries
- **Not Feasible:** $NOT_FEASIBLE libraries
- **Feature PR:** #$FEATURE_PR
---
:rocket: *pyplots CI*"
# Add completed label and close
gh issue edit "$MAIN_ISSUE" --add-label "completed"
gh issue close "$MAIN_ISSUE" --reason completed
# ============================================================================
# Job 3: Handle push-triggered post-merge summary
# ============================================================================
push-post-merge:
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: Extract info from commit
id: extract
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Get the commit message
COMMIT_MSG=$(git log -1 --format='%s')
COMMIT_SHA="${{ github.sha }}"
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
echo "commit_sha=$COMMIT_SHA" >> $GITHUB_OUTPUT
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
# Try to extract library and spec from commit message
# Format: feat(library): implement spec-id (#PR)
if [[ "$COMMIT_MSG" =~ feat\(([a-z]+)\):.*implement[[:space:]]+([a-z0-9-]+) ]]; then
LIBRARY="${BASH_REMATCH[1]}"
SPEC_ID="${BASH_REMATCH[2]}"
echo "library=$LIBRARY" >> $GITHUB_OUTPUT
echo "spec_id=$SPEC_ID" >> $GITHUB_OUTPUT
echo "found=true" >> $GITHUB_OUTPUT
echo "Found: Library=$LIBRARY, Spec=$SPEC_ID"
else
echo "found=false" >> $GITHUB_OUTPUT
echo "Could not extract library/spec from commit: $COMMIT_MSG"
fi
- name: Find and update sub-issue
if: steps.extract.outputs.found == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
LIBRARY="${{ steps.extract.outputs.library }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
COMMIT_SHA="${{ steps.extract.outputs.commit_sha }}"
SHORT_SHA="${{ steps.extract.outputs.short_sha }}"
# Find sub-issue for this library/spec
SUB_ISSUE=$(gh issue list --label "library:$LIBRARY" --search "[$SPEC_ID] in:title" --json number,labels -q '.[0].number' 2>/dev/null || echo "")
if [ -z "$SUB_ISSUE" ]; then
echo "No sub-issue found for $LIBRARY/$SPEC_ID"
exit 0
fi
# Check if already merged
LABELS=$(gh issue view "$SUB_ISSUE" --json labels -q '.labels[].name' 2>/dev/null || echo "")
if echo "$LABELS" | grep -q "merged"; then
echo "Sub-issue #$SUB_ISSUE already has merged label"
exit 0
fi
# Find implementation file
IMPL_FILE=$(find plots/$LIBRARY -name "default.py" -path "*/${SPEC_ID}/*" 2>/dev/null | head -1 || echo "")
# Update labels
gh issue edit "$SUB_ISSUE" \
--remove-label "ai-approved" \
--remove-label "reviewing" \
--remove-label "generating" \
--remove-label "testing" \
--add-label "merged" 2>/dev/null || true
# Post completion comment
gh issue comment "$SUB_ISSUE" --body "## Merged
**$LIBRARY** implementation for \`$SPEC_ID\` has been merged to main!
### Details
- **Commit:** [$SHORT_SHA](https://github.com/${{ github.repository }}/commit/$COMMIT_SHA)
- **File:** \`$IMPL_FILE\`
---
:rocket: *Auto-merged by pyplots CI (push trigger)*"
# Close sub-issue
gh issue close "$SUB_ISSUE" --reason completed
echo "Updated sub-issue #$SUB_ISSUE"
- name: Check if all libraries are done
if: steps.extract.outputs.found == 'true'
id: check_complete
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
# Find main issue
MAIN_ISSUE=$(gh issue list --label plot-request --search "$SPEC_ID in:title" --json number -q '.[0].number' 2>/dev/null || echo "")
if [ -z "$MAIN_ISSUE" ]; then
echo "all_done=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "main_issue=$MAIN_ISSUE" >> $GITHUB_OUTPUT
# Get all sub-issues for this main issue
SUB_ISSUES=$(gh issue list --search "**Parent Issue:** #$MAIN_ISSUE in:body" --json number,labels -q '.' 2>/dev/null || echo "[]")
# Count statuses (use 'any' to avoid double-counting issues with multiple labels)
TOTAL=$(echo "$SUB_ISSUES" | jq 'length')
MERGED=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "merged"))] | length')
NOT_FEASIBLE=$(echo "$SUB_ISSUES" | jq '[.[] | select(any(.labels[]; .name == "not-feasible"))] | length')
DONE=$((MERGED + NOT_FEASIBLE))
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "merged=$MERGED" >> $GITHUB_OUTPUT
echo "not_feasible=$NOT_FEASIBLE" >> $GITHUB_OUTPUT
if [ "$DONE" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
echo "all_done=true" >> $GITHUB_OUTPUT
else
echo "all_done=false" >> $GITHUB_OUTPUT
fi
echo "Progress: $DONE/$TOTAL complete ($MERGED merged, $NOT_FEASIBLE not feasible)"
- name: Create and merge feature-to-main PR
if: steps.extract.outputs.found == 'true' && steps.check_complete.outputs.all_done == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MAIN_ISSUE="${{ steps.check_complete.outputs.main_issue }}"
SPEC_ID="${{ steps.extract.outputs.spec_id }}"
MERGED="${{ steps.check_complete.outputs.merged }}"
NOT_FEASIBLE="${{ steps.check_complete.outputs.not_feasible }}"
# Check if already completed
LABELS=$(gh issue view "$MAIN_ISSUE" --json labels -q '.labels[].name' 2>/dev/null || echo "")
if echo "$LABELS" | grep -q "completed"; then
echo "Main issue #$MAIN_ISSUE already completed"
exit 0
fi
# Check if feature branch exists
if ! gh api repos/${{ github.repository }}/branches/plot/$SPEC_ID &>/dev/null; then
echo "Feature branch plot/$SPEC_ID does not exist, skipping"
exit 0
fi
# Check if PR already exists
EXISTING_PR=$(gh pr list --base main --head "plot/$SPEC_ID" --json number -q '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists for plot/$SPEC_ID -> main"
if gh pr merge "$EXISTING_PR" --squash --delete-branch; then
FEATURE_PR="$EXISTING_PR"
else
echo "::error::Failed to merge existing PR #$EXISTING_PR"
exit 1
fi
else
# Create PR from feature branch to main
PR_URL=$(gh pr create \
--base main \
--head "plot/$SPEC_ID" \
--title "feat: add $SPEC_ID implementation ($MERGED libraries)" \
--body "## Summary
Adds \`$SPEC_ID\` plot implementation.
### Libraries
- **Merged:** $MERGED
- **Not Feasible:** $NOT_FEASIBLE
### Links
- **Spec:** \`specs/$SPEC_ID.md\`
- **Parent Issue:** #$MAIN_ISSUE
---
:robot: *Auto-generated by pyplots CI*")
FEATURE_PR=$(echo "$PR_URL" | sed 's|.*/||')
echo "Created PR #$FEATURE_PR"
# Merge the PR immediately
if ! gh pr merge "$FEATURE_PR" --squash --delete-branch; then
echo "::error::Failed to merge PR #$FEATURE_PR"
gh issue comment "$MAIN_ISSUE" --body "## :warning: Merge Failed
Failed to automatically merge PR #$FEATURE_PR. Please merge manually."
exit 1
fi
fi
# Post completion to main issue
gh issue comment "$MAIN_ISSUE" --body "## :tada: All Complete!
All library implementations for \`$SPEC_ID\` have been merged to main!
### Final Status
- **Merged:** $MERGED libraries
- **Not Feasible:** $NOT_FEASIBLE libraries
- **Feature PR:** #$FEATURE_PR
---
:rocket: *pyplots CI (push trigger)*"
# Add completed label and close
gh issue edit "$MAIN_ISSUE" --add-label "completed"
gh issue close "$MAIN_ISSUE" --reason completed