From f12e66b1a17cd3e46626c85cef90f259769e06dc Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 17:28:29 +0100 Subject: [PATCH 01/28] Add Claude Code workflow for AI-assisted PR reviews Add a workflow that calls the reusable Claude Code workflow in eng-dev-ecosystem. Provides two modes: - Automatic PR review on open/sync (read-only) - Interactive @claude mentions for code changes Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/claude-code.yml diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml new file mode 100644 index 0000000000..2da3baca44 --- /dev/null +++ b/.github/workflows/claude-code.yml @@ -0,0 +1,46 @@ +name: Claude Code + +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' + uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main + secrets: + DATABRICKS_SP_CLIENT_ID: ${{ secrets.DATABRICKS_SP_CLIENT_ID }} + DATABRICKS_SP_CLIENT_SECRET: ${{ secrets.DATABRICKS_SP_CLIENT_SECRET }} + with: + prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md." + allowed_tools: "Read,Glob,Grep" + max_turns: "5" + + # 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')) + uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main + secrets: + DATABRICKS_SP_CLIENT_ID: ${{ secrets.DATABRICKS_SP_CLIENT_ID }} + DATABRICKS_SP_CLIENT_SECRET: ${{ secrets.DATABRICKS_SP_CLIENT_SECRET }} + with: + allowed_tools: | + Bash(make lint) + Bash(make test) + Bash(make fmt) + Bash(go build ./...) + Read + Edit + Write + Glob + Grep + max_turns: "10" From fe063a01bd7a2b98f739e49fc3ee80bbb2fb2ce7 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 17:41:28 +0100 Subject: [PATCH 02/28] Fix claude-code-action inputs to use claude_args and settings Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 2da3baca44..cebeba7eb6 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -20,8 +20,11 @@ jobs: DATABRICKS_SP_CLIENT_SECRET: ${{ secrets.DATABRICKS_SP_CLIENT_SECRET }} with: prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md." - allowed_tools: "Read,Glob,Grep" - max_turns: "5" + allowed_tools: | + Read + Glob + Grep + claude_args: "--max-turns 5" # Interactive @claude mentions — Claude can make changes and push commits. assist: @@ -43,4 +46,4 @@ jobs: Write Glob Grep - max_turns: "10" + claude_args: "--max-turns 10" From f2bc990a0a787bd538f8aa8d2d4c8a7c0246a3ca Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 18:18:41 +0100 Subject: [PATCH 03/28] Increase max turns for Claude Code workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review: 5 → 10 turns. Assist: 10 → 20 turns. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index cebeba7eb6..54ee702b9d 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -24,7 +24,7 @@ jobs: Read Glob Grep - claude_args: "--max-turns 5" + claude_args: "--max-turns 10" # Interactive @claude mentions — Claude can make changes and push commits. assist: @@ -46,4 +46,4 @@ jobs: Write Glob Grep - claude_args: "--max-turns 10" + claude_args: "--max-turns 20" From 47a7034582c9caba8387afdf4d91a6beb9301fa9 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 18:57:24 +0100 Subject: [PATCH 04/28] Remove secrets from Claude Code workflow caller The reusable Claude Code workflow in eng-dev-ecosystem now uses GitHub OIDC federation instead of static secrets, so callers no longer need to pass any credentials. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 32 ++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 54ee702b9d..3a518f353c 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -15,16 +15,20 @@ jobs: review: if: github.event_name == 'pull_request' uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main - secrets: - DATABRICKS_SP_CLIENT_ID: ${{ secrets.DATABRICKS_SP_CLIENT_ID }} - DATABRICKS_SP_CLIENT_SECRET: ${{ secrets.DATABRICKS_SP_CLIENT_SECRET }} with: - prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md." + prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. Post your review as a PR comment. If you have no issues to raise, post a short comment saying the PR looks good." allowed_tools: | + Bash(pr-diff) + Bash(pr-view) + Bash(pr-comment) + Bash(git log) + Bash(git diff) + Bash(git show) + Bash(grep) Read Glob Grep - claude_args: "--max-turns 10" + claude_args: "--max-turns 100" # Interactive @claude mentions — Claude can make changes and push commits. assist: @@ -32,18 +36,28 @@ jobs: (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main - secrets: - DATABRICKS_SP_CLIENT_ID: ${{ secrets.DATABRICKS_SP_CLIENT_ID }} - DATABRICKS_SP_CLIENT_SECRET: ${{ secrets.DATABRICKS_SP_CLIENT_SECRET }} with: allowed_tools: | Bash(make lint) Bash(make test) Bash(make fmt) + Bash(make schema) Bash(go build ./...) + Bash(go test) + Bash(go vet) + Bash(git add) + Bash(git commit) + Bash(git diff) + Bash(git log) + Bash(git status) + Bash(git show) + Bash(pr-comment) + Bash(pr-push) + Bash(pr-view) + Bash(grep) Read Edit Write Glob Grep - claude_args: "--max-turns 20" + claude_args: "--max-turns 100" From 5eea50636c063d9b6903a7ebc30c5842907f459c Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:13:18 +0100 Subject: [PATCH 05/28] Add wildcards to Bash() permission rules for Claude Code workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bash(command) without wildcard is an exact match — it doesn't match commands with arguments. Add * wildcards so Claude can pass arguments to allowed commands (e.g. pr-comment --body-file, git log --oneline). Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 222 ++++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 41 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 3a518f353c..458a778ab0 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -1,5 +1,10 @@ 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] @@ -14,50 +19,185 @@ jobs: # Automatic PR review when a PR is opened or updated. review: if: github.event_name == 'pull_request' - uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main - with: - prompt: "Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. Post your review as a PR comment. If you have no issues to raise, post a short comment saying the PR looks good." - allowed_tools: | - Bash(pr-diff) - Bash(pr-view) - Bash(pr-comment) - Bash(git log) - Bash(git diff) - Bash(git show) - Bash(grep) - Read - Glob - Grep - claude_args: "--max-turns 100" + 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: | + TOKEN_RESPONSE=$(curl -sfS -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") + ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + echo "::add-mask::${ACCESS_TOKEN}" + echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" + env: + SUBJECT_TOKEN: ${{ steps.github-oidc.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<> "$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 . 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')) - uses: databricks-eng/eng-dev-ecosystem/.github/workflows/claude-code.yml@main - with: - allowed_tools: | - Bash(make lint) - Bash(make test) - Bash(make fmt) - Bash(make schema) - Bash(go build ./...) - Bash(go test) - Bash(go vet) - Bash(git add) - Bash(git commit) - Bash(git diff) - Bash(git log) - Bash(git status) - Bash(git show) - Bash(pr-comment) - Bash(pr-push) - Bash(pr-view) - Bash(grep) - Read - Edit - Write - Glob - Grep - claude_args: "--max-turns 100" + 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: | + TOKEN_RESPONSE=$(curl -sfS -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") + ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + echo "::add-mask::${ACCESS_TOKEN}" + echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" + env: + SUBJECT_TOKEN: ${{ steps.github-oidc.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<> "$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 From c831fb5c28f4ceb9063bc701971658b7dd6792df Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:27:50 +0100 Subject: [PATCH 06/28] Enable inline PR review comments via MCP tool Add mcp__github_inline_comment__create_inline_comment to the review job's allowed tools and update prompts to instruct Claude to post inline comments on specific lines of the diff. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 458a778ab0..f7a8dc71d5 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -62,7 +62,7 @@ jobs: 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"] + ["mcp__github_inline_comment__create_inline_comment", "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 \ @@ -102,7 +102,13 @@ jobs: 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 . If you have no issues to raise, post a short comment saying the PR looks good." + prompt: | + Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. + + For specific code issues, use the mcp__github_inline_comment__create_inline_comment tool to post inline comments on the relevant lines of the diff. Always set confirmed: true. For each inline comment, be specific about the issue and suggest a fix if possible. + + After posting inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . If you have no issues to raise, just post a short comment saying the PR looks good. + allowed_tools: "mcp__github_inline_comment__create_inline_comment" claude_args: "--max-turns 100" trigger_phrase: "@claude" settings: ${{ steps.config.outputs.settings }} From 832a97562567dcea22df9be7de26f2bc8623ebfa Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:32:23 +0100 Subject: [PATCH 07/28] Add MCP tools and fix allowed_tools for Claude Code workflow - Remove erroneous allowed_tools action input that restricted Claude to a single tool - Add mcp__github_ci__* tools to assist job for CI investigation - Review job already has inline comment MCP tool in settings Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index f7a8dc71d5..96e1de89a4 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -108,7 +108,6 @@ jobs: For specific code issues, use the mcp__github_inline_comment__create_inline_comment tool to post inline comments on the relevant lines of the diff. Always set confirmed: true. For each inline comment, be specific about the issue and suggest a fix if possible. After posting inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . If you have no issues to raise, just post a short comment saying the PR looks good. - allowed_tools: "mcp__github_inline_comment__create_inline_comment" claude_args: "--max-turns 100" trigger_phrase: "@claude" settings: ${{ steps.config.outputs.settings }} @@ -162,7 +161,7 @@ jobs: 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"] + ["mcp__github_ci__get_ci_status", "mcp__github_ci__get_workflow_run_details", "mcp__github_ci__download_job_log", "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 \ From 36559e9404f98c203fdfb0f73ab6bd8d105ed35a Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:37:13 +0100 Subject: [PATCH 08/28] Pass inline comment tool via claude_args --allowedTools The MCP inline comment server is only registered when the action detects the tool in claude_args --allowedTools. Pass it there instead of only in settings.permissions.allow. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 96e1de89a4..58f42b5493 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -108,7 +108,7 @@ jobs: For specific code issues, use the mcp__github_inline_comment__create_inline_comment tool to post inline comments on the relevant lines of the diff. Always set confirmed: true. For each inline comment, be specific about the issue and suggest a fix if possible. After posting inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . If you have no issues to raise, just post a short comment saying the PR looks good. - claude_args: "--max-turns 100" + claude_args: '--max-turns 100 --allowedTools "mcp__github_inline_comment__create_inline_comment"' trigger_phrase: "@claude" settings: ${{ steps.config.outputs.settings }} show_full_output: true From fb84c1209ceaafe9e65b3ac39095542e30722ef8 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:40:13 +0100 Subject: [PATCH 09/28] Strengthen prompt to require inline comments for code review Claude was posting all feedback in a single PR comment instead of using inline comments on specific lines. Updated prompt to make inline comments mandatory for code-specific feedback. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 58f42b5493..9b3097f291 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -105,9 +105,9 @@ jobs: prompt: | Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. - For specific code issues, use the mcp__github_inline_comment__create_inline_comment tool to post inline comments on the relevant lines of the diff. Always set confirmed: true. For each inline comment, be specific about the issue and suggest a fix if possible. + You MUST post all code-specific feedback as inline comments using the mcp__github_inline_comment__create_inline_comment tool. For each issue you find, call this tool with the file path, line number, and your comment. Always set confirmed: true. Do NOT put code-level feedback in a summary comment. - After posting inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . If you have no issues to raise, just post a short comment saying the PR looks good. + After posting all inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . The summary should only list the number of inline comments posted and a one-line overview. If you have no issues to raise, just post a short comment saying the PR looks good. claude_args: '--max-turns 100 --allowedTools "mcp__github_inline_comment__create_inline_comment"' trigger_phrase: "@claude" settings: ${{ steps.config.outputs.settings }} From 7c700012222604f5a4ea70615d0fcdf4c757ef80 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:47:28 +0100 Subject: [PATCH 10/28] Trigger Claude Code review Empty commit to trigger the Claude Code review workflow after federation policy was created. Co-authored-by: Isaac From 5d712a8c9939ec83e378635b3241155f9d876c3a Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:48:33 +0100 Subject: [PATCH 11/28] Retry Claude Code review after federation policy propagation Co-authored-by: Isaac From 1f012392e5febc0504ba20cbee6450d00c7c29e5 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:50:25 +0100 Subject: [PATCH 12/28] Retry after fixing federation policy to use repository claim Co-authored-by: Isaac From adbf271ee64a84dc1fdca4f7371e9a7bc31b23da Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 13 Mar 2026 23:58:03 +0100 Subject: [PATCH 13/28] Restructure into reusable workflow for stable OIDC federation Split the inline workflow into a reusable workflow (claude-code.yml) and a caller (claude.yml). This ensures job_workflow_ref always points to the reusable workflow's ref (@refs/heads/main after merge), making the federation policy work for all trigger types (pull_request, issue_comment, pull_request_review_comment). Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 179 ++++++++++-------------------- .github/workflows/claude.yml | 84 ++++++++++++++ 2 files changed, 144 insertions(+), 119 deletions(-) create mode 100644 .github/workflows/claude.yml diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 9b3097f291..edd00acd0c 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -1,123 +1,39 @@ name: Claude Code -# AI-assisted PR reviews and interactive @claude mentions. +# Reusable workflow that runs Claude Code against the CLI repository. # 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. +# +# A federation policy on the DECO-TF-AWS-PROD-IS-SPN service principal +# matches the job_workflow_ref claim, so callers from this repository +# are authorized without static credentials. on: - pull_request: - types: [opened, synchronize, reopened] - - issue_comment: - types: [created] - - pull_request_review_comment: - types: [created] + workflow_call: + inputs: + prompt: + type: string + description: "Instructions for Claude Code (passed as the action's prompt input)" + required: false + + allowed_tools: + type: string + description: "Newline-separated list of tools Claude can use (e.g. Read, Edit, Bash(make test))" + required: false + + claude_args: + type: string + description: "Additional CLI arguments for Claude Code (e.g. --max-turns 10)" + required: false + + trigger_phrase: + type: string + description: "Phrase that triggers Claude in comments" + default: "@claude" 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: | - TOKEN_RESPONSE=$(curl -sfS -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") - ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) - echo "::add-mask::${ACCESS_TOKEN}" - echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" - env: - SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} - - - name: Build settings - id: config - run: | - TOOLS_JSON=$(cat << 'TOOLS' - ["mcp__github_inline_comment__create_inline_comment", "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<> "$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. - - You MUST post all code-specific feedback as inline comments using the mcp__github_inline_comment__create_inline_comment tool. For each issue you find, call this tool with the file path, line number, and your comment. Always set confirmed: true. Do NOT put code-level feedback in a summary comment. - - After posting all inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . The summary should only list the number of inline comments posted and a one-line overview. If you have no issues to raise, just post a short comment saying the PR looks good. - claude_args: '--max-turns 100 --allowedTools "mcp__github_inline_comment__create_inline_comment"' - 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')) + claude: runs-on: group: databricks-deco-testing-runner-group labels: ubuntu-latest-deco @@ -131,6 +47,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + # Request a GitHub OIDC JWT with the Databricks account ID as + # the audience. The federation policy on the service principal + # matches the job_workflow_ref claim, so callers from this + # repository are authorized. - name: Get GitHub OIDC token id: github-oidc uses: actions/github-script@v7 @@ -140,6 +60,8 @@ jobs: core.setSecret(token); core.setOutput('token', token); + # Exchange the GitHub OIDC JWT for a Databricks OAuth access + # token via the account-level token exchange endpoint. - name: Get Databricks OAuth token id: oauth run: | @@ -157,13 +79,17 @@ jobs: env: SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} + # Build the settings JSON with Databricks endpoint config and + # caller-specified tool permissions. ANTHROPIC_AUTH_TOKEN is used + # instead of ANTHROPIC_API_KEY because Databricks requires + # Authorization: Bearer (not x-api-key) for authentication. - name: Build settings id: config run: | - TOOLS_JSON=$(cat << 'TOOLS' - ["mcp__github_ci__get_ci_status", "mcp__github_ci__get_workflow_run_details", "mcp__github_ci__download_job_log", "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 - ) + TOOLS_JSON="[]" + if [ -n "$ALLOWED_TOOLS" ]; then + TOOLS_JSON=$(echo "$ALLOWED_TOOLS" | jq -R -s 'split("\n") | map(select(length > 0))') + fi SETTINGS=$(jq -n \ --argjson tools "$TOOLS_JSON" \ --arg auth_token "$OAUTH_TOKEN" \ @@ -183,17 +109,31 @@ jobs: echo "$SETTINGS" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" env: + ALLOWED_TOOLS: ${{ inputs.allowed_tools }} OAUTH_TOKEN: ${{ steps.oauth.outputs.token }} + # Create PR-scoped helper scripts so Claude can only interact with + # the PR that triggered this workflow. The PR number and branch are + # hardcoded at script creation time, not read from environment at + # runtime, so Claude cannot override them. - name: Create PR-scoped helper scripts run: | - NUMBER="$ISSUE_NUMBER" + if [ -n "$PR_NUMBER" ]; then + NUMBER="$PR_NUMBER" + else + NUMBER="$ISSUE_NUMBER" + fi 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 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-push /usr/local/bin/pr-view + + chmod +x /usr/local/bin/pr-comment /usr/local/bin/pr-push \ + /usr/local/bin/pr-diff /usr/local/bin/pr-view env: + PR_NUMBER: ${{ github.event.pull_request.number }} ISSUE_NUMBER: ${{ github.event.issue.number }} GH_TOKEN: ${{ github.token }} @@ -202,7 +142,8 @@ jobs: with: anthropic_api_key: ${{ steps.oauth.outputs.token }} github_token: ${{ github.token }} - claude_args: "--max-turns 100" - trigger_phrase: "@claude" + prompt: ${{ inputs.prompt }} + claude_args: ${{ inputs.claude_args }} + trigger_phrase: ${{ inputs.trigger_phrase }} settings: ${{ steps.config.outputs.settings }} show_full_output: true diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000000..a7f76a022b --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,84 @@ +name: Claude + +# AI-assisted PR reviews and interactive @claude mentions. +# This workflow delegates to the reusable claude-code.yml workflow +# which handles authentication and execution. + +on: + pull_request: + types: [opened, synchronize, reopened] + + issue_comment: + types: [created] + + pull_request_review_comment: + types: [created] + +jobs: + review: + if: github.event_name == 'pull_request' + uses: ./.github/workflows/claude-code.yml + with: + prompt: | + Review this PR. Focus on correctness, error handling, and adherence to the project's Go conventions documented in CLAUDE.md. + + You MUST post all code-specific feedback as inline comments using the mcp__github_inline_comment__create_inline_comment tool. For each issue you find, call this tool with the file path, line number, and your comment. Always set confirmed: true. Do NOT put code-level feedback in a summary comment. + + After posting all inline comments, write a brief summary to a temporary file and post it with: pr-comment --body-file . The summary should only list the number of inline comments posted and a one-line overview. If you have no issues to raise, just post a short comment saying the PR looks good. + allowed_tools: | + mcp__github_inline_comment__create_inline_comment + 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 + claude_args: '--max-turns 100 --allowedTools "mcp__github_inline_comment__create_inline_comment"' + + 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')) + uses: ./.github/workflows/claude-code.yml + with: + allowed_tools: | + mcp__github_ci__get_ci_status + mcp__github_ci__get_workflow_run_details + mcp__github_ci__download_job_log + 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(pr-push) + Bash(pr-push *) + Bash(git diff) + Bash(git diff *) + Bash(git log) + Bash(git log *) + Bash(git status) + Bash(git show *) + Bash(pr-comment *) + Bash(pr-view) + Bash(pr-view *) + Bash(grep *) + Read + Edit + Write + Glob + Grep + claude_args: "--max-turns 100" From 480c726a5611e0a04ae8a26907ebbf6883c18345 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:01:20 +0100 Subject: [PATCH 14/28] Add debug logging for OIDC claims and token exchange errors Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index edd00acd0c..1afaf0865f 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -62,10 +62,16 @@ jobs: # Exchange the GitHub OIDC JWT for a Databricks OAuth access # token via the account-level token exchange endpoint. + - name: Debug OIDC claims + run: | + echo "$SUBJECT_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '{iss, aud, sub, job_workflow_ref, repository, repository_owner, ref}' || true + env: + SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} + - name: Get Databricks OAuth token id: oauth run: | - TOKEN_RESPONSE=$(curl -sfS -X POST \ + 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" \ @@ -73,7 +79,12 @@ jobs: -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ -d "scope=all-apis") - ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + if [ "$HTTP_CODE" -ne 200 ]; then + echo "Token exchange failed with HTTP $HTTP_CODE:" + cat /tmp/token_response.json + exit 1 + fi + ACCESS_TOKEN=$(jq -r .access_token /tmp/token_response.json) echo "::add-mask::${ACCESS_TOKEN}" echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" env: From 7479fc03d219aa070522e29d18116121f9a8f0b9 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:04:20 +0100 Subject: [PATCH 15/28] Switch to protected runner group for Databricks IP ACL compatibility The deco-testing runner group IPs are blocked by the Databricks account IP ACL. The protected runner group should have whitelisted egress IPs. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 1afaf0865f..8954bd6a85 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -35,8 +35,7 @@ on: jobs: claude: runs-on: - group: databricks-deco-testing-runner-group - labels: ubuntu-latest-deco + group: databricks-protected-runner-group-large permissions: contents: write pull-requests: write From e70030e02420507fd72db8aa27670064d3178327 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:06:11 +0100 Subject: [PATCH 16/28] Add Linux label to protected runner group Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 8954bd6a85..52f1d62d9e 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -36,6 +36,7 @@ jobs: claude: runs-on: group: databricks-protected-runner-group-large + labels: linux-ubuntu-latest-large permissions: contents: write pull-requests: write From 33db89d7a22b83975cf331044c810b971fea0bbe Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:07:54 +0100 Subject: [PATCH 17/28] Try eng-protected runner group for IP ACL whitelisted IPs Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 52f1d62d9e..e44530cef0 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -35,8 +35,8 @@ on: jobs: claude: runs-on: - group: databricks-protected-runner-group-large - labels: linux-ubuntu-latest-large + group: databricks-eng-protected-runner-group + labels: linux-ubuntu-latest permissions: contents: write pull-requests: write From fcde226f578808e50605054f8eaab995267f79cc Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:10:05 +0100 Subject: [PATCH 18/28] Clean up debug logging, revert to deco-testing runner group Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index e44530cef0..edd00acd0c 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -35,8 +35,8 @@ on: jobs: claude: runs-on: - group: databricks-eng-protected-runner-group - labels: linux-ubuntu-latest + group: databricks-deco-testing-runner-group + labels: ubuntu-latest-deco permissions: contents: write pull-requests: write @@ -62,16 +62,10 @@ jobs: # Exchange the GitHub OIDC JWT for a Databricks OAuth access # token via the account-level token exchange endpoint. - - name: Debug OIDC claims - run: | - echo "$SUBJECT_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '{iss, aud, sub, job_workflow_ref, repository, repository_owner, ref}' || true - env: - SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} - - name: Get Databricks OAuth token id: oauth run: | - HTTP_CODE=$(curl -s -o /tmp/token_response.json -w "%{http_code}" -X POST \ + TOKEN_RESPONSE=$(curl -sfS -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" \ @@ -79,12 +73,7 @@ jobs: -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ -d "scope=all-apis") - if [ "$HTTP_CODE" -ne 200 ]; then - echo "Token exchange failed with HTTP $HTTP_CODE:" - cat /tmp/token_response.json - exit 1 - fi - ACCESS_TOKEN=$(jq -r .access_token /tmp/token_response.json) + ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) echo "::add-mask::${ACCESS_TOKEN}" echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" env: From f97a217156ff6096be6df45a1b1afc240cc5e7c5 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:33:43 +0100 Subject: [PATCH 19/28] Add connectivity validation steps to Claude Code review workflow Adds progressive validation before running Claude: first a simple workspace API call (spark-versions), then a model serving endpoint query. This helps diagnose OIDC federation or network issues. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 207 +++++++++++++++++++++--------- 1 file changed, 147 insertions(+), 60 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index edd00acd0c..9650ed1885 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -1,39 +1,24 @@ name: Claude Code -# Reusable workflow that runs Claude Code against the CLI repository. +# 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. -# -# A federation policy on the DECO-TF-AWS-PROD-IS-SPN service principal -# matches the job_workflow_ref claim, so callers from this repository -# are authorized without static credentials. on: - workflow_call: - inputs: - prompt: - type: string - description: "Instructions for Claude Code (passed as the action's prompt input)" - required: false - - allowed_tools: - type: string - description: "Newline-separated list of tools Claude can use (e.g. Read, Edit, Bash(make test))" - required: false - - claude_args: - type: string - description: "Additional CLI arguments for Claude Code (e.g. --max-turns 10)" - required: false - - trigger_phrase: - type: string - description: "Phrase that triggers Claude in comments" - default: "@claude" + pull_request: + types: [opened, synchronize, reopened] + + issue_comment: + types: [created] + + pull_request_review_comment: + types: [created] jobs: - claude: + # 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 @@ -47,10 +32,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - # Request a GitHub OIDC JWT with the Databricks account ID as - # the audience. The federation policy on the service principal - # matches the job_workflow_ref claim, so callers from this - # repository are authorized. - name: Get GitHub OIDC token id: github-oidc uses: actions/github-script@v7 @@ -60,8 +41,6 @@ jobs: core.setSecret(token); core.setOutput('token', token); - # Exchange the GitHub OIDC JWT for a Databricks OAuth access - # token via the account-level token exchange endpoint. - name: Get Databricks OAuth token id: oauth run: | @@ -79,17 +58,46 @@ jobs: env: SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} - # Build the settings JSON with Databricks endpoint config and - # caller-specified tool permissions. ANTHROPIC_AUTH_TOKEN is used - # instead of ANTHROPIC_API_KEY because Databricks requires - # Authorization: Bearer (not x-api-key) for authentication. + - 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="[]" - if [ -n "$ALLOWED_TOOLS" ]; then - TOOLS_JSON=$(echo "$ALLOWED_TOOLS" | jq -R -s 'split("\n") | map(select(length > 0))') - fi + 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" \ @@ -109,31 +117,111 @@ jobs: echo "$SETTINGS" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" env: - ALLOWED_TOOLS: ${{ inputs.allowed_tools }} OAUTH_TOKEN: ${{ steps.oauth.outputs.token }} - # Create PR-scoped helper scripts so Claude can only interact with - # the PR that triggered this workflow. The PR number and branch are - # hardcoded at script creation time, not read from environment at - # runtime, so Claude cannot override them. - name: Create PR-scoped helper scripts run: | - if [ -n "$PR_NUMBER" ]; then - NUMBER="$PR_NUMBER" - else - NUMBER="$ISSUE_NUMBER" - fi - BRANCH=$(gh pr view "$NUMBER" --json headRefName -q .headRefName) - + NUMBER="$PR_NUMBER" 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 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-push \ - /usr/local/bin/pr-diff /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 . 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: | + TOKEN_RESPONSE=$(curl -sfS -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") + ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + echo "::add-mask::${ACCESS_TOKEN}" + echo "token=${ACCESS_TOKEN}" >> "$GITHUB_OUTPUT" + env: + SUBJECT_TOKEN: ${{ steps.github-oidc.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<> "$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 }} @@ -142,8 +230,7 @@ jobs: with: anthropic_api_key: ${{ steps.oauth.outputs.token }} github_token: ${{ github.token }} - prompt: ${{ inputs.prompt }} - claude_args: ${{ inputs.claude_args }} - trigger_phrase: ${{ inputs.trigger_phrase }} + claude_args: "--max-turns 100" + trigger_phrase: "@claude" settings: ${{ steps.config.outputs.settings }} show_full_output: true From e2efceff93fe41d47755e32bd5f8b032533d1c7b Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:37:09 +0100 Subject: [PATCH 20/28] Show token exchange error response body for debugging Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 53 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 9650ed1885..d24d375165 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -44,7 +44,7 @@ jobs: - name: Get Databricks OAuth token id: oauth run: | - TOKEN_RESPONSE=$(curl -sfS -X POST \ + 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" \ @@ -52,7 +52,13 @@ jobs: -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ -d "scope=all-apis") - ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + 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: @@ -171,7 +177,7 @@ jobs: - name: Get Databricks OAuth token id: oauth run: | - TOKEN_RESPONSE=$(curl -sfS -X POST \ + 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" \ @@ -179,12 +185,51 @@ jobs: -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ -d "client_id=b76b6808-9e10-43b3-be20-6b6d19ed1af0" \ -d "scope=all-apis") - ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token) + 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: | From 3cf6d9bdb600409757aed5c89762226a7e511b89 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 00:53:43 +0100 Subject: [PATCH 21/28] Dispatch Claude Code to eng-dev-ecosystem for IP ACL compatibility The CLI runners are blocked by the Databricks account IP ACL at the OIDC token exchange endpoint. This moves the Claude Code execution to eng-dev-ecosystem's protected runners (which are allowlisted) and keeps this workflow as a thin dispatch trigger. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 297 ++++++------------------------ 1 file changed, 57 insertions(+), 240 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index d24d375165..bb73477132 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -1,9 +1,11 @@ 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. +# +# The actual Claude Code execution runs in eng-dev-ecosystem on +# protected runners whose IPs are allowlisted by the Databricks +# account IP ACL. This workflow is a thin trigger that dispatches +# to eng-dev-ecosystem via the DECO workflow trigger GitHub App. on: pull_request: @@ -19,263 +21,78 @@ 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 + runs-on: ubuntu-latest + environment: test-trigger-is permissions: - contents: write - pull-requests: write - issues: write - id-token: write + contents: read steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Get GitHub OIDC token - id: github-oidc - uses: actions/github-script@v7 + - name: Generate GitHub App token + id: token + uses: actions/create-github-app-token@v2 with: - script: | - const token = await core.getIDToken('968367da-7edd-44f7-9dea-3e0b20b0ec97'); - core.setSecret(token); - core.setOutput('token', token); + app-id: ${{ secrets.DECO_WORKFLOW_TRIGGER_APP_ID }} + private-key: ${{ secrets.DECO_WORKFLOW_TRIGGER_PRIVATE_KEY }} + owner: databricks-eng + repositories: eng-dev-ecosystem - - name: Get Databricks OAuth token - id: oauth + - name: Trigger Claude Code review 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" + gh workflow run cli-claude-code.yml \ + -R databricks-eng/eng-dev-ecosystem \ + --ref main \ + -F pull_request_number=${{ github.event.pull_request.number }} \ + -F event_type=review env: - SUBJECT_TOKEN: ${{ steps.github-oidc.outputs.token }} + GH_TOKEN: ${{ steps.token.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<> "$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 . 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. + # Interactive @claude mentions. 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 + runs-on: ubuntu-latest + environment: test-trigger-is permissions: - contents: write - pull-requests: write - issues: write - id-token: write + contents: read steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Get GitHub OIDC token - id: github-oidc - uses: actions/github-script@v7 + - name: Generate GitHub App token + id: token + uses: actions/create-github-app-token@v2 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 }} + app-id: ${{ secrets.DECO_WORKFLOW_TRIGGER_APP_ID }} + private-key: ${{ secrets.DECO_WORKFLOW_TRIGGER_PRIVATE_KEY }} + owner: databricks-eng + repositories: eng-dev-ecosystem - - name: Validate workspace connectivity + - name: Determine PR number + id: pr 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 + if [ -n "$ISSUE_NUMBER" ]; then + echo "number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT" + else + echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" 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<> "$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 }} + PR_NUMBER: ${{ github.event.pull_request.number }} - - name: Run Claude Code - uses: anthropics/claude-code-action@v1 + - name: Trigger Claude Code assist + uses: actions/github-script@v7 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 + github-token: ${{ steps.token.outputs.token }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'databricks-eng', + repo: 'eng-dev-ecosystem', + workflow_id: 'cli-claude-code.yml', + ref: 'main', + inputs: { + pull_request_number: '${{ steps.pr.outputs.number }}', + event_type: 'assist', + comment_body: process.env.COMMENT_BODY + } + }); + env: + COMMENT_BODY: ${{ github.event.comment.body }} From 4d0821f9c7fec97f9c386db5247df47d8a99b5f5 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 08:50:18 +0100 Subject: [PATCH 22/28] Temporarily point to PR branch for testing Will switch back to main after eng-dev-ecosystem PR is merged. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index bb73477132..504555e6ef 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -40,7 +40,7 @@ jobs: run: | gh workflow run cli-claude-code.yml \ -R databricks-eng/eng-dev-ecosystem \ - --ref main \ + --ref add-claude-code-workflow \ -F pull_request_number=${{ github.event.pull_request.number }} \ -F event_type=review env: @@ -87,7 +87,7 @@ jobs: owner: 'databricks-eng', repo: 'eng-dev-ecosystem', workflow_id: 'cli-claude-code.yml', - ref: 'main', + ref: 'add-claude-code-workflow', inputs: { pull_request_number: '${{ steps.pr.outputs.number }}', event_type: 'assist', From 0cd7b6c24485192373b0b06ffe0662e534a81411 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 08:58:42 +0100 Subject: [PATCH 23/28] Use deco-testing runner group for Claude Code dispatch The databricks-eng org has a GitHub IP allowlist that blocks ubuntu-latest runners from generating GitHub App tokens. Switch to databricks-deco-testing-runner-group (with ubuntu-latest-deco label) which has allowlisted IPs, matching the pattern used by start-integration-tests.yml and other dispatch workflows. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 504555e6ef..bd86fe0091 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -21,7 +21,9 @@ jobs: # Automatic PR review when a PR is opened or updated. review: if: github.event_name == 'pull_request' - runs-on: ubuntu-latest + runs-on: + group: databricks-deco-testing-runner-group + labels: ubuntu-latest-deco environment: test-trigger-is permissions: contents: read @@ -51,7 +53,9 @@ jobs: 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: ubuntu-latest + runs-on: + group: databricks-deco-testing-runner-group + labels: ubuntu-latest-deco environment: test-trigger-is permissions: contents: read From 118f6eacea89b4feeda82998e11b480a6c2acc0b Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 09:19:30 +0100 Subject: [PATCH 24/28] Add concurrency group and bot filter to Claude Code workflow - Add concurrency group to cancel in-progress reviews on the same PR when a new push arrives (avoids duplicate reviews from rapid pushes) - Filter out bot comments from @claude trigger to prevent infinite loops Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index bd86fe0091..d6066b49f5 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -21,6 +21,9 @@ jobs: # Automatic PR review when a PR is opened or updated. review: if: github.event_name == 'pull_request' + concurrency: + group: claude-review-${{ github.event.pull_request.number }} + cancel-in-progress: true runs-on: group: databricks-deco-testing-runner-group labels: ubuntu-latest-deco @@ -51,8 +54,11 @@ jobs: # Interactive @claude mentions. 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')) + github.event.comment.user.type != 'Bot' && + ( + (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 From c6576dd64a478d9c609f754abf68f1434899f7bf Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 09:32:07 +0100 Subject: [PATCH 25/28] Only trigger Claude Code review on PR open, not every push For re-reviews after subsequent pushes, authors can comment "@claude review" which triggers via the assist flow. Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index d6066b49f5..024184537b 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -9,7 +9,7 @@ name: Claude Code on: pull_request: - types: [opened, synchronize, reopened] + types: [opened] issue_comment: types: [created] @@ -18,7 +18,7 @@ on: types: [created] jobs: - # Automatic PR review when a PR is opened or updated. + # Automatic review on PR open. For re-reviews, comment "@claude review". review: if: github.event_name == 'pull_request' concurrency: From e6ecd808965c71f17207f2b4581b809ea8098a4b Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 15:04:58 +0100 Subject: [PATCH 26/28] Add author_association and PR-only guards to assist job - Restrict @claude to trusted authors (author_association != NONE) - Only trigger on PR comments, not regular issues (check github.event.issue.pull_request) Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 024184537b..aca025240a 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -51,12 +51,13 @@ jobs: env: GH_TOKEN: ${{ steps.token.outputs.token }} - # Interactive @claude mentions. + # Interactive @claude mentions (PRs only, trusted authors only). assist: if: | github.event.comment.user.type != 'Bot' && + github.event.comment.author_association != 'NONE' && ( - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ) runs-on: From 7995c3917c68fd4c7e9ab1c8dd8f5988ade9b49e Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 15:07:15 +0100 Subject: [PATCH 27/28] Use allowlist for author_association, point --ref to main - Switch author_association from denylist (!= NONE) to allowlist (COLLABORATOR, MEMBER, OWNER) for defense in depth. - Point workflow dispatch --ref to main (eng-dev-ecosystem PR will be merged first). See: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index aca025240a..ab7141ac19 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -45,17 +45,20 @@ jobs: run: | gh workflow run cli-claude-code.yml \ -R databricks-eng/eng-dev-ecosystem \ - --ref add-claude-code-workflow \ + --ref main \ -F pull_request_number=${{ github.event.pull_request.number }} \ -F event_type=review env: GH_TOKEN: ${{ steps.token.outputs.token }} # Interactive @claude mentions (PRs only, trusted authors only). + # Restrict to collaborators/members/owners to prevent untrusted users from + # triggering Claude with write access to the repo. See: + # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ assist: if: | github.event.comment.user.type != 'Bot' && - github.event.comment.author_association != 'NONE' && + contains(fromJson('["COLLABORATOR","MEMBER","OWNER"]'), github.event.comment.author_association) && ( (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) From e8a70ba4da5b65e2661257350e6dd4448f561250 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Sat, 14 Mar 2026 15:11:56 +0100 Subject: [PATCH 28/28] Security hardening from self-review - Add author_association allowlist to review job (prevents external fork PRs from consuming model serving resources) - Fix assist job ref: was still pointing to feature branch instead of main Co-authored-by: Isaac --- .github/workflows/claude-code.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index ab7141ac19..55151d9f55 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -19,8 +19,13 @@ on: jobs: # Automatic review on PR open. For re-reviews, comment "@claude review". + # Restrict to collaborators/members/owners to prevent untrusted users + # (e.g. external fork PRs) from consuming model serving resources. See: + # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ review: - if: github.event_name == 'pull_request' + if: | + github.event_name == 'pull_request' && + contains(fromJson('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association) concurrency: group: claude-review-${{ github.event.pull_request.number }} cancel-in-progress: true @@ -101,7 +106,7 @@ jobs: owner: 'databricks-eng', repo: 'eng-dev-ecosystem', workflow_id: 'cli-claude-code.yml', - ref: 'add-claude-code-workflow', + ref: 'main', inputs: { pull_request_number: '${{ steps.pr.outputs.number }}', event_type: 'assist',