Skip to content

feat(bots): onboard reviewer-bot to pinned databricks-bot-engine #7

feat(bots): onboard reviewer-bot to pinned databricks-bot-engine

feat(bots): onboard reviewer-bot to pinned databricks-bot-engine #7

Workflow file for this run

# Reviewer Bot — initial PR review.
#
# Own-job composite caller of the databricks-bot-engine actions. We deliberately
# do NOT use the engine's reusable WORKFLOW (jobs.<id>.uses:
# .../reviewer-bot.reusable.yml@<sha>): a cross-repo reusable-workflow call to
# the private engine fails to resolve ("workflow was not found"). Cross-repo
# composite ACTIONS (steps: - uses: .../.github/actions/...@<sha>) DO resolve
# in-org — this is the pattern databricks-driver-test's engineer bot uses in
# production, and the one the engineer-bot workflows here use too. So this file
# owns the job and replicates the reusable's steps (fork gate, token mint,
# install, run) with cross-repo action refs.
#
# Engine pin: the SHA below pins BOTH the action refs AND engine-ref, so the
# workflow and installed engine match. Bump in lockstep; never @main.
#
# Auth: PAT-free. We mint a short-lived, engine-scoped App installation token
# from the review-bot App creds and pass it as engine-pat to install-bot-engine
# (no stored BOT_ENGINE_PAT).
name: Reviewer Bot
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: string
dry_run:
description: 'Print what would be posted instead of posting'
required: false
default: 'true'
type: string
permissions:
contents: read
pull-requests: write
id-token: write # JFrog OIDC exchange for the engine/SDK/CLI install
jobs:
review:
# SECURITY FLOOR (was the reusable's job `if:`): manual dispatch, OR a
# non-draft, non-fork PR. fork == false keeps the App + model tokens off
# untrusted code.
if: >-
github.event_name == 'workflow_dispatch'
|| (github.event.pull_request.draft == false
&& github.event.pull_request.head.repo.fork == false)
environment: azure-prod # DATABRICKS_HOST / DATABRICKS_TOKEN live here
runs-on:
group: databricks-protected-runner-group
labels: [linux-ubuntu-latest]
timeout-minutes: 30
steps:
# Checkout FIRST. Default ref = the PR merge ref on pull_request, the
# default branch on dispatch. persist-credentials:false — the reviewer
# reads this tree via read_paths/grep, so no token may sit in .git/config.
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
# Mint the review-bot token (posts findings).
- name: Mint review-bot App token
id: setup
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.REVIEW_BOT_APP_ID }}
private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }}
- name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.11'
- name: Setup Node (for the Claude Code CLI the SDK spawns)
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
# PAT-free engine install: mint a per-run token scoped to ONLY the engine
# repo (contents:read) from the review-bot App creds. Requires the
# review-bot App installed on databricks-bot-engine with contents:read.
- name: Mint engine-scoped install token
id: engine-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.REVIEW_BOT_APP_ID }}
private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }}
owner: databricks
repositories: databricks-bot-engine
permission-contents: read
# LOCAL composite (not a cross-repo `uses:` of the engine — that fails to
# resolve from this external repo). It pip-installs the pinned engine via
# the engine-scoped token.
- name: Install engine + Claude SDK/CLI
uses: ./.github/actions/install-bot-engine
with:
engine-ref: b6205fb502566d617a456ccf3c37f3e4e3072ccd
engine-token: ${{ steps.engine-token.outputs.token }}
- name: Resolve trigger inputs
id: inputs
# SECURITY: dispatcher-supplied values pass via env, never interpolated
# into the script body (an inline expression would inject at assignment,
# before the digits-only check). Env values expand without re-parsing.
env:
GH_TOKEN: ${{ steps.setup.outputs.token }}
EVENT_NAME: ${{ github.event_name }}
INPUT_PR: ${{ inputs.pr_number }}
INPUT_DRY_RUN: ${{ inputs.dry_run }}
EVENT_PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
RAW_PR="$INPUT_PR"; DRY_RUN="$INPUT_DRY_RUN"
else
RAW_PR="$EVENT_PR"; DRY_RUN="false"
fi
[[ "$RAW_PR" =~ ^[0-9]+$ ]] || { echo "::error::Invalid pr_number '$RAW_PR'"; exit 1; }
case "$DRY_RUN" in true|false) ;; *) DRY_RUN="true" ;; esac # fail safe
HEAD_SHA=$(gh pr view "$RAW_PR" --repo "$REPO" --json headRefOid -q .headRefOid)
{ echo "pr_number=$RAW_PR"; echo "head_sha=$HEAD_SHA"; echo "dry_run=$DRY_RUN"; } >> "$GITHUB_OUTPUT"
- name: Run reviewer
env:
GH_TOKEN: ${{ steps.setup.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ steps.inputs.outputs.pr_number }}
HEAD_SHA: ${{ steps.inputs.outputs.head_sha }}
DRY_RUN: ${{ steps.inputs.outputs.dry_run }}
MODEL_ENDPOINT: https://${{ secrets.DATABRICKS_HOST }}/serving-endpoints/databricks-claude-opus-4-8/invocations
DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }}
RUNNER_TEMP: ${{ runner.temp }}
# The reviewer's repo-specific ADDITIVE guidance, read from the TRUSTED
# checkout (run_review enforces containment). Engine owns the base prompt.
REVIEW_SYSTEM_PROMPT: .bot/prompts/review/system.md
run: python -m databricks_bot_engine.reviewer_bot.run_review