Skip to content

Commit 356882d

Browse files
refactor(bots): extract shared bot-prelude composite across all 4 workflows
The four bot workflows duplicated ~4 setup steps (mint bot token, mint engine-scoped token, Setup Node, install engine). Factor them into a local ./.github/actions/bot-prelude composite (inputs: app-id/private-key + engine-ref; output: token). Each workflow keeps only what genuinely differs: its checkout, Setup Python (reviewer — interpreter only) vs setup-poetry (engineer — connector deps for pytest self-verify), and its run/publish tail. bot-prelude calls the existing local install-bot-engine. Behavior unchanged; token refs now come from steps.prelude.outputs.token. Headers refreshed to match. Engine feedback on the duplicated install-bot-engine noted in issue #104. Co-authored-by: Isaac Signed-off-by: eric-wang-1990 <e.wang@databricks.com>
1 parent 1fe8d9b commit 356882d

6 files changed

Lines changed: 158 additions & 156 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Shared prelude for the bot workflows — the steps identical across all four
2+
# (reviewer, reviewer-followup, engineer, engineer-followup):
3+
# 1. mint the bot's App installation token (this repo) — for PR/issue posting
4+
# 2. mint a SECOND token scoped to the engine repo (contents:read) — PAT-free
5+
# auth for the private-engine install
6+
# 3. set up Node (the Claude Code CLI the SDK spawns)
7+
# 4. install the pinned engine + Claude SDK/CLI via the local install-bot-engine
8+
# composite, authenticated by the engine-scoped token
9+
#
10+
# LOCAL composite (`uses: ./…`) — resolves against this checked-out repo, so it
11+
# works where a cross-repo `uses:` of the engine does NOT. Call it AFTER checkout
12+
# (a composite can't run the checkout that loads it) and AFTER Python/Poetry setup
13+
# (the engine `pip install` needs the interpreter present). What stays inline in
14+
# each workflow: checkout (differs per flow), Python-vs-Poetry (reviewer needs
15+
# only Python; engineer needs the connector's deps via setup-poetry), and the
16+
# run/publish tail.
17+
name: Bot prelude (tokens + Node + engine install)
18+
description: Mint the bot + engine-scoped App tokens, set up Node, and install the pinned engine (PAT-free). Shared by all four bot workflows.
19+
20+
inputs:
21+
app-id:
22+
description: 'Bot App id (review-bot or engineer-bot) — mints the this-repo token used to post.'
23+
required: true
24+
private-key:
25+
description: 'Bot App private key (PEM) for the same App.'
26+
required: true
27+
engine-ref:
28+
description: 'Engine commit SHA (full 40-char) to install.'
29+
required: true
30+
engine-repo:
31+
description: 'owner/name of the engine repo.'
32+
required: false
33+
default: 'databricks/databricks-bot-engine'
34+
35+
outputs:
36+
token:
37+
description: 'The minted bot App installation token for THIS repo (post reviews / push fix branches / comment).'
38+
value: ${{ steps.app-token.outputs.token }}
39+
40+
runs:
41+
using: composite
42+
steps:
43+
# This-repo token: the identity the bot acts as (post reviews, push the fix
44+
# branch, comment). Exposed as the `token` output for the caller's run steps.
45+
- name: Mint bot App token
46+
id: app-token
47+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
48+
with:
49+
app-id: ${{ inputs.app-id }}
50+
private-key: ${{ inputs.private-key }}
51+
52+
# Engine-scoped token: a SECOND, per-run token scoped to ONLY the engine repo
53+
# (contents:read), used to `pip install` the private engine with no stored
54+
# PAT. Requires the same App installed on the engine repo with contents:read.
55+
- name: Mint engine-scoped install token
56+
id: engine-token
57+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
58+
with:
59+
app-id: ${{ inputs.app-id }}
60+
private-key: ${{ inputs.private-key }}
61+
owner: databricks
62+
repositories: databricks-bot-engine
63+
permission-contents: read
64+
65+
- name: Setup Node (for the Claude Code CLI the SDK spawns)
66+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
67+
with:
68+
node-version: '20'
69+
70+
- name: Install engine + Claude SDK/CLI
71+
uses: ./.github/actions/install-bot-engine
72+
with:
73+
engine-ref: ${{ inputs.engine-ref }}
74+
engine-repo: ${{ inputs.engine-repo }}
75+
engine-token: ${{ steps.engine-token.outputs.token }}

