Skip to content

CI Assistant

CI Assistant #96

Workflow file for this run

# CI Assistant — automated bot
#
# Triggers after the "Tests" workflow completes:
#
# 1. Push to main + Tests FAIL → sends "ci_failure" to the CI assistant
# Claude attempts a fix and pushes, or creates a GitHub issue
# explaining the root cause with options/pros/cons.
#
# 2. PR + Tests PASS → sends "pr_review" to the CI assistant
# Claude posts a PR comment with a structured review:
# a) Purpose check — does the PR state what problem it solves
# and how users benefit? (can be implicit)
# b) If purpose unclear or PR not useful → explain why, recommend closing
# c) If useful → review implementation and quality
# d) Quality OK → recommend merging (may suggest optional improvements)
# e) Quality not OK → request specific changes
#
# 3. PR + Tests FAIL → sends "pr_ci_failure" to the CI assistant
# Claude posts a review comment noting the test failure + reviews the PR.
#
# 4. Comment "@@fix" on any issue → sends "issue_fix" to the CI assistant
# Claude reads the issue, fixes it, and pushes (or opens a PR).
#
# Secrets required: CI_ASSISTANT_URL, CI_ASSISTANT_SECRET
name: CI Assistant
on:
workflow_run:
workflows: ["Tests"]
types: [completed]
issue_comment:
types: [created]
jobs:
fix-on-comment:
if: >-
github.event_name == 'issue_comment'
&& contains(github.event.comment.body, '@@fix')
&& (github.event.comment.author_association == 'OWNER'
|| github.event.comment.author_association == 'MEMBER'
|| github.event.comment.author_association == 'COLLABORATOR')
runs-on: ubuntu-latest
steps:
- name: Notify CI Assistant
run: |
# Determine if this is a PR comment or issue comment
if [ -n "${{ github.event.issue.pull_request.url }}" ]; then
EVENT="pr_ci_failure"
PR_NUMBER=${{ github.event.issue.number }}
ISSUE_NUMBER=null
REF=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json headRefName --jq '.headRefName')
else
EVENT="issue_fix"
PR_NUMBER=null
ISSUE_NUMBER=${{ github.event.issue.number }}
REF=$(gh repo view ${{ github.repository }} --json defaultBranchRef --jq '.defaultBranchRef.name')
fi
jq -n \
--arg repo "${{ github.repository }}" \
--arg ref "$REF" \
--arg event "$EVENT" \
--argjson pr_number "$PR_NUMBER" \
--argjson issue_number "$ISSUE_NUMBER" \
--arg details "${{ github.event.issue.title }}" \
'{repo: $repo, ref: $ref, event: $event, pr_number: $pr_number, issue_number: $issue_number, details: $details}' \
> /tmp/payload.json
curl -s -X POST "${{ secrets.CI_ASSISTANT_URL }}/webhook" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.CI_ASSISTANT_SECRET }}" \
-d @/tmp/payload.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
on-failure:
if: >-
github.event.workflow_run.conclusion == 'failure'
&& github.event.workflow_run.event == 'push'
runs-on: ubuntu-latest
steps:
- name: Fetch failure logs
id: logs
run: |
gh run view ${{ github.event.workflow_run.id }} --repo ${{ github.repository }} --log-failed 2>/dev/null | tail -50 > /tmp/logs.txt || echo "Could not fetch logs" > /tmp/logs.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify CI Assistant
run: |
jq -n \
--arg repo "${{ github.repository }}" \
--arg ref "${{ github.event.workflow_run.head_branch }}" \
--arg event "ci_failure" \
--argjson run_id ${{ github.event.workflow_run.id }} \
--arg details "$(cat /tmp/logs.txt)" \
'{repo: $repo, ref: $ref, event: $event, run_id: $run_id, details: $details}' \
> /tmp/payload.json
curl -s -X POST "${{ secrets.CI_ASSISTANT_URL }}/webhook" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.CI_ASSISTANT_SECRET }}" \
-d @/tmp/payload.json
on-pr-tests-passed:
if: >-
github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Get PR number
id: pr
run: |
PR_NUMBER=$(echo '${{ toJSON(github.event.workflow_run.pull_requests) }}' | jq '.[0].number // empty')
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head ${{ github.event.workflow_run.head_branch }} --json number --jq '.[0].number')
fi
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify CI Assistant
if: steps.pr.outputs.number
run: |
jq -n \
--arg repo "${{ github.repository }}" \
--arg ref "${{ github.event.workflow_run.head_branch }}" \
--arg event "pr_review" \
--argjson pr_number ${{ steps.pr.outputs.number }} \
'{repo: $repo, ref: $ref, event: $event, pr_number: $pr_number}' \
> /tmp/payload.json
curl -s -X POST "${{ secrets.CI_ASSISTANT_URL }}/webhook" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.CI_ASSISTANT_SECRET }}" \
-d @/tmp/payload.json
on-pr-tests-failed:
if: >-
github.event.workflow_run.conclusion == 'failure'
&& github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Get PR number
id: pr
run: |
PR_NUMBER=$(echo '${{ toJSON(github.event.workflow_run.pull_requests) }}' | jq '.[0].number // empty')
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head ${{ github.event.workflow_run.head_branch }} --json number --jq '.[0].number')
fi
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Fetch failure logs
run: |
gh run view ${{ github.event.workflow_run.id }} --repo ${{ github.repository }} --log-failed 2>/dev/null | tail -50 > /tmp/logs.txt || echo "Could not fetch logs" > /tmp/logs.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify CI Assistant
if: steps.pr.outputs.number
run: |
jq -n \
--arg repo "${{ github.repository }}" \
--arg ref "${{ github.event.workflow_run.head_branch }}" \
--arg event "pr_ci_failure" \
--argjson pr_number ${{ steps.pr.outputs.number }} \
--argjson run_id ${{ github.event.workflow_run.id }} \
--arg details "$(cat /tmp/logs.txt)" \
'{repo: $repo, ref: $ref, event: $event, pr_number: $pr_number, run_id: $run_id, details: $details}' \
> /tmp/payload.json
curl -s -X POST "${{ secrets.CI_ASSISTANT_URL }}/webhook" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.CI_ASSISTANT_SECRET }}" \
-d @/tmp/payload.json