Skip to content

Merge: dependabot/uv/sqlalchemy-asyncio--gte-2.0.49 #4105

Merge: dependabot/uv/sqlalchemy-asyncio--gte-2.0.49

Merge: dependabot/uv/sqlalchemy-asyncio--gte-2.0.49 #4105

Workflow file for this run

name: "Impl: Merge"
run-name: "Merge: ${{ github.event.inputs.pr_number || github.event.pull_request.head.ref }}"
# Auto-merge implementation PRs when ai-approved label is added
# Creates per-library metadata file and promotes GCS images
on:
pull_request:
types: [labeled]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to auto-merge'
required: true
type: number
jobs:
merge:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: write # Required for gh workflow run sync-postgres.yml
steps:
- name: Check conditions
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
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] | any(. == "ai-approved")')
else
ACTION="${{ github.event.action }}"
LABEL="${{ github.event.label.name }}"
BRANCH="${{ github.event.pull_request.head.ref }}"
PR_NUM="${{ github.event.pull_request.number }}"
if [[ "$ACTION" != "labeled" || "$LABEL" != "ai-approved" ]]; then
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
HAS_APPROVED="true"
fi
# Only process implementation/* branches
if [[ ! "$BRANCH" =~ ^implementation/ ]]; then
echo "::notice::Skipping: Branch '$BRANCH' is not implementation/*"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ "$HAS_APPROVED" != "true" ]]; 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
- name: Checkout repository
if: steps.check.outputs.should_run == 'true'
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Extract info from branch
if: steps.check.outputs.should_run == 'true'
id: extract
run: |
BRANCH="${{ steps.check.outputs.branch }}"
# Format: implementation/{specification-id}/{library}
SPEC_ID=$(echo "$BRANCH" | cut -d'/' -f2)
LIBRARY=$(echo "$BRANCH" | cut -d'/' -f3)
echo "specification_id=$SPEC_ID" >> $GITHUB_OUTPUT
echo "library=$LIBRARY" >> $GITHUB_OUTPUT
- name: Validate PR completeness before merge
if: steps.check.outputs.should_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SPEC_ID: ${{ steps.extract.outputs.specification_id }}
LIBRARY: ${{ steps.extract.outputs.library }}
BRANCH: ${{ steps.check.outputs.branch }}
PR_NUM: ${{ steps.check.outputs.pr_number }}
run: |
# Fetch the PR branch to check its contents
git fetch origin "$BRANCH"
IMPL_FILE="plots/${SPEC_ID}/implementations/${LIBRARY}.py"
META_FILE="plots/${SPEC_ID}/metadata/${LIBRARY}.yaml"
# Check if implementation file exists on the PR branch
if ! git show "origin/${BRANCH}:${IMPL_FILE}" &>/dev/null; then
echo "::error::Implementation file missing on branch: ${IMPL_FILE}"
echo "::error::This indicates an incomplete generation - metadata exists but implementation is missing"
echo "::error::Closing PR to prevent partial merge"
gh pr close "$PR_NUM" --comment "**Merge blocked:** Implementation file \`${IMPL_FILE}\` is missing from this PR branch. Only metadata was found. This indicates an incomplete code generation. Please regenerate using \`generate:${LIBRARY}\` label."
exit 1
fi
# Check if metadata file exists
if ! git show "origin/${BRANCH}:${META_FILE}" &>/dev/null; then
echo "::warning::Metadata file missing on branch: ${META_FILE}"
echo "::warning::This is unusual but not blocking - metadata will be created on next review"
fi
echo "::notice::PR completeness validated: both implementation and metadata present"
- name: Get parent issue from PR body
if: steps.check.outputs.should_run == 'true'
id: 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 "")
ISSUE=$(echo "$PR_BODY" | grep -oP '\*\*Parent Issue:\*\* #\K\d+' | head -1 || echo "")
echo "number=$ISSUE" >> $GITHUB_OUTPUT
- name: Extract quality score from PR labels
if: steps.check.outputs.should_run == 'true'
id: quality
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUM="${{ steps.check.outputs.pr_number }}"
LABELS=$(gh pr view "$PR_NUM" --json labels -q '.labels[].name' 2>/dev/null || echo "")
SCORE=$(echo "$LABELS" | grep -oP 'quality:\K\d+' | head -1 || echo "")
echo "score=$SCORE" >> $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: Merge PR to main (with retry)
if: steps.check.outputs.should_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUM="${{ steps.check.outputs.pr_number }}"
MAX_ATTEMPTS=5
for attempt in $(seq 1 $MAX_ATTEMPTS); do
echo "::notice::Merge attempt $attempt/$MAX_ATTEMPTS"
# Update branch before merge attempt
gh pr update-branch "$PR_NUM" --repo ${{ github.repository }} 2>/dev/null || true
sleep 2
if gh pr merge "$PR_NUM" \
--repo ${{ github.repository }} \
--squash \
--delete-branch; then
echo "::notice::Merge successful on attempt $attempt"
exit 0
fi
if [ $attempt -lt $MAX_ATTEMPTS ]; then
DELAY=$((attempt * 10))
echo "::warning::Merge failed, retrying in ${DELAY}s..."
sleep $DELAY
fi
done
echo "::error::Merge failed after $MAX_ATTEMPTS attempts"
exit 1
# Note: quality_score is now set by impl-review.yml before merge
# The PR already contains the updated metadata when merged
- name: Promote GCS images to production
if: steps.check.outputs.should_run == 'true'
env:
GCS_CREDENTIALS: ${{ secrets.GCS_CREDENTIALS }}
SPEC_ID: ${{ steps.extract.outputs.specification_id }}
LIBRARY: ${{ steps.extract.outputs.library }}
run: |
if [ -z "$GCS_CREDENTIALS" ]; then
echo "::warning::GCS_CREDENTIALS not configured - skipping promotion"
exit 0
fi
echo "$GCS_CREDENTIALS" > /tmp/gcs-key.json
gcloud auth activate-service-account --key-file=/tmp/gcs-key.json
STAGING="gs://pyplots-images/staging/${SPEC_ID}/${LIBRARY}"
PRODUCTION="gs://pyplots-images/plots/${SPEC_ID}/${LIBRARY}"
# Copy from staging to production
gsutil -m -h "Cache-Control:public, max-age=604800" cp -r "${STAGING}/*" "${PRODUCTION}/" 2>/dev/null || echo "No staging files to promote"
# Make production files public
gsutil -m acl ch -r -u AllUsers:R "${PRODUCTION}/" 2>/dev/null || true
# Clean up staging
gsutil -m rm -r "${STAGING}/" 2>/dev/null || true
rm -f /tmp/gcs-key.json
echo "::notice::Promoted images to production"
- name: Update issue labels
if: steps.check.outputs.should_run == 'true' && steps.issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE: ${{ steps.issue.outputs.number }}
LIBRARY: ${{ steps.extract.outputs.library }}
run: |
# Create labels if they don't exist
gh label create "impl:${LIBRARY}:done" --color "0e8a16" --description "${LIBRARY} implementation merged" 2>/dev/null || true
# Remove trigger and pending, add done
gh issue edit "$ISSUE" --remove-label "generate:${LIBRARY}" 2>/dev/null || true
gh issue edit "$ISSUE" --remove-label "impl:${LIBRARY}:pending" 2>/dev/null || true
gh issue edit "$ISSUE" --add-label "impl:${LIBRARY}:done"
- name: Post success to issue
if: steps.check.outputs.should_run == 'true' && steps.issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE: ${{ steps.issue.outputs.number }}
LIBRARY: ${{ steps.extract.outputs.library }}
SPEC_ID: ${{ steps.extract.outputs.specification_id }}
PR_NUM: ${{ steps.check.outputs.pr_number }}
QUALITY_SCORE: ${{ steps.quality.outputs.score }}
run: |
BODY="## :white_check_mark: ${LIBRARY} Complete
**${LIBRARY}** implementation for \`${SPEC_ID}\` has been merged to main.
**PR:** #${PR_NUM}"
if [ -n "$QUALITY_SCORE" ]; then
BODY="${BODY}
**Quality Score:** ${QUALITY_SCORE}/100"
fi
BODY="${BODY}
**Preview:** [View image](https://storage.googleapis.com/pyplots-images/plots/${SPEC_ID}/${LIBRARY}/plot.png)
---
:robot: *[impl-merge](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*"
gh issue comment "$ISSUE" --body "$BODY"
- name: Close issue if all libraries done
if: steps.check.outputs.should_run == 'true' && steps.issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE: ${{ steps.issue.outputs.number }}
SPEC_ID: ${{ steps.extract.outputs.specification_id }}
run: |
# All 9 supported libraries
LIBRARIES="matplotlib seaborn plotly bokeh altair plotnine pygal highcharts letsplot"
# Get current labels on the issue
LABELS=$(gh issue view "$ISSUE" --json labels -q '.labels[].name' 2>/dev/null || echo "")
# Count done and failed implementations
DONE_COUNT=0
FAILED_COUNT=0
DONE_LIBS=""
FAILED_LIBS=""
for lib in $LIBRARIES; do
if echo "$LABELS" | grep -q "^impl:${lib}:done$"; then
DONE_COUNT=$((DONE_COUNT + 1))
DONE_LIBS="$DONE_LIBS $lib"
elif echo "$LABELS" | grep -q "^impl:${lib}:failed$"; then
FAILED_COUNT=$((FAILED_COUNT + 1))
FAILED_LIBS="$FAILED_LIBS $lib"
fi
done
TOTAL=$((DONE_COUNT + FAILED_COUNT))
echo "::notice::Libraries: $DONE_COUNT done, $FAILED_COUNT failed, $TOTAL/9 total"
# Close issue if all 9 libraries are done OR done+failed=9
if [ "$TOTAL" -eq 9 ]; then
# Build status table
TABLE="| Library | Status |\n|---------|--------|"
for lib in $LIBRARIES; do
if echo "$DONE_LIBS" | grep -w -q "$lib"; then
TABLE="$TABLE\n| $lib | :white_check_mark: |"
elif echo "$FAILED_LIBS" | grep -w -q "$lib"; then
TABLE="$TABLE\n| $lib | :x: (not supported) |"
fi
done
if [ "$FAILED_COUNT" -eq 0 ]; then
TITLE=":tada: All Implementations Complete!"
SUMMARY="All 9 library implementations for \`${SPEC_ID}\` have been successfully merged."
else
TITLE=":white_check_mark: Implementations Complete"
SUMMARY="${DONE_COUNT}/9 implementations merged, ${FAILED_COUNT} libraries could not implement this plot type."
fi
gh issue comment "$ISSUE" --body "## ${TITLE}
${SUMMARY}
$(echo -e "$TABLE")
---
:robot: *[impl-merge](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*"
gh issue close "$ISSUE"
echo "::notice::Closed issue #$ISSUE - all implementations complete ($DONE_COUNT done, $FAILED_COUNT failed)"
fi
- name: Trigger database sync
if: steps.check.outputs.should_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run sync-postgres.yml
echo "::notice::Triggered sync-postgres.yml"