.github/workflows/engineer-bot-followup.yml

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
# catch-up mode, applies a fixup (or replies / marks blocked), and pushes to the
66
# PR branch. Driven by this repo's .bot/ prompts.
77
#
8-
# Like the author, this builds/runs the product, so it keeps its own job and
9-
# interleaves `poetry install` before the shared bot-run composite. Consumer
10-
# differences vs the engine dogfood: REF-mode install pinned to an engine SHA,
11-
# authenticated PAT-free by an engine-scoped App token minted below.
8+
# Like the author, this builds/runs the product (setup-poetry). Shared setup
9+
# (tokens + Node + engine install, PAT-free via an engine-scoped App token) is in
10+
# the local ./.github/actions/bot-prelude composite; this file owns checkout,
11+
# setup-poetry, and the followup run. Engine installed in REF mode pinned to a SHA.
1212
#
1313
# The gate below is the canonical label-gated form copied verbatim from the
1414
# engine dogfood (the single source of truth). It ANDs with the engine-side
@@ -85,14 +85,6 @@ jobs:
8585
fetch-depth: 0
8686
persist-credentials: false
8787

88-
# Mint the engineer-bot token (this repo — pushes fixup commits, posts replies).
89-
- name: Mint engineer-bot App token
90-
id: setup
91-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
92-
with:
93-
app-id: ${{ secrets.ENGINEER_BOT_APP_ID }}
94-
private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }}
95-
9688
# Python + connector deps via the repo's OWN setup-poetry action (JFrog
9789
# mirror + poetry lock + install) — egress-safe, tolerates a stale lock.
9890
# The agent runs `poetry run python -m pytest tests/unit` to verify fixups.
@@ -101,37 +93,23 @@ jobs:
10193
with:
10294
python-version: '3.11'
10395

104-
- name: Setup Node (for the Claude Code CLI the SDK spawns)
105-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
106-
with:
107-
node-version: '20'
108-
109-
# PAT-free engine install: engine-scoped token (contents:read) from the
110-
# same App creds.
111-
- name: Mint engine-scoped install token
112-
id: engine-token
113-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
96+
# Shared prelude: mint the engineer-bot token (pushes fixup commits, posts
97+
# replies) + the engine-scoped token, set up Node, install the engine.
98+
- name: Bot prelude (tokens + Node + engine install)
99+
id: prelude
100+
uses: ./.github/actions/bot-prelude
114101
with:
115102
app-id: ${{ secrets.ENGINEER_BOT_APP_ID }}
116103
private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }}
117-
owner: databricks
118-
repositories: databricks-bot-engine
119-
permission-contents: read
120-
121-
# LOCAL composite (cross-repo `uses:` of the engine won't resolve here).
122-
- name: Install engine + Claude SDK/CLI
123-
uses: ./.github/actions/install-bot-engine
124-
with:
125104
engine-ref: b6205fb502566d617a456ccf3c37f3e4e3072ccd
126-
engine-token: ${{ steps.engine-token.outputs.token }}
127105

128106
# Run the follow-up. Inlines the engine's bot-run env contract for
129107
# engineer:followup, plus the authenticated push remote bot-run would set
130108
# up (followup pushes fixup commits via a plain `git push`, which doesn't
131109
# consult GH_TOKEN; the checkout is persist-credentials:false).
132110
- name: Run engineer follow-up (catch-up over all unaddressed threads)
133111
env:
134-
GH_TOKEN: ${{ steps.setup.outputs.token }}
112+
GH_TOKEN: ${{ steps.prelude.outputs.token }}
135113
GITHUB_REPOSITORY: ${{ github.repository }}
136114
PR_NUMBER: ${{ github.event.pull_request.number }}
137115
HEAD_SHA: ${{ github.event.pull_request.head.sha }}

.github/workflows/engineer-bot.yml

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
# the bug with a failing test, fixes the connector code, and opens a PR. Driven
55
# by the bug-fix flow + this repo's .bot/ (config + prompts).
66
#
7-
# Unlike the reviewer (a thin reusable-workflow caller), the engineer builds and
8-
# runs THIS repo's tests, so it keeps its own job and interleaves env-prep
9-
# (`poetry install`) before invoking the engine — a reusable workflow can't host
10-
# that. It mirrors the engine's dogfood engineer-bot.yml, with two consumer
11-
# differences: (1) REF-mode install pinned to an engine SHA, authenticated
12-
# PAT-free by an engine-scoped App token minted below; (2) an explicit
13-
# `poetry install` so the connector's own deps are present for `pytest`
14-
# self-verify (REF mode installs only the engine, not the product).
7+
# The engineer builds and runs THIS repo's tests, so it needs the connector's
8+
# deps installed (via setup-poetry) in addition to the engine. Shared setup
9+
# (mint tokens + Node + install the pinned engine, PAT-free) is factored into the
10+
# local ./.github/actions/bot-prelude composite; this file owns checkout,
11+
# setup-poetry, and the author/publish/comment tail. The engine is installed in
12+
# REF mode pinned to a SHA and authenticated PAT-free by an engine-scoped App
13+
# token (minted inside bot-prelude).
1514
#
1615
# SECURITY: the trigger is a label only maintainers can apply — issue BODIES are
1716
# untrusted input, so the gate is who-can-label, not the content. The issue body
@@ -60,44 +59,29 @@ jobs:
6059
fetch-depth: 0
6160
persist-credentials: false
6261

