Skip to content

Commit 2cd4bd5

Browse files
authored
refactor: migrate Deequ Bot to the shared Shadow engine (#756)
* Migrate Deequ Bot to the shared Shadow engine Replace the vendored copy of the bot (src/scripts/issue_bot/) with consuming the shared engine sudsali/shadow via its reusable workflow, SHA-pinned to 3a057597420d7123df162bfe4686aaafb5f7eccd (v1.2). One engine, maintained once upstream, instead of a per-repo fork. Zero intended behavior change: - Deequ's language-tuned prompts stay in Secrets Manager (deequ-bot/* namespace) and are selected via prompt_sm_prefix: deequ-bot — the engine fetches all eight (pr-investigator/critic/reporter + 2 commit nudges + issue-classify/ issue-respond/followup) in place of its bundled language-agnostic defaults. - .shadow.yml sets bot.name: deequ-bot so the clean-review marker stays <!-- deequ-bot:clean --> (auto-approve.yml keys on it) and the Scala codebase config (src/main/scala, .scala) matches the vendored setup. - All four surfaces (PR review, issue triage, issue-respond, follow-up), all four triggers, and the job-level trigger guard are preserved. Workflow name stays "Deequ Bot" (auto-approve.yml keys on it too). BOT_AGENT_PIPELINE=1 is already the live path, so the dropped legacy file-review prompts are dead code. generate_kb.py stays (update-kb.yml still runs it); its stale paths-ignore entries for the removed dirs are cleaned up. Requires a one-time OIDC trust update on the bot's IAM role (job_workflow_ref → shadow's reusable workflow), applied out-of-band with an @* version wildcard so future engine bumps need no IAM change. * Clarify OIDC trust comment: hardening is optional and post-merge only The caller works on the current sub-only IAM trust with no change (its OIDC sub is still repo:awslabs/deequ:*). The job_workflow_ref pin is optional defense-in-depth to be applied only after this PR merges — applying it while the base branch still runs the vendored bot denies AssumeRole. Comment-only. * Add top-level permissions to the Shadow caller workflow A reusable-workflow caller must grant, at the top level, the union of permissions its nested jobs request — GitHub caps nested jobs at the caller's token and otherwise fails at startup ('is requesting ... but is only allowed none'). The reusable workflow's analyze (id-token:write, pull-requests:read) and act (issues:write, pull-requests:write) jobs need this union; the vendored workflow declared these per-job, which doesn't carry across the workflow_call boundary. Fixes the workflow_dispatch startup_failure. * Trim workflow/config comments to WHY-only Drop WHAT-narration and multi-paragraph explanations; keep only comments that prevent a concrete mistake (name coupling, dual-SHA pin, pull_request_target no-ref, permissions ceiling, marker, if-guard). * Rename bot identity deequ-bot -> shadow Surface the Shadow product to the OSS community. Renames the user-facing identity in lockstep across the three coupled points: .shadow.yml bot.name (the clean-review marker), the workflow name, and auto-approve.yml (both the workflows[] trigger and the CLEAN_MARKER it greps). prompt_sm_prefix stays deequ-bot (private Secrets Manager namespace). * Pin engine to v1.3 and set Shadow attribution footer Bumps the shared-engine SHA to v1.3 (adds bot.attribution) and sets the footer attribution so posted comments surface the Shadow product to the OSS community.
1 parent 0b8cea5 commit 2cd4bd5

25 files changed

Lines changed: 55 additions & 8233 deletions

.github/workflows/auto-approve.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Auto-Approve Clean PRs
22

33
on:
44
workflow_run:
5-
workflows: ["Java CI with Maven", "Deequ Bot"]
5+
workflows: ["Java CI with Maven", "Shadow"]
66
types: [completed]
77

88
permissions:
@@ -72,7 +72,7 @@ jobs:
7272
owner, repo, pull_number: prNumber
7373
});
7474
75-
const CLEAN_MARKER = '<!-- deequ-bot:clean -->';
75+
const CLEAN_MARKER = '<!-- shadow:clean -->';
7676
7777
const latestBot = reviews
7878
.filter(r => r.user.login === 'github-actions[bot]')

.github/workflows/issue-bot.yml

Lines changed: 34 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
name: Deequ Bot
1+
name: Shadow # load-bearing: auto-approve.yml keys on this exact name
2+
# To upgrade the engine, bump the SHA in BOTH `uses:` and `shadow_ref` below
3+
# (GitHub forbids expressions in `uses:`, so they can't share a variable).
24

