From 272727f16834641b7eef56697cc80831978746c6 Mon Sep 17 00:00:00 2001 From: Daniel Ochoa Date: Wed, 6 May 2026 10:13:45 -0500 Subject: [PATCH] Add code review to closedloop-electron --- .github/workflows/claude-code-review.yml | 70 +++++++++++++++++++++--- .github/workflows/claude.yml | 50 +++++++++++++++++ 2 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/claude.yml diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 36cab808..202b4684 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -1,13 +1,12 @@ name: Claude Code Review on: + pull_request: + types: [opened] + pull_request_target: + types: [opened] workflow_dispatch: -# Cancel in-progress runs for the same PR to prevent duplicate reviews -concurrency: - group: claude-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - jobs: claude-review: # Filter by PR author or allow bot actors like Dependabot @@ -32,7 +31,7 @@ jobs: steps: - name: Generate GitHub App Token id: generate_token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ secrets.CLOSEDLOOP_APP_ID_STAGE }} private-key: ${{ secrets.CLOSEDLOOP_APP_SECRET_STAGE }} @@ -80,10 +79,40 @@ jobs: run: | git config --global url."https://x-access-token:${{ steps.generate_token.outputs.token }}@github.com/".insteadOf "https://github.com/" + - name: Install Claude CLI + shell: bash + run: | + set -e + # claude.ai/install.sh redirects here; use direct URL to avoid Cloudflare bot challenge in CI + for i in 1 2 3; do + echo "Attempt $i: Installing Claude CLI..." + if curl -fsSL https://downloads.claude.ai/claude-code-releases/bootstrap.sh | bash; then + break + fi + [ "$i" -eq 3 ] && { echo "::error::Failed to install Claude CLI"; exit 1; } + sleep 5 + done + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Verify Claude CLI + shell: bash + run: | + if ! command -v claude &> /dev/null; then + echo "::error::Claude CLI not found in PATH" + exit 1 + fi + claude --version + + - name: Ensure ClosedLoop Workspace + shell: bash + run: mkdir -p .closedloop-ai + - name: Run Claude Code Review id: claude-review continue-on-error: true uses: anthropics/claude-code-action@v1 + env: + CR_GLOBAL_CACHE: 1 with: github_token: ${{ steps.generate_token.outputs.token }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_NEW }} @@ -113,7 +142,7 @@ jobs: GH_TOKEN: ${{ steps.generate_token.outputs.token }} shell: bash run: | - THREADS_FILE=".claude/code-review-threads.json" + THREADS_FILE=".closedloop-ai/code-review-threads.json" if [ ! -f "$THREADS_FILE" ]; then echo "No threads file — skipping." exit 0 @@ -132,7 +161,7 @@ jobs: GITHUB_REPOSITORY: ${{ github.repository }} shell: bash run: | - FINDINGS_FILE=".claude/code-review-findings.json" + FINDINGS_FILE=".closedloop-ai/code-review-findings.json" if [ ! -f "$FINDINGS_FILE" ]; then echo "No findings file — skipping." exit 0 @@ -144,6 +173,29 @@ jobs: fi python3 "$HELPERS" post-comments --findings "$FINDINGS_FILE" + - name: Validate migrated review artifacts + id: validate-review-artifacts + if: always() && steps.claude-review.outcome == 'success' + shell: bash + run: | + ARTIFACT_COUNT=0 + for FILE in \ + ".closedloop-ai/code-review-findings.json" \ + ".closedloop-ai/code-review-threads.json" \ + ".closedloop-ai/code-review-summary.md"; do + if [ -f "$FILE" ]; then + ARTIFACT_COUNT=$((ARTIFACT_COUNT + 1)) + fi + done + + if [ "$ARTIFACT_COUNT" -eq 0 ]; then + echo "::warning::Claude review completed successfully but produced no .closedloop-ai/code-review-* artifacts." + echo "::warning::This usually means the runtime/plugin path contract is out of sync." + echo "missing=true" >> "$GITHUB_OUTPUT" + else + echo "missing=false" >> "$GITHUB_OUTPUT" + fi + - name: Post code review summary id: post-summary if: always() @@ -160,7 +212,7 @@ jobs: exit 0 fi - SUMMARY_FILE=".claude/code-review-summary.md" + SUMMARY_FILE=".closedloop-ai/code-review-summary.md" if [ -f "$SUMMARY_FILE" ]; then SUMMARY=$(cat "$SUMMARY_FILE") diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 00000000..6b15fac7 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,50 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr *)' +