63-
# Mint the engineer-bot token (this repo — pushes the fix branch, comments).
64-
- name: Mint engineer-bot App token
65-
id: setup
66-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
67-
with:
68-
app-id: ${{ secrets.ENGINEER_BOT_APP_ID }}
69-
private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }}
70-
7162
# Python + connector deps via the repo's OWN setup-poetry action: it
7263
# configures poetry against the internal JFrog mirror (the protected runner
7364
# is egress-blocked from pypi.org), runs `poetry lock` (tolerating a stale
7465
# lock), and installs the connector + its deps. This is the maintained,
7566
# egress-safe way this repo installs itself — the agent then runs
7667
# `poetry run python -m pytest tests/unit` to self-verify its red→green fix.
68+
# (The engineer builds/runs the connector, so unlike the reviewer it needs
69+
# the deps, not just an interpreter.)
7770
- name: Setup Poetry + connector deps (for pytest self-verify)
7871
uses: ./.github/actions/setup-poetry
7972
with:
8073
python-version: '3.11'
8174

82-
- name: Setup Node (for the Claude Code CLI the SDK spawns)
83-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
84-
with:
85-
node-version: '20'
86-
87-
# PAT-free engine install: mint a SECOND token, scoped to the engine repo
88-
# (contents:read), from the same engineer-bot App creds. The install step
89-
# below authenticates the private-engine clone with this instead of a
90-
# stored BOT_ENGINE_PAT. Requires the engineer-bot App installed on the
91-
# engine repo with contents:read.
92-
- name: Mint engine-scoped install token
93-
id: engine-token
94-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
75+
# Shared prelude: mint the engineer-bot token (pushes the fix branch,
76+
# comments) + the engine-scoped token, set up Node, install the pinned
77+
# engine (PAT-free). `token` output is used by the steps below.
78+
- name: Bot prelude (tokens + Node + engine install)
79+
id: prelude
80+
uses: ./.github/actions/bot-prelude
9581
with:
9682
app-id: ${{ secrets.ENGINEER_BOT_APP_ID }}
9783
private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }}
98-
owner: databricks
99-
repositories: databricks-bot-engine
100-
permission-contents: read
84+
engine-ref: b6205fb502566d617a456ccf3c37f3e4e3072ccd
10185

10286
- name: Resolve issue + gather context
10387
id: ctx
@@ -108,7 +92,7 @@ jobs:
10892
# chosen delimiter, which would otherwise inject extra env vars). The
10993
# number/url are safe shapes and exported directly.
11094
env:
111-
GH_TOKEN: ${{ steps.setup.outputs.token }}
95+
GH_TOKEN: ${{ steps.prelude.outputs.token }}
11296
EVENT_NAME: ${{ github.event_name }}
11397
INPUT_ISSUE: ${{ inputs.issue_number }}
11498
EVENT_ISSUE: ${{ github.event.issue.number }}
@@ -131,15 +115,6 @@ jobs:
131115
fi
132116
{ echo "ISSUE_TITLE<<$DELIM"; printf '%s\n' "$TITLE"; echo "$DELIM"; } >> "$GITHUB_ENV"
133117
134-
# Install the engine (REF mode, pinned) + Claude SDK/CLI via the LOCAL
135-
# composite (cross-repo `uses:` of the engine won't resolve here),
136-
# authenticated PAT-free by the engine-scoped token above.
137-
- name: Install engine + Claude SDK/CLI
138-
uses: ./.github/actions/install-bot-engine
139-
with:
140-
engine-ref: b6205fb502566d617a456ccf3c37f3e4e3072ccd
141-
engine-token: ${{ steps.engine-token.outputs.token }}
142-
143118
- name: Run author (bug-fix)
144119
id: author
145120
# Run from RUNNER_TEMP so the engine-rendered prompt's context file
@@ -168,7 +143,7 @@ jobs:
168143
# via the App token embedded in the remote URL (checkout ran with
169144
# persist-credentials: false, so this is the only push credential).
170145
env:
171-
GH_TOKEN: ${{ steps.setup.outputs.token }}
146+
GH_TOKEN: ${{ steps.prelude.outputs.token }}
172147
GITHUB_REPOSITORY: ${{ github.repository }}
173148
TEST_REPO_ROOT: ${{ github.workspace }}
174149
RUNNER_TEMP: ${{ runner.temp }}
@@ -190,7 +165,7 @@ jobs:
190165
# via --body-file so nothing reaches a shell.
191166
if: always() && steps.ctx.outputs.issue_number != ''
192167
env:
193-
GH_TOKEN: ${{ steps.setup.outputs.token }}
168+
GH_TOKEN: ${{ steps.prelude.outputs.token }}
194169
REPO: ${{ github.repository }}
195170
ISSUE: ${{ steps.ctx.outputs.issue_number }}
196171
OUTCOME: ${{ steps.author.outputs.outcome }}

