feat(ci): add CLIO-powered issue triage and PR review #1
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: PR Review with CLIO | ||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, review_requested] | ||
| permissions: | ||
| pull-requests: write | ||
| contents: read | ||
| packages: read | ||
| env: | ||
| CLIO_MODEL: gpt-5-mini | ||
| REGISTRY: ghcr.io | ||
| jobs: | ||
| review: | ||
| name: Analyze and Review PR | ||
| runs-on: ubuntu-latest | ||
| if: | | ||
| github.event.pull_request.user.type != 'Bot' && | ||
| github.event.pull_request.draft != true | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Log in to GHCR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Pull CLIO container | ||
| run: docker pull ghcr.io/syntheticautonomicmind/clio:latest | ||
| - name: Prepare workspace | ||
| run: | | ||
| mkdir -p /tmp/clio-workspace | ||
| cp -r ./* /tmp/clio-workspace/ 2>/dev/null || true | ||
| cp -r ./.clio /tmp/clio-workspace/ 2>/dev/null || true | ||
| cp -r ./.github /tmp/clio-workspace/ 2>/dev/null || true | ||
| mkdir -p /tmp/clio-workspace/.clio/sessions | ||
| mkdir -p /tmp/clio-config | ||
| - name: Configure CLIO authentication | ||
| env: | ||
| CLIO_ACCESS: ${{ secrets.CLIO_ACCESS }} | ||
| run: | | ||
| set +x | ||
| printf '{"github_token": "%s", "saved_at": %d}' "$CLIO_ACCESS" "$(date +%s)" > /tmp/clio-config/github_tokens.json | ||
| chmod 600 /tmp/clio-config/github_tokens.json | ||
| echo '{"provider": "github_copilot"}' > /tmp/clio-config/config.json | ||
| - name: Prepare PR info file | ||
| env: | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | ||
| PR_HEAD: ${{ github.event.pull_request.head.ref }} | ||
| PR_BASE: ${{ github.event.pull_request.base.ref }} | ||
| PR_ACTION: ${{ github.event.action }} | ||
| PR_BODY: ${{ github.event.pull_request.body }} | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh pr diff "$PR_NUMBER" > /tmp/clio-workspace/PR_DIFF.txt 2>/dev/null || echo "No diff available" > /tmp/clio-workspace/PR_DIFF.txt | ||
| gh pr view "$PR_NUMBER" --json files --jq '.files[].path' > /tmp/clio-workspace/PR_FILES.txt 2>/dev/null || echo "" > /tmp/clio-workspace/PR_FILES.txt | ||
| # Create PR info file using env vars (safe from shell metacharacters) | ||
| { | ||
| echo "# Pull Request #${PR_NUMBER}" | ||
| echo "" | ||
| echo "**Title:** ${PR_TITLE}" | ||
| echo "**Author:** ${PR_AUTHOR}" | ||
| echo "**Branch:** ${PR_HEAD} -> ${PR_BASE}" | ||
| echo "**Event:** ${PR_ACTION}" | ||
| echo "" | ||
| echo "## Description" | ||
| echo "" | ||
| } > /tmp/clio-workspace/PR_INFO.md | ||
| # Append body safely via printf | ||
| printf '%s\n' "$PR_BODY" >> /tmp/clio-workspace/PR_INFO.md | ||
| echo "PR info prepared" | ||
| - name: Run CLIO PR Review | ||
| id: review | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: | | ||
| TASK="You are in HEADLESS CI/CD MODE. No human is present. | ||
| TASK: Review Pull Request #${PR_NUMBER} | ||
| STEPS: | ||
| 1. Read .github/clio-prompts/pr-review.md for instructions | ||
| 2. Read PR_INFO.md, PR_DIFF.txt, PR_FILES.txt | ||
| 3. Check AGENTS.md and docs/STYLE_GUIDE.md if needed | ||
| 4. WRITE your review JSON to /workspace/review.json using file_operations | ||
| CRITICAL: | ||
| - DO NOT use user_collaboration (it will hang forever) | ||
| - Write JSON to /workspace/review.json using file_operations create_file" | ||
| echo "=== Starting CLIO PR Review ===" | ||
| docker run -i --rm \ | ||
| -v "/tmp/clio-workspace":/workspace:rw \ | ||
| -v "/tmp/clio-config":/root/.clio:rw \ | ||
| -w /workspace \ | ||
| -e CLIO_LOG_LEVEL=WARNING \ | ||
| -e GH_TOKEN="${GH_TOKEN}" \ | ||
| ghcr.io/syntheticautonomicmind/clio:latest \ | ||
| --new \ | ||
| --model "$CLIO_MODEL" \ | ||
| --input "$TASK" \ | ||
| --exit 2>&1 | tee /tmp/clio-workspace/full_response.txt || true | ||
| echo "" | ||
| echo "=== CLIO Review Complete ===" | ||
| if [ -f /tmp/clio-workspace/review.json ]; then | ||
| echo "review.json found!" | ||
| else | ||
| echo "review.json NOT found" | ||
| fi | ||
| - name: Get review JSON | ||
| id: extract | ||
| run: | | ||
| if [ -f /tmp/clio-workspace/review.json ] && jq . /tmp/clio-workspace/review.json > /dev/null 2>&1; then | ||
| echo "Using review.json written by CLIO" | ||
| cp /tmp/clio-workspace/review.json /tmp/clio-workspace/analysis.json | ||
| echo "json_valid=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "json_valid=false" >> $GITHUB_OUTPUT | ||
| echo '{"recommendation":"needs-review","security_concerns":[],"style_issues":[],"documentation_issues":[],"test_coverage":"unknown","breaking_changes":false,"suggested_labels":["needs-review"],"summary":"Automated analysis could not produce review.json. Manual review recommended.","detailed_feedback":[]}' > /tmp/clio-workspace/analysis.json | ||
| echo "Using fallback JSON" | ||
| fi | ||
| echo "=== Review JSON ===" | ||
| cat /tmp/clio-workspace/analysis.json | ||
| - name: Parse results | ||
| id: parse | ||
| run: | | ||
| ANALYSIS=$(cat /tmp/clio-workspace/analysis.json) | ||
| RECOMMENDATION=$(echo "$ANALYSIS" | jq -r '.recommendation // "needs-review"') | ||
| LABELS=$(echo "$ANALYSIS" | jq -r '(.labels // .suggested_labels // []) | join(",")') | ||
| echo "recommendation=$RECOMMENDATION" >> $GITHUB_OUTPUT | ||
| echo "labels=$LABELS" >> $GITHUB_OUTPUT | ||
| - name: Apply labels | ||
| if: steps.parse.outputs.labels != '' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: | | ||
| IFS=',' read -ra LABELS <<< "${{ steps.parse.outputs.labels }}" | ||
| for label in "${LABELS[@]}"; do | ||
| label=$(echo "$label" | xargs) | ||
| if [ -n "$label" ]; then | ||
| gh pr edit "$PR_NUMBER" --add-label "$label" 2>/dev/null || \ | ||
| (gh label create "$label" --color "c5def5" 2>/dev/null; gh pr edit "$PR_NUMBER" --add-label "$label" 2>/dev/null) || true | ||
| fi | ||
| done | ||
| - name: Post review comment | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: | | ||
| ANALYSIS=$(cat /tmp/clio-workspace/analysis.json) | ||
| RECOMMENDATION=$(echo "$ANALYSIS" | jq -r '.recommendation // "needs-review"') | ||
| SUMMARY=$(echo "$ANALYSIS" | jq -r '.summary // "Analysis complete."') | ||
| TEST_COV=$(echo "$ANALYSIS" | jq -r '.test_coverage // "unknown"') | ||
| BREAKING=$(echo "$ANALYSIS" | jq -r '.breaking_changes // false') | ||
| case "$RECOMMENDATION" in | ||
| "approve"|"approved") EMOJI="OK"; VERDICT="LGTM - Ready for Human Review" ;; | ||
| "needs-changes") EMOJI="WARN"; VERDICT="Changes Requested" ;; | ||
| "security-concern") EMOJI="STOP"; VERDICT="Security Review Required" ;; | ||
| *) EMOJI="INFO"; VERDICT="Needs Review" ;; | ||
| esac | ||
| # Build security concerns section | ||
| SEC_CONCERNS=$(echo "$ANALYSIS" | jq -r '(.security_concerns // []) | if length > 0 then "## Security Concerns\n\n" + (map("- " + .) | join("\n")) + "\n" else "" end') | ||
| # Build style issues section | ||
| STYLE_ISSUES=$(echo "$ANALYSIS" | jq -r '(.style_issues // []) | if length > 0 then "## Style Issues\n\n" + (map("- " + .) | join("\n")) + "\n" else "" end') | ||
| # Build detailed feedback section | ||
| FEEDBACK=$(echo "$ANALYSIS" | jq -r '(.detailed_feedback // []) | if length > 0 then "## Detailed Feedback\n\n" + (map("- " + .) | join("\n")) + "\n" else "" end') | ||
| # Construct comment body | ||
| COMMENT_BODY="## ${EMOJI} CLIO Automated Review: ${VERDICT} | ||
| **Summary:** ${SUMMARY} | ||
| | Metric | Value | | ||
| |--------|-------| | ||
| | Test Coverage | \`${TEST_COV}\` | | ||
| | Breaking Changes | \`${BREAKING}\` | | ||
| ${SEC_CONCERNS} | ||
| ${STYLE_ISSUES} | ||
| ${FEEDBACK} | ||
| --- | ||
| _This is an automated review. A human maintainer will provide final approval._" | ||
| gh pr comment "$PR_NUMBER" --body "$COMMENT_BODY" | ||
| - name: Upload artifacts | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: review-artifacts | ||
| path: | | ||
| /tmp/clio-workspace/full_response.txt | ||
| /tmp/clio-workspace/review.json | ||
| /tmp/clio-workspace/analysis.json | ||
| /tmp/clio-workspace/PR_INFO.md | ||
| /tmp/clio-workspace/PR_DIFF.txt | ||
| retention-days: 7 | ||