Add Claude AI workflows for automated code review and PR management #3
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: Claude AI Review | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request: | |
| types: [opened, synchronize] | |
| branches: [main] | |
| pull_request_review_comment: | |
| types: [created] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| claude-review: | |
| name: Claude Auto Review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' | |
| steps: | |
| - name: Checkout PR | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get changed files | |
| id: changed-files | |
| uses: tj-actions/changed-files@v44 | |
| with: | |
| separator: "," | |
| - name: Check for stale/out-of-scope files | |
| id: scope-check | |
| run: | | |
| echo "🔍 Checking for stale files and out-of-scope changes..." > scope_check.md | |
| echo "" >> scope_check.md | |
| SCOPE_ISSUES=0 | |
| CHANGED_FILES="${{ steps.changed-files.outputs.all_changed_files }}" | |
| echo "### Stale File Check" >> scope_check.md | |
| for file in $CHANGED_FILES; do | |
| if [ -f "$file" ]; then | |
| FILE_AGE=$(git log -1 --format="%cr" origin/main -- "$file" 2>/dev/null || echo "new file") | |
| FILE_DATE=$(git log -1 --format="%ci" origin/main -- "$file" 2>/dev/null || echo "") | |
| if [ -n "$FILE_DATE" ]; then | |
| YEARS_OLD=$(( ($(date +%s) - $(date -d "$FILE_DATE" +%s)) / 31536000 )) | |
| if [ $YEARS_OLD -gt 2 ]; then | |
| echo "⚠️ **WARNING**: \`$file\` is $YEARS_OLD years old (last modified: $FILE_AGE)" >> scope_check.md | |
| SCOPE_ISSUES=$((SCOPE_ISSUES + 1)) | |
| fi | |
| fi | |
| fi | |
| done | |
| if [ $SCOPE_ISSUES -eq 0 ]; then | |
| echo "✅ No stale files detected" >> scope_check.md | |
| fi | |
| echo "" >> scope_check.md | |
| echo "### Scope Check" >> scope_check.md | |
| echo "Checking file relevance (blocks binaries, temp files, etc.)..." >> scope_check.md | |
| echo "" >> scope_check.md | |
| RELEVANT_PATTERNS=( | |
| "lib/" | |
| "test/" | |
| "config/" | |
| "priv/" | |
| "mix.exs" | |
| "mix.lock" | |
| ".github/" | |
| ".githooks/" | |
| "flake.nix" | |
| "flake.lock" | |
| ".formatter.exs" | |
| ".credo.exs" | |
| "README.md" | |
| "CHANGELOG.md" | |
| "LICENSE" | |
| "CONTRIBUTING.md" | |
| "*.md" | |
| "scripts/" | |
| ".envrc" | |
| "renovate.json5" | |
| ) | |
| OUT_OF_SCOPE_FILES=() | |
| for file in $CHANGED_FILES; do | |
| MATCHES_PATTERN=false | |
| for pattern in "${RELEVANT_PATTERNS[@]}"; do | |
| if [[ "$file" == $pattern ]] || [[ "$file" == *"$pattern"* ]]; then | |
| MATCHES_PATTERN=true | |
| break | |
| fi | |
| done | |
| if [ "$MATCHES_PATTERN" = false ]; then | |
| case "$file" in | |
| *.exe|*.dll|*.so|*.dylib|*.bin|*.dat|*.tmp|*.log|*.beam|node_modules/*|.DS_Store|thumbs.db|_build/*|deps/*) | |
| echo "❌ **OUT OF SCOPE**: \`$file\` - Binary/temp/build file not relevant to library" >> scope_check.md | |
| OUT_OF_SCOPE_FILES+=("$file") | |
| SCOPE_ISSUES=$((SCOPE_ISSUES + 1)) | |
| ;; | |
| *.jpg|*.png|*.gif|*.mp4|*.avi|*.mov) | |
| if [[ ! "$file" =~ ^docs/ ]] && [[ ! "$file" =~ ^assets/ ]]; then | |
| echo "⚠️ **REVIEW NEEDED**: \`$file\` - Media file, ensure it's for documentation" >> scope_check.md | |
| fi | |
| ;; | |
| esac | |
| fi | |
| done | |
| if [ ${#OUT_OF_SCOPE_FILES[@]} -eq 0 ]; then | |
| echo "✅ All changes appear relevant (includes .github/ workflows, lib/, test/, config)" >> scope_check.md | |
| fi | |
| echo "" >> scope_check.md | |
| GITHUB_FILES=$(echo "$CHANGED_FILES" | tr ' ' '\n' | grep -c "^.github/" || true) | |
| if [ "$GITHUB_FILES" -gt 0 ]; then | |
| echo "ℹ️ **Note**: $GITHUB_FILES .github/ file(s) changed - workflows/actions are critical infrastructure" >> scope_check.md | |
| echo "" >> scope_check.md | |
| fi | |
| echo "SCOPE_ISSUES=$SCOPE_ISSUES" >> $GITHUB_OUTPUT | |
| cat scope_check.md | |
| - name: Claude Code Review | |
| id: claude-review | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| show_full_output: true | |
| prompt: | | |
| Review this PR for an Elixir workflow orchestration library (Singularity.Workflow). | |
| Focus on: | |
| 1. Elixir code quality (mix format, credo checks) | |
| 2. Security vulnerabilities (SQL injection, XSS, etc.) | |
| 3. Test coverage (ExUnit tests) | |
| 4. Documentation completeness (@doc, @moduledoc) | |
| 5. Whether changes are appropriate for a library package | |
| Scope check results: | |
| $(cat scope_check.md 2>/dev/null || echo "No scope issues") | |
| Provide a concise review. If the code looks good, say "LGTM - auto-approving". | |
| If there are issues, list them clearly. | |
| claude_args: "--max-turns 3 --model sonnet" | |
| - name: Determine auto-approve | |
| id: claude-analysis | |
| run: | | |
| SCOPE_ISSUES=${{ steps.scope-check.outputs.SCOPE_ISSUES }} | |
| if [ $SCOPE_ISSUES -eq 0 ]; then | |
| echo "AUTO_APPROVE=true" >> $GITHUB_OUTPUT | |
| echo "✅ No scope issues detected - eligible for auto-approve" | |
| else | |
| echo "AUTO_APPROVE=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ $SCOPE_ISSUES scope issue(s) found - human review required" | |
| fi | |
| - name: Post scope check results | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let comment_body = '## 🔍 Automated Checks\n\n'; | |
| try { | |
| const scope_check = fs.readFileSync('scope_check.md', 'utf8'); | |
| comment_body += scope_check; | |
| } catch (error) { | |
| comment_body += '✅ Scope check passed\n'; | |
| } | |
| comment_body += '\n---\n'; | |
| comment_body += '*Claude is reviewing the code... Check the "Claude Code Review" step for detailed feedback.*\n'; | |
| const pr_number = context.payload.pull_request.number; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr_number, | |
| body: comment_body | |
| }); | |
| - name: Set review status | |
| id: review-status | |
| uses: actions/github-script@v7 | |
| env: | |
| AUTO_APPROVE: ${{ steps.claude-analysis.outputs.AUTO_APPROVE }} | |
| with: | |
| github-token: ${{ secrets.ORG_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const pr_number = context.payload.pull_request.number; | |
| const auto_approve = process.env.AUTO_APPROVE === 'true'; | |
| await github.rest.pulls.createReview({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number, | |
| body: auto_approve | |
| ? '✅ Claude AI approved this PR! All checks passed. Will auto-merge when CI is green.' | |
| : '⚠️ Claude AI review found issues. Human review required.', | |
| event: auto_approve ? 'APPROVE' : 'COMMENT' | |
| }); | |
| return { auto_approve }; | |
| - name: Enable auto-merge | |
| if: steps.claude-analysis.outputs.AUTO_APPROVE == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| gh pr merge $PR_NUMBER --auto --squash || echo "Auto-merge may already be enabled or not allowed" | |
| echo "✅ Auto-merge enabled! PR will merge automatically when:" | |
| echo " - All required checks pass (test, quality, coverage)" | |
| echo " - All conversations are resolved" | |
| echo " - Branch is up to date with main" |