From 1a52c30b268947d9c9ab9103786e9d60bb4711a4 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:07:27 -0500 Subject: [PATCH 1/2] factor out the check team membership step --- .github/README.md | 71 ++++++++++++ .github/claude-issue-actions.yml | 148 +++++++++++++++++++++++++ .github/workflows/claude-pr-review.yml | 79 +++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 .github/README.md create mode 100644 .github/claude-issue-actions.yml create mode 100644 .github/workflows/claude-pr-review.yml diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..c14d44954 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,71 @@ +# GitHub Automation Tools + +This directory contains GitHub Actions workflows that use Claude to automate various development tasks. + +## Available Actions + +### Claude PR Review (`/claude review`) + +Triggers an AI-powered code review on a pull request. Reviews code quality, potential bugs, security implications, and performance considerations. + +**Usage:** Comment `/claude review` on any pull request + +**Workflow file:** [workflows/claude-pr-review.yml](workflows/claude-pr-review.yml) + +--- + +### Claude Issue Actions + +Automates bug fixes, feature implementations, and issue investigations directly from GitHub issues. + +**Workflow file:** `.github-issue-actions.yml` (in root directory) + +**Available commands:** + +#### `/claude fix` +Creates a PR to fix a bug. Analyzes the issue, creates a fix branch, implements the fix with tests, and opens a PR. + +#### `/claude implement` +Creates a PR to implement a feature. Creates a feature branch, implements following project patterns, adds tests and documentation, and opens a PR. + +#### `/claude investigate` +Investigates an issue and posts detailed analysis including root cause, recommended solution, and affected files. Does not create a PR. + +--- + +### Adding Custom Instructions + +All commands support optional custom instructions that get passed directly to Claude's prompt: + +``` +/claude fix + +Ensure backwards compatibility with Python 3.8 and add comprehensive unit tests +``` + +``` +/claude implement + +Follow the pattern used in src/services/auth.py +``` + +``` +/claude investigate + +Check for interactions with the new caching layer added in PR #123 +``` + +Use custom instructions to specify implementation details, highlight constraints, or provide additional context. + +--- + +## Requirements + +### Permissions +- **Team membership**: Must be a member of the authorized team to trigger actions +- **GitHub token permissions**: Workflows require: + - PR Review: `contents: read`, `pull-requests: write`, `id-token: write` + - Issue Actions: `contents: write`, `issues: write`, `pull-requests: write`, `id-token: write` + +### Secrets +- `ANTHROPIC_API_KEY`: Required secret configured in repository settings (managed by repository administrators) diff --git a/.github/claude-issue-actions.yml b/.github/claude-issue-actions.yml new file mode 100644 index 000000000..62d05ee8e --- /dev/null +++ b/.github/claude-issue-actions.yml @@ -0,0 +1,148 @@ +name: Claude Issue Actions +on: + issue_comment: + types: [created] + +jobs: + handle-issue: + if: | + !github.event.issue.pull_request && ( + contains(github.event.comment.body, '/claude fix') || + contains(github.event.comment.body, '/claude implement') || + contains(github.event.comment.body, '/claude investigate') + ) + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v5 + + - name: Check team membership + uses: ./.github/actions/check-team-membership + with: + org: nebari-dev + team: Contributors + username: ${{ github.event.comment.user.login }} + issue_number: ${{ github.event.issue.number }} + comment_type: issue + github_token: ${{ github.token }} + + - name: Determine action type + id: action_type + run: | + COMMENT="${{ github.event.comment.body }}" + if echo "$COMMENT" | grep -q "/claude implement"; then + echo "type=implement" >> $GITHUB_OUTPUT + echo "branch_prefix=feature" >> $GITHUB_OUTPUT + elif echo "$COMMENT" | grep -q "/claude investigate"; then + echo "type=investigate" >> $GITHUB_OUTPUT + echo "branch_prefix=none" >> $GITHUB_OUTPUT + else + echo "type=fix" >> $GITHUB_OUTPUT + echo "branch_prefix=fix" >> $GITHUB_OUTPUT + fi + + - name: Extract custom instructions + id: instructions + run: | + COMMENT="${{ github.event.comment.body }}" + # Extract everything after the command + CUSTOM_INSTRUCTIONS=$(echo "$COMMENT" | sed -n 's/.*\/claude \(fix\|implement\|investigate\)\s*//p') + + if [ -z "$CUSTOM_INSTRUCTIONS" ]; then + echo "instructions=No additional instructions provided." >> $GITHUB_OUTPUT + else + echo "instructions<> $GITHUB_OUTPUT + echo "$CUSTOM_INSTRUCTIONS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Fix Bug + if: steps.action_type.outputs.type == 'fix' + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + track_progress: true + prompt: | + REPO: ${{ github.repository }} + ISSUE: #${{ github.event.issue.number }} - ${{ github.event.issue.title }} + REQUESTED BY: @${{ github.event.comment.user.login }} + + ISSUE DESCRIPTION: + ${{ github.event.issue.body }} + + ADDITIONAL INSTRUCTIONS: + ${{ steps.instructions.outputs.instructions }} + + Create a PR to fix this bug: + + 1. Analyze the issue and additional instructions + 2. Create branch: "fix/issue-${{ github.event.issue.number }}-" + 3. Implement the fix + 4. Add/update tests to prevent regression + 5. Commit: "fix: (fixes #${{ github.event.issue.number }})" + 6. Push and create PR with "Fixes #${{ github.event.issue.number }}" + 7. Comment on the original issue with PR link + + claude_args: | + --allowedTools "Read,Write,Edit,Bash(git:*),Bash(gh pr:*),Bash(gh issue:*),Bash(npm:*),Bash(pytest:*)" + + - name: Implement Feature + if: steps.action_type.outputs.type == 'implement' + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + track_progress: true + prompt: | + REPO: ${{ github.repository }} + ISSUE: #${{ github.event.issue.number }} - ${{ github.event.issue.title }} + REQUESTED BY: @${{ github.event.comment.user.login }} + + FEATURE REQUEST: + ${{ github.event.issue.body }} + + ADDITIONAL INSTRUCTIONS: + ${{ steps.instructions.outputs.instructions }} + + Create a PR to implement this feature: + + 1. Analyze requirements and additional instructions + 2. Create branch: "feature/issue-${{ github.event.issue.number }}-" + 3. Implement the feature following project patterns + 4. Add comprehensive tests + 5. Update relevant documentation + 6. Commit: "feat: (implements #${{ github.event.issue.number }})" + 7. Push and create PR with "Closes #${{ github.event.issue.number }}" + 8. Comment on the original issue with PR link + + claude_args: | + --allowedTools "Read,Write,Edit,Bash(git:*),Bash(gh pr:*),Bash(gh issue:*),Bash(npm:*),Bash(pytest:*)" + + - name: Investigate Issue + if: steps.action_type.outputs.type == 'investigate' + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + track_progress: true + prompt: | + REPO: ${{ github.repository }} + ISSUE: #${{ github.event.issue.number }} - ${{ github.event.issue.title }} + REQUESTED BY: @${{ github.event.comment.user.login }} + + ISSUE TO INVESTIGATE: + ${{ github.event.issue.body }} + + INVESTIGATION FOCUS: + ${{ steps.instructions.outputs.instructions }} + + Investigate this issue and provide analysis: + + 1. Review the codebase to understand the issue + 2. Consider the investigation focus provided above + 3. Identify root cause and contributing factors + 4. Search for similar issues or patterns in the codebase + 5. Post a detailed comment with: + - **Root Cause**: What's causing this issue diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml new file mode 100644 index 000000000..3ff409457 --- /dev/null +++ b/.github/workflows/claude-pr-review.yml @@ -0,0 +1,79 @@ +name: Claude PR Review +on: + issue_comment: + types: [created] + +jobs: + review: + if: | + github.event.issue.pull_request && + contains(github.event.comment.body, '/claude review') + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v5 + + - name: Check team membership + uses: ./.github/actions/check-team-membership + with: + org: nebari-dev + team: Contributors + username: ${{ github.event.comment.user.login }} + issue_number: ${{ github.event.issue.number }} + comment_type: pr + github_token: ${{ github.token }} + + - name: Get PR branch + id: pr + run: | + PR_DATA=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json headRefName) + echo "branch=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Extract custom instructions + id: instructions + run: | + COMMENT="${{ github.event.comment.body }}" + # Extract everything after "/claude review" + CUSTOM_INSTRUCTIONS=$(echo "$COMMENT" | sed -n 's/.*\/claude review\s*//p') + + if [ -z "$CUSTOM_INSTRUCTIONS" ]; then + echo "instructions=No additional instructions provided." >> $GITHUB_OUTPUT + else + echo "instructions<> $GITHUB_OUTPUT + echo "$CUSTOM_INSTRUCTIONS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Checkout PR branch + run: | + git fetch origin ${{ steps.pr.outputs.branch }} + git checkout ${{ steps.pr.outputs.branch }} + + - uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + track_progress: true + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.issue.number }} + REQUESTED BY: @${{ github.event.comment.user.login }} + + ADDITIONAL INSTRUCTIONS: + ${{ steps.instructions.outputs.instructions }} + + Review this pull request focusing on: + - Code quality and best practices + - Potential bugs or issues + - Security implications + - Performance considerations + + Use inline comments for specific issues. + Post a summary when complete. + + claude_args: | + --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" From 5543acf642b21f904bdef2150fefbeec609a7b6d Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:08:19 -0500 Subject: [PATCH 2/2] factor out the check team membership step --- .../actions/check-team-membership/action.yml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/actions/check-team-membership/action.yml diff --git a/.github/actions/check-team-membership/action.yml b/.github/actions/check-team-membership/action.yml new file mode 100644 index 000000000..fee0b3c61 --- /dev/null +++ b/.github/actions/check-team-membership/action.yml @@ -0,0 +1,44 @@ +name: 'Check Team Membership' +description: 'Verify that the user is a member of the authorized team' +inputs: + org: + description: 'GitHub organization name' + required: true + team: + description: 'Team name (slug)' + required: true + username: + description: 'GitHub username to check' + required: true + issue_number: + description: 'Issue or PR number for posting error message' + required: true + comment_type: + description: 'Type of comment (issue or pr)' + required: true + default: 'issue' + github_token: + description: 'GitHub token for API access' + required: true +runs: + using: 'composite' + steps: + - name: Check team membership + shell: bash + run: | + TEAM_CHECK=$(gh api \ + /orgs/${{ inputs.org }}/teams/${{ inputs.team }}/memberships/${{ inputs.username }} \ + --silent 2>&1 || echo "not_member") + + if [[ "$TEAM_CHECK" == *"not_member"* ]] || [[ "$TEAM_CHECK" == *"404"* ]]; then + if [[ "${{ inputs.comment_type }}" == "pr" ]]; then + gh pr comment ${{ inputs.issue_number }} --repo ${{ github.repository }} \ + --body "⚠️ Only members of @${{ inputs.org }}/${{ inputs.team }} can trigger Claude actions." + else + gh issue comment ${{ inputs.issue_number }} --repo ${{ github.repository }} \ + --body "⚠️ Only members of @${{ inputs.org }}/${{ inputs.team }} can trigger Claude actions." + fi + exit 1 + fi + env: + GH_TOKEN: ${{ inputs.github_token }}