.github/workflows/reviewer-bot-followup.yml

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Reviewer Bot — follow-up on review-comment replies.
22
#
3-
# Own-job composite caller (see reviewer-bot.yml for why we don't use the engine
4-
# reusable WORKFLOW: cross-repo reusable-workflow calls to the private engine
5-
# fail "workflow was not found"; cross-repo composite ACTIONS resolve in-org).
6-
# This file owns the job and replicates the reusable followup's steps with
7-
# cross-repo action refs, PAT-free (engine-scoped App token).
3+
# Own-job workflow (see reviewer-bot.yml: an external repo can't `uses:` the
4+
# internal engine's actions/workflows; the engine is pip-installed via the local
5+
# bot-prelude -> install-bot-engine composites). This file owns checkout, the
6+
# base-commit fetch, Setup Python, and the followup run; bot-prelude mints tokens
7+
# + Node + installs the engine, PAT-free.
88
#
99
# Enablement: no label gate — engage every non-fork open PR (this repo's policy).
1010
# The security floor (fork==false, PR open) is the job `if:` below; loop-
@@ -38,14 +38,6 @@ jobs:
3838
group: reviewer-bot-followup-pr-${{ github.event.pull_request.number }}
3939
cancel-in-progress: false
4040
steps:
41-
# Review-bot token (posts inline replies + resolves threads).
42-
- name: Mint review-bot App token
43-
id: app-token
44-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
45-
with:
46-
app-id: ${{ secrets.REVIEW_BOT_APP_ID }}
47-
private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }}
48-
4941
- name: Checkout PR head (read-only; agent only POSTS via GH_TOKEN)
5042
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
5143
with:
@@ -71,39 +63,25 @@ jobs:
7163
echo "::warning::Could not fetch PR base commit ${PR_BASE_SHA}. Continuing: followup.py fail-closes to an empty SHA allowlist, so SHA verification degrades gracefully but reconciliation still runs." >&2
7264
fi
7365
66+
# Reviewer reads code, doesn't run the connector — Python interpreter only.
7467
- name: Setup Python
7568
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
7669
with:
7770
python-version: '3.11'
7871

79-
- name: Setup Node (for the Claude Code CLI the SDK spawns)
80-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
81-
with:
82-
node-version: '20'
83-
84-
# PAT-free engine install: engine-scoped token (contents:read) from the
85-
# review-bot App creds — SEPARATE from app-token above (which is scoped to
86-
# THIS repo for PR posting).
87-
- name: Mint engine-scoped install token
88-
id: engine-token
89-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
72+
# Shared prelude: mint the review-bot token (posts replies / resolves
73+
# threads) + the engine-scoped token, set up Node, install the engine.
74+
- name: Bot prelude (tokens + Node + engine install)
75+
id: prelude
76+
uses: ./.github/actions/bot-prelude
9077
with:
9178
app-id: ${{ secrets.REVIEW_BOT_APP_ID }}
9279
private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }}
93-
owner: databricks
94-
repositories: databricks-bot-engine
95-
permission-contents: read
96-
97-
# LOCAL composite (cross-repo `uses:` of the engine won't resolve here).
98-
- name: Install engine + Claude SDK/CLI
99-
uses: ./.github/actions/install-bot-engine
100-
with:
10180
engine-ref: b6205fb502566d617a456ccf3c37f3e4e3072ccd
102-
engine-token: ${{ steps.engine-token.outputs.token }}
10381

10482
- name: Run reviewer follow-up
10583
env:
106-
GH_TOKEN: ${{ steps.app-token.outputs.token }}
84+
GH_TOKEN: ${{ steps.prelude.outputs.token }}
10785
GITHUB_REPOSITORY: ${{ github.repository }}
10886
PR_NUMBER: ${{ github.event.pull_request.number }}
10987
TRIGGER_COMMENT_ID: ${{ github.event.comment.id }}

0 commit comments

Comments
 (0)