Skip to content

Auto Merge Queue on CI Success #555

Auto Merge Queue on CI Success

Auto Merge Queue on CI Success #555

name: Auto Merge Queue on CI Success
on:
check_suite:
types: [completed]
pull_request:
types: [labeled]
branches: [master-gmq]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to test workflow'
required: true
type: string
jobs:
auto_enqueue:
runs-on: ubuntu-latest
steps:
- name: Debug Event Information
run: |
echo "Event name: ${{ github.event_name }}"
echo "Event action: ${{ github.event.action || 'N/A' }}"
if [[ "${{ github.event_name }}" == "check_suite" ]]; then
echo "Check suite conclusion: ${{ github.event.check_suite.conclusion }}"
echo "Check suite status: ${{ github.event.check_suite.status }}"
echo "Check suite head branch: ${{ github.event.check_suite.head_branch }}"
echo "Pull requests count: ${{ github.event.check_suite.pull_requests[0] && '1+' || '0' }}"
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "PR number: ${{ github.event.pull_request.number }}"
echo "PR base ref: ${{ github.event.pull_request.base.ref }}"
echo "PR state: ${{ github.event.pull_request.state }}"
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "Manual trigger for PR: ${{ github.event.inputs.pr_number }}"
fi
- name: Determine PR Number and Validate
id: get_pr
run: |
if [[ "${{ github.event_name }}" == "check_suite" ]]; then
# For check_suite events
if [[ "${{ github.event.check_suite.conclusion }}" != "success" ]]; then
echo "Skip: Check suite conclusion is not success"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
if [[ -z "${{ github.event.check_suite.pull_requests[0].number }}" ]]; then
echo "Skip: No pull requests in check suite"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
PR_NUMBER="${{ github.event.check_suite.pull_requests[0].number }}"
BASE_REF="${{ github.event.check_suite.pull_requests[0].base.ref }}"
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
# For pull_request events
PR_NUMBER="${{ github.event.pull_request.number }}"
BASE_REF="${{ github.event.pull_request.base.ref }}"
# Only process if auto-merge label was added
if [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" != "auto-merge" ]]; then
echo "Skip: Label '${{ github.event.label.name }}' is not 'auto-merge'"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
# For manual dispatch
PR_NUMBER="${{ github.event.inputs.pr_number }}"
# Get base ref from API
BASE_REF=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER --jq '.base.ref')
else
echo "Skip: Unsupported event type"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
# Validate base branch is master-gmq
if [[ "$BASE_REF" != "master-gmq" ]]; then
echo "Skip: PR base branch '$BASE_REF' is not 'master-gmq'"
echo "should_run=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "PR Number: $PR_NUMBER"
echo "Base Ref: $BASE_REF"
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "should_run=true" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Check if PR has auto-merge label
id: check_label
if: steps.get_pr.outputs.should_run == 'true'
run: |
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
echo "Checking auto-merge label for PR #$PR_NUMBER"
HAS_LABEL=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER --jq '.labels[] | select(.name == "auto-merge") | .name')
echo "Label result: '$HAS_LABEL'"
if [[ -n "$HAS_LABEL" ]]; then
echo "✅ PR has auto-merge label"
echo "has_auto_merge_label=true" >> $GITHUB_OUTPUT
else
echo "❌ PR does not have auto-merge label"
echo "has_auto_merge_label=false" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Wait for CI Completion (with 10min timeout)
id: wait_for_ci
if: steps.check_label.outputs.has_auto_merge_label == 'true'
run: |
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
echo "⏳ Waiting for CI completion on PR #$PR_NUMBER (timeout: 10 minutes)"
# Function to check CI status
check_ci_status() {
gh api graphql \
-f owner="${{ github.repository_owner }}" \
-f repo="${{ github.event.repository.name }}" \
-F number="$PR_NUMBER" \
-f query='
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
commits(last: 1) {
nodes {
commit {
statusCheckRollup {
state
}
}
}
}
}
}
}' --jq '.data.repository.pullRequest.commits.nodes[0].commit.statusCheckRollup.state'
}
# Initial status check
CHECKS_STATUS=$(check_ci_status)
echo "Initial CI Status: $CHECKS_STATUS"
if [[ "$CHECKS_STATUS" == "SUCCESS" ]]; then
echo "✅ CI checks already passed"
echo "checks_status=SUCCESS" >> $GITHUB_OUTPUT
exit 0
fi
# Post initial waiting message
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="⏳ Auto-merge label detected! Waiting for CI checks to complete (timeout: 10 minutes)..."
# Wait up to 10 minutes (600 seconds) with 30-second intervals
TIMEOUT=600
INTERVAL=30
ELAPSED=0
while [[ $ELAPSED -lt $TIMEOUT ]]; do
sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
CHECKS_STATUS=$(check_ci_status)
echo "[$ELAPSED/${TIMEOUT}s] CI Status: $CHECKS_STATUS"
case "$CHECKS_STATUS" in
"SUCCESS")
echo "✅ All CI checks passed after ${ELAPSED}s"
echo "checks_status=SUCCESS" >> $GITHUB_OUTPUT
exit 0
;;
"FAILURE"|"ERROR")
echo "❌ CI checks failed (status: $CHECKS_STATUS)"
echo "checks_status=$CHECKS_STATUS" >> $GITHUB_OUTPUT
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="❌ CI checks failed (status: $CHECKS_STATUS). Cannot add to merge queue."
exit 0
;;
"PENDING"|"EXPECTED"|null)
# Continue waiting
if [[ $((ELAPSED % 120)) -eq 0 ]]; then # Update every 2 minutes
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="⏳ Still waiting for CI checks... (${ELAPSED}s elapsed, status: $CHECKS_STATUS)"
fi
;;
*)
echo "⚠️ Unknown CI status: $CHECKS_STATUS, continuing to wait..."
;;
esac
done
# Timeout reached
echo "⏰ Timeout reached after 10 minutes"
echo "checks_status=TIMEOUT" >> $GITHUB_OUTPUT
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="⏰ Timeout: CI checks did not complete within 10 minutes. Please check CI status and re-add auto-merge label if needed."
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Verify Final CI Status
id: check_status
if: |
steps.check_label.outputs.has_auto_merge_label == 'true' &&
steps.wait_for_ci.outputs.checks_status == 'SUCCESS'
run: |
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
CHECKS_STATUS="${{ steps.wait_for_ci.outputs.checks_status }}"
echo "Final CI Status: $CHECKS_STATUS"
echo "checks_status=$CHECKS_STATUS" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Add to Merge Queue
if: |
steps.check_label.outputs.has_auto_merge_label == 'true' &&
steps.wait_for_ci.outputs.checks_status == 'SUCCESS'
run: |
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
echo "🚀 Adding PR #$PR_NUMBER to merge queue with PAT..."
# Get PR ID using GraphQL
PR_ID=$(gh api graphql \
-f owner="${{ github.repository_owner }}" \
-f repo="${{ github.event.repository.name }}" \
-F number="$PR_NUMBER" \
-f query='
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
id
title
baseRefName
}
}
}' --jq '.data.repository.pullRequest.id')
echo "📋 PR ID: $PR_ID"
# Enqueue PR using GraphQL mutation
RESULT=$(gh api graphql \
-f pr_id="$PR_ID" \
-f query='
mutation($pr_id: ID!) {
enqueuePullRequest(input: {
pullRequestId: $pr_id
}) {
clientMutationId
mergeQueueEntry {
id
position
}
}
}')
echo "GraphQL Result: $RESULT"
QUEUE_POSITION=$(echo "$RESULT" | jq -r '.data.enqueuePullRequest.mergeQueueEntry.position // "unknown"')
echo "✅ Successfully added to merge queue at position: $QUEUE_POSITION"
# Comment on PR with success
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="🤖 Automatically added to merge queue at position **$QUEUE_POSITION** after CI success ✅ (Event: ${{ github.event_name }}, Trigger: Auto-merge label detected with successful CI)"
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Handle Conditions Not Met
if: |
steps.get_pr.outputs.should_run == 'true' &&
(steps.check_label.outputs.has_auto_merge_label != 'true' ||
(steps.wait_for_ci.outputs.checks_status != 'SUCCESS' && steps.wait_for_ci.outputs.checks_status != ''))
run: |
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
HAS_LABEL="${{ steps.check_label.outputs.has_auto_merge_label }}"
CHECKS_STATUS="${{ steps.wait_for_ci.outputs.checks_status }}"
echo "❌ Conditions not met for PR #$PR_NUMBER:"
echo " - Has auto-merge label: $HAS_LABEL"
echo " - CI status after waiting: $CHECKS_STATUS"
if [[ "$HAS_LABEL" == "true" && "$CHECKS_STATUS" == "TIMEOUT" ]]; then
echo "CI checks timed out after 10 minutes"
elif [[ "$HAS_LABEL" == "true" && "$CHECKS_STATUS" == "FAILURE" ]]; then
echo "CI checks failed"
elif [[ "$HAS_LABEL" != "true" ]]; then
echo "Auto-merge label not found"
fi
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}
- name: Handle Enqueue Failure
if: failure()
run: |
if [[ "${{ steps.get_pr.outputs.should_run }}" == "true" ]]; then
PR_NUMBER="${{ steps.get_pr.outputs.pr_number }}"
echo "❌ Failed to add PR #$PR_NUMBER to merge queue"
# Comment on PR with failure
gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
-f body="❌ Failed to add PR to merge queue automatically. Please check the [action logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) or add manually using GraphQL. Error occurred during: ${{ github.event_name }} event"
fi
env:
GITHUB_TOKEN: ${{ secrets.MERGE_QUEUE_PAT }}