35
on:
46
issues:
57
types: [opened, reopened]
6-
pull_request_target: # Runs base branch code with secrets; safe because bot fetches diff via API, never executes PR code. NEVER add ref: to checkout.
8+
pull_request_target: # base-branch checkout only; never add ref: (see SECURITY A1)
79
types: [opened, reopened, synchronize]
810
issue_comment:
911
types: [created]
@@ -17,128 +19,42 @@ on:
1719
type: boolean
1820
default: true
1921

20-
# Serialize per issue/PR to prevent duplicate comments
22+
# Union of the reusable workflow's nested-job permissions; a caller must grant
23+
# these or the call fails at startup (each nested job still narrows its own set).
24+
permissions:
25+
contents: read
26+
id-token: write
27+
pull-requests: write
28+
issues: write
29+
30+
# inputs.issue_number fallback: workflow_dispatch has no event PR/issue number.
2131
concurrency:
2232
group: bot-${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
2333
cancel-in-progress: false
2434

2535
jobs:
26-
analyze:
27-
runs-on: ubuntu-latest
28-
timeout-minutes: 10
36+
shadow:
37+
# Avoid self-triggered loops (bot's own comments) and double-reviewing a PR
38+
# from both issue_comment and pull_request_target; dispatch is always manual.
2939
if: >-
3040
(github.event_name == 'workflow_dispatch') ||
3141
(github.actor != 'github-actions[bot]' &&
3242
(github.event.issue.pull_request == null || github.event_name == 'pull_request_target'))
33-
permissions:
34-
contents: read
35-
id-token: write
36-
37-
steps:
38-
- name: Checkout repository
39-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
40-
with:
41-
persist-credentials: false
42-
43-
- name: Configure AWS credentials
44-
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
45-
with:
46-
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
47-
aws-region: us-east-1
48-
49-
- name: Set up Python
50-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
51-
with:
52-
python-version: "3.12"
53-
54-
- name: Install dependencies
55-
run: pip install requests==2.33.1 boto3==1.42.94
56-
57-
- name: Run analysis
58-
env:
59-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60-
GITHUB_REPOSITORY: ${{ github.repository }}
61-
ISSUE_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
62-
EVENT_TYPE: ${{ github.event_name }}
63-
EVENT_ACTION: ${{ github.event.action }}
64-
EVENT_BEFORE: ${{ github.event.before }}
65-
EVENT_AFTER: ${{ github.event.pull_request.head.sha || github.event.after }}
66-
GITHUB_ACTOR: ${{ github.actor }}
67-
KB_S3_BUCKET: ${{ secrets.KB_S3_BUCKET }}
68-
KB_S3_KEY: ${{ secrets.KB_S3_KEY }}
69-
BEDROCK_MODEL_ID: ${{ secrets.BEDROCK_MODEL_ID }}
70-
BEDROCK_REPORTER_MODEL_ID: ${{ vars.BEDROCK_REPORTER_MODEL_ID }}
71-
BEDROCK_CRITIC_MODEL_ID: ${{ vars.BEDROCK_CRITIC_MODEL_ID }}
72-
GUARDRAIL_ID: ${{ secrets.GUARDRAIL_ID }}
73-
GUARDRAIL_VERSION: ${{ secrets.GUARDRAIL_VERSION }}
74-
SM_ISSUE_CLASSIFY_PROMPT: deequ-bot/issue-classify-prompt
75-
SM_ISSUE_RESPOND_PROMPT: deequ-bot/issue-respond-prompt
76-
SM_PR_FILE_REVIEW_PROMPT: deequ-bot/pr-file-review-prompt
77-
SM_PR_FILE_REVIEW_REPORT_PROMPT: deequ-bot/pr-file-review-report-prompt
78-
SM_PR_INVESTIGATOR_PROMPT: deequ-bot/pr-investigator-prompt
79-
SM_PR_CRITIC_PROMPT: deequ-bot/pr-critic-prompt
80-
SM_PR_REPORTER_PROMPT: deequ-bot/pr-reporter-prompt
81-
SM_PR_INVESTIGATOR_COMMIT_PROMPT: deequ-bot/pr-investigator-commit-prompt
82-
SM_PR_CRITIC_COMMIT_PROMPT: deequ-bot/pr-critic-commit-prompt
83-
SM_FOLLOWUP_PROMPT: deequ-bot/followup-prompt
84-
# Flip BOT_AGENT_PIPELINE to "1" to enable the 3-agent (Investigator+Critic+Reporter) pipeline.
85-
# When unset/empty, the legacy two-phase flow runs unchanged.
86-
# NOTE: Set as a REPOSITORY VARIABLE (Settings → Secrets and variables → Actions → Variables tab),
87-
# NOT a secret. Misplacing it under "Secrets" leaves vars.BOT_AGENT_PIPELINE empty → legacy flow
88-
# runs silently. To verify: `gh api repos/awslabs/deequ/actions/variables` should list it.
89-
BOT_AGENT_PIPELINE: ${{ vars.BOT_AGENT_PIPELINE || '' }}
90-
CODEBASE_SRC_DIR: src/main/scala
91-
CODEBASE_FILE_EXT: .scala
92-
DRY_RUN: ${{ inputs.dry_run || 'false' }}
93-
ARTIFACT_PATH: ${{ runner.temp }}/bot_result.json
94-
run: python -m issue_bot.main analyze
95-
working-directory: src/scripts
96-
97-
- name: Upload artifact
98-
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
99-
with:
100-
name: bot-result
101-
path: ${{ runner.temp }}/bot_result.json
102-
retention-days: 30
103-
104-
act:
105-
runs-on: ubuntu-latest
106-
timeout-minutes: 1
107-
needs: analyze
108-
permissions:
109-
contents: read
110-
issues: write
111-
pull-requests: write
112-
113-
steps:
114-
- name: Checkout repository
115-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
116-
with:
117-
persist-credentials: false
118-
119-
- name: Set up Python
120-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
121-
with:
122-
python-version: "3.12"
123-
124-
- name: Install dependencies
125-
run: pip install requests==2.33.1 boto3==1.42.94
126-
127-
- name: Download artifact
128-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
129-
with:
130-
name: bot-result
131-
path: ${{ runner.temp }}
132-
133-
- name: Execute actions
134-
env:
135-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136-
GITHUB_REPOSITORY: ${{ github.repository }}
137-
ISSUE_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
138-
EVENT_TYPE: ${{ github.event_name }}
139-
EVENT_ACTION: ${{ github.event.action }}
140-
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
141-
DRY_RUN: ${{ inputs.dry_run || 'false' }}
142-
ARTIFACT_PATH: ${{ runner.temp }}/bot_result.json
143-
run: python -m issue_bot.main act
144-
working-directory: src/scripts
43+
uses: sudsali/shadow/.github/workflows/shadow-review.yml@0ff3d759e3860fe5439682f51b6cd6352bf271c3
44+
with:
45+
pr_number: ${{ inputs.issue_number }}
46+
dry_run: ${{ inputs.dry_run && 'true' || 'false' }}
47+
shadow_ref: 0ff3d759e3860fe5439682f51b6cd6352bf271c3
48+
aws_region: us-east-1
49+
prompt_sm_prefix: deequ-bot
50+
secrets:
51+
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
52+
GUARDRAIL_ID: ${{ secrets.GUARDRAIL_ID }}
53+
GUARDRAIL_VERSION: ${{ secrets.GUARDRAIL_VERSION }}
54+
KB_S3_BUCKET: ${{ secrets.KB_S3_BUCKET }}
55+
KB_S3_KEY: ${{ secrets.KB_S3_KEY }}
56+
BEDROCK_MODEL_ID: ${{ secrets.BEDROCK_MODEL_ID }}
57+
# Repo variables (not secrets); empty falls back to engine defaults.
58+
BEDROCK_REPORTER_MODEL_ID: ${{ vars.BEDROCK_REPORTER_MODEL_ID }}
59+
BEDROCK_CRITIC_MODEL_ID: ${{ vars.BEDROCK_CRITIC_MODEL_ID }}
60+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

.github/workflows/update-kb.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ on:
55
branches: [master]
66
paths-ignore:
77
- '.github/workflows/**'
8-
- 'src/scripts/issue_bot/**'
9-
- 'src/scripts/tests/**'
108
workflow_dispatch:
119

1210
jobs:

.shadow.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Shadow engine config for awslabs/deequ.
2+
3+
codebase:
4+
src_dir: src/main/scala
5+
file_ext: .scala
6+
test_dir: src/test/scala
7+
language: scala
8+
9+
bot:
10+
name: shadow # renders the <!-- shadow:clean --> marker auto-approve.yml greps for
11+
attribution: "Reviewed by Shadow · github.com/sudsali/shadow"
12+
escalate_label: needs-human
13+
max_replies: 2
14+
max_runs_per_hour: 20
15+
16+
models:
17+
investigator: us.anthropic.claude-opus-4-7
18+
critic: us.anthropic.claude-opus-4-7
19+
reporter: us.anthropic.claude-haiku-4-5-20251001-v1:0

src/scripts/issue_bot/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/scripts/issue_bot/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)