Add Claude Code workflow for AI-assisted PR reviews #19
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 Code | |
| # AI-assisted PR reviews and interactive @claude mentions. | |
| # Authentication to the Databricks Model Serving endpoint (which hosts | |
| # the Claude model) is handled via GitHub OIDC federation so this | |
| # workflow does not need any secrets. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| issue_comment: | |
| types: [created] | |
| pull_request_review_comment: | |
| types: [created] | |
| jobs: | |
| # Automatic PR review when a PR is opened or updated. | |
| review: | |
| if: github.event_name == 'pull_request' | |
| runs-on: | |
| group: databricks-deco-testing-runner-group | |
| labels: ubuntu-latest-deco | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Get GitHub OIDC token | |
| id: github-oidc | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const token = await core.getIDToken('968367da-7edd-44f7-9dea-3e0b20b0ec97'); | |
| core.setSecret(token); | |
| core.setOutput('token', token); | |
| - name: Get Databricks OAuth token | |
| id: oauth | |
| run: | | |
| HTTP_CODE=$(curl -s -o /tmp/token_response.json -w "%{http_code}" -X POST \ | |
| "https://accounts.cloud.databricks.com/oidc/accounts/968367da-7edd-44f7-9dea-3e0b20b0ec97/v1/token" \ | |
| -H "Content-Type: application/x-www-form-urlencoded" \ | |
| -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ | |
| -d "subject_token=${SUBJECT_TOKEN}" \ | |
| -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ | |
| -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ | |
| -d "scope=all-apis") | |
| echo "Token exchange HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Token exchange failed with HTTP $HTTP_CODE" | |
| cat /tmp/token_response.json | |
| exit 1 | |
| fi | |
| ACCESS_TOKEN=$(cat /tmp/token_response.json | jq -r .access_token) | |
| echo "::add-mask::${ACCESS_TOKEN}" | |
| echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" | |
| env: | |
| SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} | |
| - name: Validate workspace connectivity | |
| run: | | |
| echo "=== Step 1: Simple workspace API call ===" | |
| HTTP_CODE=$(curl -s -o /tmp/ws_response.json -w "%{http_code}" \ | |
| "https://dbc-1232e87d-9384.cloud.databricks.com/api/2.0/clusters/spark-versions" \ | |
| -H "Authorization: Bearer ${ACCESS_TOKEN}") | |
| echo "Workspace API HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Workspace API call failed with HTTP $HTTP_CODE" | |
| cat /tmp/ws_response.json | |
| exit 1 | |
| fi | |
| echo "Workspace API: OK" | |
| echo "" | |
| echo "=== Step 2: Model serving endpoint query ===" | |
| HTTP_CODE=$(curl -s -o /tmp/ms_response.json -w "%{http_code}" \ | |
| "https://dbc-1232e87d-9384.cloud.databricks.com/serving-endpoints/anthropic/v1/messages" \ | |
| -H "Authorization: Bearer ${ACCESS_TOKEN}" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"model":"databricks-claude-opus-4-6","max_tokens":20,"messages":[{"role":"user","content":"Say OK"}]}') | |
| echo "Model serving HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Model serving endpoint failed with HTTP $HTTP_CODE" | |
| cat /tmp/ms_response.json | |
| exit 1 | |
| fi | |
| echo "Model serving response:" | |
| cat /tmp/ms_response.json | jq -r '.content[0].text // "no text"' | |
| echo "Model serving: OK" | |
| env: | |
| ACCESS_TOKEN: ${{ steps.oauth.outputs.token }} | |
| - name: Build settings | |
| id: config | |
| run: | | |
| TOOLS_JSON=$(cat << 'TOOLS' | |
| ["Bash(pr-diff)", "Bash(pr-diff *)", "Bash(pr-view)", "Bash(pr-view *)", "Bash(pr-comment *)", "Bash(git log)", "Bash(git log *)", "Bash(git diff)", "Bash(git diff *)", "Bash(git show *)", "Bash(grep *)", "Read", "Write", "Glob", "Grep"] | |
| TOOLS | |
| ) | |
| SETTINGS=$(jq -n \ | |
| --argjson tools "$TOOLS_JSON" \ | |
| --arg auth_token "$OAUTH_TOKEN" \ | |
| '{ | |
| env: { | |
| ANTHROPIC_BASE_URL: "https://dbc-1232e87d-9384.cloud.databricks.com/serving-endpoints/anthropic", | |
| ANTHROPIC_AUTH_TOKEN: $auth_token, | |
| ANTHROPIC_MODEL: "databricks-claude-opus-4-6", | |
| DISABLE_PROMPT_CACHING: "1", | |
| CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1" | |
| }, | |
| permissions: { | |
| allow: $tools | |
| } | |
| }') | |
| echo "settings<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$SETTINGS" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| env: | |
| OAUTH_TOKEN: ${{ steps.oauth.outputs.token }} | |
| - name: Create PR-scoped helper scripts | |
| run: | | |
| NUMBER="$PR_NUMBER" | |
| printf '#!/bin/bash\nexec gh pr comment %s "$@"\n' "$NUMBER" > /usr/local/bin/pr-comment | |
| printf '#!/bin/bash\nexec gh pr diff %s "$@"\n' "$NUMBER" > /usr/local/bin/pr-diff | |
| printf '#!/bin/bash\nexec gh pr view %s "$@"\n' "$NUMBER" > /usr/local/bin/pr-view | |
| chmod +x /usr/local/bin/pr-comment /usr/local/bin/pr-diff /usr/local/bin/pr-view | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Run Claude Code | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| anthropic_api_key: ${{ steps.oauth.outputs.token }} | |
| github_token: ${{ github.token }} | |
| prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. Write your review to a temporary file using the Write tool, then post it with: pr-comment --body-file <path>. If you have no issues to raise, post a short comment saying the PR looks good." | |
| claude_args: "--max-turns 100" | |
| trigger_phrase: "@claude" | |
| settings: ${{ steps.config.outputs.settings }} | |
| show_full_output: true | |
| # Interactive @claude mentions — Claude can make changes and push commits. | |
| assist: | |
| 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')) | |
| runs-on: | |
| group: databricks-deco-testing-runner-group | |
| labels: ubuntu-latest-deco | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Get GitHub OIDC token | |
| id: github-oidc | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const token = await core.getIDToken('968367da-7edd-44f7-9dea-3e0b20b0ec97'); | |
| core.setSecret(token); | |
| core.setOutput('token', token); | |
| - name: Get Databricks OAuth token | |
| id: oauth | |
| run: | | |
| HTTP_CODE=$(curl -s -o /tmp/token_response.json -w "%{http_code}" -X POST \ | |
| "https://accounts.cloud.databricks.com/oidc/accounts/968367da-7edd-44f7-9dea-3e0b20b0ec97/v1/token" \ | |
| -H "Content-Type: application/x-www-form-urlencoded" \ | |
| -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ | |
| -d "subject_token=${SUBJECT_TOKEN}" \ | |
| -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ | |
| -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ | |
| -d "scope=all-apis") | |
| echo "Token exchange HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Token exchange failed with HTTP $HTTP_CODE" | |
| cat /tmp/token_response.json | |
| exit 1 | |
| fi | |
| ACCESS_TOKEN=$(cat /tmp/token_response.json | jq -r .access_token) | |
| echo "::add-mask::${ACCESS_TOKEN}" | |
| echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" | |
| env: | |
| SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} | |
| - name: Validate workspace connectivity | |
| run: | | |
| echo "=== Step 1: Simple workspace API call ===" | |
| HTTP_CODE=$(curl -s -o /tmp/ws_response.json -w "%{http_code}" \ | |
| "https://dbc-1232e87d-9384.cloud.databricks.com/api/2.0/clusters/spark-versions" \ | |
| -H "Authorization: Bearer ${ACCESS_TOKEN}") | |
| echo "Workspace API HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Workspace API call failed with HTTP $HTTP_CODE" | |
| cat /tmp/ws_response.json | |
| exit 1 | |
| fi | |
| echo "Workspace API: OK" | |
| echo "" | |
| echo "=== Step 2: Model serving endpoint query ===" | |
| HTTP_CODE=$(curl -s -o /tmp/ms_response.json -w "%{http_code}" \ | |
| "https://dbc-1232e87d-9384.cloud.databricks.com/serving-endpoints/anthropic/v1/messages" \ | |
| -H "Authorization: Bearer ${ACCESS_TOKEN}" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"model":"databricks-claude-opus-4-6","max_tokens":20,"messages":[{"role":"user","content":"Say OK"}]}') | |
| echo "Model serving HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Model serving endpoint failed with HTTP $HTTP_CODE" | |
| cat /tmp/ms_response.json | |
| exit 1 | |
| fi | |
| echo "Model serving response:" | |
| cat /tmp/ms_response.json | jq -r '.content[0].text // "no text"' | |
| echo "Model serving: OK" | |
| env: | |
| ACCESS_TOKEN: ${{ steps.oauth.outputs.token }} | |
| - name: Build settings | |
| id: config | |
| run: | | |
| TOOLS_JSON=$(cat << 'TOOLS' | |
| ["Bash(make lint)", "Bash(make test)", "Bash(make fmt)", "Bash(make schema)", "Bash(go build *)", "Bash(go test *)", "Bash(go vet)", "Bash(go vet *)", "Bash(git add *)", "Bash(git commit *)", "Bash(git diff)", "Bash(git diff *)", "Bash(git log)", "Bash(git log *)", "Bash(git status)", "Bash(git show *)", "Bash(pr-comment *)", "Bash(pr-push)", "Bash(pr-push *)", "Bash(pr-view)", "Bash(pr-view *)", "Bash(grep *)", "Read", "Edit", "Write", "Glob", "Grep"] | |
| TOOLS | |
| ) | |
| SETTINGS=$(jq -n \ | |
| --argjson tools "$TOOLS_JSON" \ | |
| --arg auth_token "$OAUTH_TOKEN" \ | |
| '{ | |
| env: { | |
| ANTHROPIC_BASE_URL: "https://dbc-1232e87d-9384.cloud.databricks.com/serving-endpoints/anthropic", | |
| ANTHROPIC_AUTH_TOKEN: $auth_token, | |
| ANTHROPIC_MODEL: "databricks-claude-opus-4-6", | |
| DISABLE_PROMPT_CACHING: "1", | |
| CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1" | |
| }, | |
| permissions: { | |
| allow: $tools | |
| } | |
| }') | |
| echo "settings<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$SETTINGS" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| env: | |
| OAUTH_TOKEN: ${{ steps.oauth.outputs.token }} | |
| - name: Create PR-scoped helper scripts | |
| run: | | |
| NUMBER="$ISSUE_NUMBER" | |
| BRANCH=$(gh pr view "$NUMBER" --json headRefName -q .headRefName) | |
| printf '#!/bin/bash\nexec gh pr comment %s "$@"\n' "$NUMBER" > /usr/local/bin/pr-comment | |
| printf '#!/bin/bash\nexec git push origin "HEAD:refs/heads/%s" "$@"\n' "$BRANCH" > /usr/local/bin/pr-push | |
| printf '#!/bin/bash\nexec gh pr view %s "$@"\n' "$NUMBER" > /usr/local/bin/pr-view | |
| chmod +x /usr/local/bin/pr-comment /usr/local/bin/pr-push /usr/local/bin/pr-view | |
| env: | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Run Claude Code | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| anthropic_api_key: ${{ steps.oauth.outputs.token }} | |
| github_token: ${{ github.token }} | |
| claude_args: "--max-turns 100" | |
| trigger_phrase: "@claude" | |
| settings: ${{ steps.config.outputs.settings }} | |
| show_full_output: true |