Skip to content

Copilot Issue Assignment #1368

Copilot Issue Assignment

Copilot Issue Assignment #1368

name: Copilot Issue Assignment
on:
issues:
types: [opened, labeled]
schedule:
# Run every 10 minutes to process queued issues
- cron: '*/10 * * * *'
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to assign to Copilot'
required: true
type: number
permissions:
contents: write
pull-requests: write
issues: write
actions: read
jobs:
assign-to-copilot:
runs-on: [self-hosted, linux, x64]
if: >
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule' ||
(github.event.action == 'opened' && startsWith(github.event.issue.title, 'Fix:')) ||
(github.event.action == 'labeled' && github.event.label.name == 'copilot-fix')
steps:
- name: Clean workspace
run: |
# Fix permissions and clean workspace to avoid checkout failures
if [ -d "$GITHUB_WORKSPACE" ]; then
sudo chown -R $(whoami):$(whoami) "$GITHUB_WORKSPACE" 2>/dev/null || true
sudo chmod -R u+rwX "$GITHUB_WORKSPACE" 2>/dev/null || true
rm -rf "$GITHUB_WORKSPACE"/* "$GITHUB_WORKSPACE"/.* 2>/dev/null || true
fi
continue-on-error: true
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
clean: true
- name: Find queued issues
id: find_queued
if: github.event_name == 'schedule'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Find issues labeled 'copilot-queued' OR with 'Fix:' prefix
# Priority: 1) copilot-queued, 2) Fix: prefix issues
QUEUED_ISSUES=$(gh issue list --repo "${{ github.repository }}" --label "copilot-queued" --state open --json number --jq '.[].number' | head -1)
if [ -z "$QUEUED_ISSUES" ]; then
# No queued issues, search for 'Fix:' prefix issues
QUEUED_ISSUES=$(gh issue list --repo "${{ github.repository }}" --search "Fix: in:title" --state open --json number --jq '.[].number' | head -1)
fi
if [ -n "$QUEUED_ISSUES" ]; then
echo "number=$QUEUED_ISSUES" >> $GITHUB_OUTPUT
echo "Found queued issue #${QUEUED_ISSUES}" | tee -a $GITHUB_STEP_SUMMARY
else
echo "No queued issues found" | tee -a $GITHUB_STEP_SUMMARY
echo "number=" >> $GITHUB_OUTPUT
fi
- name: Determine issue number
id: issue
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "number=${{ inputs.issue_number }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" == "schedule" ]; then
echo "number=${{ steps.find_queued.outputs.number }}" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
fi
- name: Check Copilot agent concurrency
id: check_concurrency
if: steps.issue.outputs.number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Retry logic with exponential backoff
MAX_RETRIES=3
RETRY_COUNT=0
BACKOFF=2
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if ACTIVE_COUNT=$(gh pr list --author "app/copilot-swe-agent" --state open --repo "${{ github.repository }}" --json number 2>/dev/null | jq 'length'); then
echo "Currently active Copilot agents: ${ACTIVE_COUNT}" | tee -a $GITHUB_STEP_SUMMARY
MAX_CONCURRENT=3
if [ "${ACTIVE_COUNT}" -ge "${MAX_CONCURRENT}" ]; then
echo "⚠️ Concurrency limit reached (${ACTIVE_COUNT}/${MAX_CONCURRENT})" | tee -a $GITHUB_STEP_SUMMARY
echo "should_wait=true" >> $GITHUB_OUTPUT
else
echo "✅ Concurrency available (${ACTIVE_COUNT}/${MAX_CONCURRENT})" | tee -a $GITHUB_STEP_SUMMARY
echo "should_wait=false" >> $GITHUB_OUTPUT
fi
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
WAIT_TIME=$((BACKOFF ** RETRY_COUNT))
echo "⚠️ API call failed, retrying in ${WAIT_TIME}s (attempt $((RETRY_COUNT + 1))/${MAX_RETRIES})..." | tee -a $GITHUB_STEP_SUMMARY
sleep $WAIT_TIME
else
echo "❌ Failed to check concurrency after ${MAX_RETRIES} attempts" | tee -a $GITHUB_STEP_SUMMARY
echo "should_wait=true" >> $GITHUB_OUTPUT # Fail safe: queue the issue
fi
fi
done
- name: Queue issue if limit reached
if: steps.check_concurrency.outputs.should_wait == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE_NUMBER="${{ steps.issue.outputs.number }}"
gh issue edit "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --add-label "copilot-queued"
gh issue comment "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --body "🤖 **Copilot Agent Queue Status**
This issue has been queued for Copilot assignment due to concurrency limits.
- **Active agents**: $(gh pr list --author 'app/copilot-swe-agent' --state open --repo '${{ github.repository }}' --json number | jq 'length')
- **Max concurrent**: 3
You can manually trigger assignment later using:
\`\`\`bash
gh workflow run copilot-issue-assignment.yml --field issue_number=${ISSUE_NUMBER}
\`\`\`"
echo "Issue #${ISSUE_NUMBER} queued" | tee -a $GITHUB_STEP_SUMMARY
- name: Invoke Copilot coding agent
if: steps.check_concurrency.outputs.should_wait == 'false'
env:
# Using PAT with Copilot permissions (see .github/COPILOT_PAT_SETUP.md)
GH_TOKEN: ${{ secrets.GH_COPILOT_PAT }}
run: |
ISSUE_NUMBER="${{ steps.issue.outputs.number }}"
echo "## 🤖 Invoking Copilot Coding Agent" | tee -a $GITHUB_STEP_SUMMARY
echo "Assigning issue #${ISSUE_NUMBER} to GitHub Copilot..." | tee -a $GITHUB_STEP_SUMMARY
# Get issue details with retry
MAX_RETRIES=3
RETRY_COUNT=0
BACKOFF=2
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if ISSUE_TITLE=$(gh issue view "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --json title --jq '.title' 2>/dev/null) && \
ISSUE_BODY=$(gh issue view "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --json body --jq '.body' 2>/dev/null); then
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
WAIT_TIME=$((BACKOFF ** RETRY_COUNT))
echo "⚠️ Failed to fetch issue details, retrying in ${WAIT_TIME}s (attempt $((RETRY_COUNT + 1))/${MAX_RETRIES})..." | tee -a $GITHUB_STEP_SUMMARY
sleep $WAIT_TIME
else
echo "❌ Failed to fetch issue details after ${MAX_RETRIES} attempts" | tee -a $GITHUB_STEP_SUMMARY
exit 1
fi
fi
done
ISSUE_URL="https://github.com/${{ github.repository }}/issues/${ISSUE_NUMBER}"
cat > /tmp/copilot_task_${ISSUE_NUMBER}.txt << TASK_EOF
Fix ${ISSUE_TITLE}
${ISSUE_BODY}
References:
- Issue: ${ISSUE_URL}
Please analyze this issue, identify the root cause, and implement a solution that:
- Fixes the immediate problem
- Prevents similar issues in the future
- Includes proper error handling
- Adds tests if applicable
Ensure all tests pass and security scans are clean before finalizing.
TASK_EOF
# Create agent task with retry
RETRY_COUNT=0
AGENT_CREATED=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if AGENT_URL=$(gh agent-task create --repo "${{ github.repository }}" --base main -F /tmp/copilot_task_${ISSUE_NUMBER}.txt 2>&1); then
echo "✅ Successfully created Copilot agent task" | tee -a $GITHUB_STEP_SUMMARY
echo "Agent session: ${AGENT_URL}" | tee -a $GITHUB_STEP_SUMMARY
AGENT_CREATED=true
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
WAIT_TIME=$((BACKOFF ** RETRY_COUNT))
echo "⚠️ Failed to create agent task, retrying in ${WAIT_TIME}s (attempt $((RETRY_COUNT + 1))/${MAX_RETRIES})..." | tee -a $GITHUB_STEP_SUMMARY
echo "Error: ${AGENT_URL}" | tee -a $GITHUB_STEP_SUMMARY
sleep $WAIT_TIME
else
echo "❌ Failed to create Copilot agent task after ${MAX_RETRIES} attempts" | tee -a $GITHUB_STEP_SUMMARY
echo "Error: ${AGENT_URL}" | tee -a $GITHUB_STEP_SUMMARY
rm -f /tmp/copilot_task_${ISSUE_NUMBER}.txt
exit 1
fi
fi
done
if [ "$AGENT_CREATED" = true ]; then
# Update issue labels with retry
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gh issue edit "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --remove-label "copilot-queued" --add-label "copilot-assigned" 2>/dev/null; then
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
WAIT_TIME=$((BACKOFF ** RETRY_COUNT))
echo "⚠️ Failed to update labels, retrying in ${WAIT_TIME}s..." | tee -a $GITHUB_STEP_SUMMARY
sleep $WAIT_TIME
fi
fi
done
# Post comment with retry
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gh issue comment "${ISSUE_NUMBER}" --repo "${{ github.repository }}" --body "🤖 **Copilot Agent Task Created**
A Copilot coding agent has been assigned to this issue.
**Session URL**: ${AGENT_URL}
The agent will:
- Analyze the issue and related code
- Create a new branch and pull request
- Implement fixes autonomously
- Run tests and security scans
- Update the PR based on review feedback
Track progress in the agent session logs and the PR that will be created." 2>/dev/null; then
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
WAIT_TIME=$((BACKOFF ** RETRY_COUNT))
echo "⚠️ Failed to post comment, retrying in ${WAIT_TIME}s..." | tee -a $GITHUB_STEP_SUMMARY
sleep $WAIT_TIME
fi
fi
done
fi
rm -f /tmp/copilot_task_${ISSUE_NUMBER}.txt
- name: Summary
if: always()
run: |
echo "" | tee -a $GITHUB_STEP_SUMMARY
echo "---" | tee -a $GITHUB_STEP_SUMMARY
echo "" | tee -a $GITHUB_STEP_SUMMARY
echo "**Method**: \`gh agent-task create\` (Official GitHub CLI command)" | tee -a $GITHUB_STEP_SUMMARY
echo "**Requires**: GitHub CLI v2.80.0 or later" | tee -a $GITHUB_STEP_SUMMARY
echo "**Concurrency Limit**: 3 active agents maximum" | tee -a $GITHUB_STEP_SUMMARY
echo "**Cron Schedule**: Runs every 10 minutes to process queued issues" | tee -a $GITHUB_STEP_SUMMARY
echo "**Retry Policy**: 3 attempts with exponential backoff (2^n seconds)" | tee -a $GITHUB_STEP_SUMMARY