Release v0.2.0-rc3: Headless Components with RC2 Features #66
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Bot CI Integration | |
on: | |
pull_request: | |
types: [opened, synchronize] | |
check_suite: | |
types: [completed] | |
workflow_run: | |
workflows: ["CI"] | |
types: [completed] | |
permissions: | |
contents: write | |
pull-requests: write | |
checks: read | |
jobs: | |
monitor-ci-for-bots: | |
name: Monitor CI for Bot PRs | |
runs-on: ubuntu-latest | |
# Only run for bot-created PRs | |
if: | | |
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'bot-created')) || | |
(github.event_name == 'check_suite' && github.event.check_suite.pull_requests[0] && contains(github.event.check_suite.pull_requests[0].labels.*.name, 'bot-created')) || | |
(github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0]) | |
steps: | |
- name: Get PR details | |
id: pr-details | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
let prNumber; | |
if (context.eventName === 'pull_request') { | |
prNumber = context.payload.pull_request.number; | |
} else if (context.eventName === 'check_suite') { | |
prNumber = context.payload.check_suite.pull_requests[0]?.number; | |
} else if (context.eventName === 'workflow_run') { | |
prNumber = context.payload.workflow_run.pull_requests[0]?.number; | |
} | |
if (!prNumber) { | |
console.log('No PR number found'); | |
return null; | |
} | |
// Get PR details | |
const pr = await github.rest.pulls.get({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
pull_number: prNumber | |
}); | |
// Check if this is a bot PR | |
const labels = pr.data.labels.map(l => l.name); | |
const isBotPR = labels.includes('bot-created') || labels.includes('agent:wip') || labels.includes('agent:needs-review'); | |
core.setOutput('pr_number', prNumber); | |
core.setOutput('is_bot_pr', isBotPR); | |
core.setOutput('branch', pr.data.head.ref); | |
return { | |
prNumber, | |
isBotPR, | |
branch: pr.data.head.ref, | |
labels | |
}; | |
- name: Check CI status | |
if: steps.pr-details.outputs.is_bot_pr == 'true' | |
id: ci-status | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const prNumber = ${{ steps.pr-details.outputs.pr_number }}; | |
// Get check runs for the PR | |
const checkRuns = await github.rest.checks.listForRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: '${{ steps.pr-details.outputs.branch }}' | |
}); | |
const checks = checkRuns.data.check_runs; | |
const failedChecks = checks.filter(c => c.conclusion === 'failure'); | |
const pendingChecks = checks.filter(c => c.status === 'in_progress' || c.status === 'queued'); | |
console.log(`Total checks: ${checks.length}`); | |
console.log(`Failed: ${failedChecks.length}`); | |
console.log(`Pending: ${pendingChecks.length}`); | |
core.setOutput('has_failures', failedChecks.length > 0); | |
core.setOutput('all_complete', pendingChecks.length === 0); | |
core.setOutput('failed_count', failedChecks.length); | |
// Get failure details | |
const failures = failedChecks.map(c => ({ | |
name: c.name, | |
conclusion: c.conclusion, | |
detailsUrl: c.details_url | |
})); | |
return { | |
totalChecks: checks.length, | |
failedChecks: failedChecks.length, | |
pendingChecks: pendingChecks.length, | |
failures | |
}; | |
- name: Notify bot of CI failures | |
if: | | |
steps.pr-details.outputs.is_bot_pr == 'true' && | |
steps.ci-status.outputs.has_failures == 'true' && | |
steps.ci-status.outputs.all_complete == 'true' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const prNumber = ${{ steps.pr-details.outputs.pr_number }}; | |
const failedCount = ${{ steps.ci-status.outputs.failed_count }}; | |
// Check if we've already notified about these failures | |
const comments = await github.rest.issues.listComments({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber | |
}); | |
const recentBotComments = comments.data.filter(c => | |
c.user.type === 'Bot' && | |
c.body.includes('CI Failure Detected') && | |
(new Date() - new Date(c.created_at)) < 3600000 // Within last hour | |
); | |
if (recentBotComments.length === 0) { | |
// Post notification comment | |
const comment = `🚨 **CI Failure Detected** | |
${failedCount} CI check(s) have failed on this PR. | |
**Bot Actions Available:** | |
1. Attempt automatic fixes: \`node scripts/bot-workflow/utils/bot-fix-ci.js ${prNumber}\` | |
2. Analyze failures: \`gh pr checks ${prNumber}\` | |
3. Request human help: \`/bot handoff "CI failures need manual intervention"\` | |
The bot can attempt to fix common issues like: | |
- Formatting errors | |
- Linting issues | |
- Whitespace problems | |
For test failures or type errors, human intervention may be required.`; | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
body: comment | |
}); | |
// Add label to indicate CI issues | |
await github.rest.issues.addLabels({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
labels: ['bot:ci-failure'] | |
}); | |
} | |
- name: Auto-trigger bot CI fix | |
if: | | |
steps.pr-details.outputs.is_bot_pr == 'true' && | |
steps.ci-status.outputs.has_failures == 'true' && | |
steps.ci-status.outputs.all_complete == 'true' && | |
github.event_name == 'workflow_run' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const prNumber = ${{ steps.pr-details.outputs.pr_number }}; | |
// Check if this is the first CI failure | |
const labels = await github.rest.issues.listLabelsOnIssue({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber | |
}); | |
const hasAutoFixed = labels.data.some(l => l.name === 'bot:ci-auto-fixed'); | |
if (!hasAutoFixed) { | |
// Add comment to trigger bot fix | |
const comment = `🤖 **Auto-triggering CI fix** | |
/bot fix-ci | |
The bot will attempt to automatically fix CI failures.`; | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
body: comment | |
}); | |
// Add label to prevent multiple attempts | |
await github.rest.issues.addLabels({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
labels: ['bot:ci-auto-fixed'] | |
}); | |
} | |
- name: Celebrate CI success | |
if: | | |
steps.pr-details.outputs.is_bot_pr == 'true' && | |
steps.ci-status.outputs.has_failures == 'false' && | |
steps.ci-status.outputs.all_complete == 'true' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const prNumber = ${{ steps.pr-details.outputs.pr_number }}; | |
// Remove CI failure labels if present | |
try { | |
await github.rest.issues.removeLabel({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
name: 'bot:ci-failure' | |
}); | |
} catch (e) { | |
// Label might not exist | |
} | |
// Check if we should add success comment | |
const labels = await github.rest.issues.listLabelsOnIssue({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber | |
}); | |
const needsReview = labels.data.some(l => l.name === 'agent:needs-review'); | |
if (needsReview) { | |
// Add success comment | |
const comment = `✅ **All CI Checks Passed!** | |
This PR is ready for human review. | |
- All tests are passing | |
- Code quality checks passed | |
- No type errors | |
- Build successful | |
@${context.repo.owner} - This bot PR is ready for your review.`; | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
body: comment | |
}); | |
} |