mirror of
https://github.com/trailofbits/claude-code-config
synced 2026-06-21 14:11:43 +00:00
2917b69a30
- fix-issue: Add upstream remote detection, Exa research step, branch creation before implementation, CI-as-source-of-truth quality pipeline with per-language fallback tables, codegen sync checks, and self-review via pr-review-toolkit - review-pr: Add upstream remote detection, two-pass parallel review (pr-review-toolkit + Codex + Gemini), CI-discovered quality pipeline, Exa for fix research, structured PR comment format - merge-dependabot: New command for evaluating and merging dependabot PRs with config audit, transitive dependency analysis, parallel subagent evaluation, sequential merge with post-merge re-testing, and turn budget management - README: Add merge-dependabot to install block and commands section Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
583 lines
17 KiB
Markdown
583 lines
17 KiB
Markdown
# Merge Dependabot PRs
|
|
|
|
@description Evaluate and merge dependabot PRs with parallel builds, dependency-aware batching, and transitive dep analysis.
|
|
@arguments $REPO: GitHub org/repo (e.g., trailofbits/algo). $OPTIONS: Optional flags — "--skip-config-audit" skips Phase 0 (use in batch runs where config audit is a separate pass).
|
|
|
|
Clone $REPO if not already available locally:
|
|
|
|
```bash
|
|
gh repo clone $REPO /tmp/depbot-eval-$(echo "$REPO" | tr '/' '-') -- --depth=50 2>/dev/null || \
|
|
(cd /tmp/depbot-eval-$(echo "$REPO" | tr '/' '-') && git fetch origin)
|
|
```
|
|
|
|
Work from `/tmp/depbot-eval-{repo-slug}` for all subsequent phases.
|
|
|
|
Execute every phase below sequentially. Do not stop or ask for
|
|
confirmation at any phase.
|
|
|
|
## Turn Budget Management
|
|
|
|
If you are running as a background agent with a `max_turns` cap:
|
|
|
|
- **At 75% of turns used:** Stop launching new evaluations. Merge
|
|
any PRs already evaluated as PASS. Skip Phase 5's detailed
|
|
reports — print only the summary table.
|
|
- **At 90% of turns used:** Immediately print whatever summary you
|
|
have and stop. Do not start new evaluations or re-tests.
|
|
- **Prioritize merging over analysis.** If you must choose between
|
|
thorough analysis of the last PR and merging already-evaluated
|
|
PASS PRs, merge first.
|
|
|
|
## Phase 0: Dependabot Config Audit
|
|
|
|
If `$OPTIONS` includes `--skip-config-audit`, skip this entire
|
|
phase and proceed to Phase 1.
|
|
|
|
Detect all package ecosystems present in the repo by checking for
|
|
these indicator files:
|
|
|
|
| Indicator file(s) | Ecosystem |
|
|
|---|---|
|
|
| `pyproject.toml` + `uv.lock` | `uv` |
|
|
| `pyproject.toml` (no `uv.lock`), `requirements*.txt`, `setup.py`, `setup.cfg` | `pip` |
|
|
| `Cargo.toml` | `cargo` |
|
|
| `package.json` | `npm` |
|
|
| `go.mod` | `gomod` |
|
|
| `Gemfile` | `bundler` |
|
|
| `Dockerfile`, `docker-compose.yml` | `docker` |
|
|
| `.github/workflows/*.yml` | `github-actions` |
|
|
| `composer.json` | `composer` |
|
|
| `*.csproj`, `*.fsproj` | `nuget` |
|
|
|
|
Read `.github/dependabot.yml`. Verify all five conditions:
|
|
|
|
1. **Coverage** — every detected ecosystem has a corresponding
|
|
`updates` entry with the correct `package-ecosystem` value and
|
|
appropriate `directory` (usually `"/"`)
|
|
2. **uv vs pip** — if a directory has both `pyproject.toml` and
|
|
`uv.lock`, the ecosystem MUST be `uv`, not `pip`. The `pip`
|
|
ecosystem does not update `uv.lock`, which causes PRs that
|
|
modify `pyproject.toml` but leave `uv.lock` out of sync.
|
|
If any entry uses `pip` where `uv` is correct, flag it for
|
|
correction.
|
|
3. **Schedule** — every entry has `schedule.interval: "weekly"`
|
|
4. **Cooldown** — every entry has a `cooldown` block with
|
|
`default-days: 7`. This prevents dependabot from flooding the
|
|
PR queue with rapid re-attempts after a PR is closed or merged.
|
|
5. **Grouped updates** — every entry has a `groups` key with at
|
|
least one group using `patterns: ["*"]` or more specific
|
|
grouping patterns
|
|
|
|
If the file is missing or any condition fails, create a corrective
|
|
PR:
|
|
|
|
1. `git checkout -b fix/dependabot-config`
|
|
2. Write or update `.github/dependabot.yml`. Every `updates` entry
|
|
MUST include all four required blocks. Use this template for each
|
|
ecosystem entry:
|
|
|
|
```yaml
|
|
- package-ecosystem: "{ecosystem}"
|
|
directory: "/"
|
|
schedule:
|
|
interval: "weekly"
|
|
cooldown:
|
|
default-days: 7
|
|
groups:
|
|
{ecosystem}-dependencies:
|
|
patterns:
|
|
- "*"
|
|
```
|
|
|
|
When updating an existing file, preserve any extra fields already
|
|
present (labels, reviewers, open-pull-requests-limit, etc.) and
|
|
only add missing blocks.
|
|
|
|
3. `git commit -m "chore: update dependabot config for full coverage, weekly schedule, 7-day cooldown, and grouped updates"`
|
|
4. `git push origin fix/dependabot-config`
|
|
5. `gh pr create --repo $REPO --title "Update dependabot configuration" --body "Adds missing ecosystem coverage, enforces weekly schedule, 7-day cooldown, and grouped updates."`
|
|
6. `git checkout main`
|
|
|
|
Continue to Phase 1 regardless — this PR is non-blocking.
|
|
|
|
## Phase 1: Discovery & Baseline
|
|
|
|
### 1a. Fetch dependabot PRs
|
|
|
|
```bash
|
|
gh pr list --repo $REPO --author "app/dependabot" --state open \
|
|
--json number,title,headRefName,labels,files,mergeable
|
|
```
|
|
|
|
If zero PRs are returned, print "No open dependabot PRs for $REPO"
|
|
and stop.
|
|
|
|
### 1b. Categorize PRs
|
|
|
|
For each PR, examine its changed files:
|
|
|
|
- **Actions dep** — all changed files are under `.github/workflows/`
|
|
or `.github/actions/`
|
|
- **Library dep** — everything else (lockfiles, manifests, version
|
|
pins, dependency specification files)
|
|
|
|
Store the categorized list for later phases.
|
|
|
|
### 1c. Baseline build and test
|
|
|
|
Verify the main branch is healthy before evaluating any PR.
|
|
|
|
1. Check out the default branch: use whatever
|
|
`gh repo view --repo $REPO --json defaultBranchRef` reports
|
|
2. Discover the build system — follow the same discovery process
|
|
described in Phase 3's subagent instructions (read CI workflows
|
|
first, then Makefile, then language-specific defaults)
|
|
3. Run the build command. If it fails, **stop the entire command**
|
|
and report: "Main branch build is broken. Fix main before
|
|
processing dependabot PRs." Include the error output.
|
|
4. Run the test command. If tests fail, **stop the entire command**
|
|
and report: "Main branch tests are failing. Fix main before
|
|
processing dependabot PRs." Include which tests fail.
|
|
5. Record the baseline:
|
|
- Full dependency tree from lockfile(s) (`pip freeze`,
|
|
`cargo tree`, `npm ls --all`, `go list -m all`, etc.)
|
|
- List of passing tests
|
|
- Build output summary
|
|
|
|
Store the baseline data — subagents need it for comparison.
|
|
|
|
## Phase 2: Dependency Graph Analysis
|
|
|
|
### 2a. Build the transitive dependency map
|
|
|
|
Parse the repo's lockfile(s) to understand the full dependency
|
|
tree:
|
|
|
|
| Ecosystem | Lockfile | Tree command |
|
|
|---|---|---|
|
|
| uv | `uv.lock` | `uv pip freeze` (after `uv sync`) |
|
|
| pip | `poetry.lock`, `requirements*.txt` | `pip freeze` |
|
|
| cargo | `Cargo.lock` | `cargo tree` |
|
|
| npm | `package-lock.json`, `pnpm-lock.yaml` | `npm ls --all` or `pnpm ls --depth=Infinity` |
|
|
| gomod | `go.sum` | `go list -m all` |
|
|
| bundler | `Gemfile.lock` | `bundle list` |
|
|
|
|
For each library dep PR, identify which direct dependency it bumps
|
|
(from the PR title and changed files). Look up that package in the
|
|
dependency tree to find all its transitive dependents and
|
|
dependencies.
|
|
|
|
### 2b. Group overlapping PRs into batches
|
|
|
|
Two PRs overlap if:
|
|
- PR A bumps package X, PR B bumps package Y, and X depends on Y
|
|
(or Y depends on X) in the transitive tree
|
|
- Both PRs modify the same lockfile section for shared transitive
|
|
dependencies
|
|
|
|
Group overlapping PRs into **batches**. PRs with no overlaps
|
|
remain **independent** work units.
|
|
|
|
Actions dep PRs are always independent work units — they don't
|
|
interact with library dependency trees.
|
|
|
|
### 2c. Sort and queue
|
|
|
|
Sort work units in topological order — leaf dependencies first,
|
|
core/shared dependencies last. This ensures earlier merges are
|
|
less likely to affect later ones.
|
|
|
|
If there are more than 5 work units total, process in **waves
|
|
of 5**. The first wave starts immediately; subsequent waves start
|
|
after the previous wave completes.
|
|
|
|
Print the grouping plan before proceeding:
|
|
- List each work unit (batch or independent)
|
|
- Show which PRs are in each batch and why they were grouped
|
|
- Show the evaluation order
|
|
|
|
## Phase 3: Parallel Evaluation
|
|
|
|
### 3a. Fetch PR branches
|
|
|
|
For each work unit, fetch the PR branch into the local repo:
|
|
|
|
```bash
|
|
git fetch origin pull/{number}/head:pr-{number}
|
|
```
|
|
|
|
Do NOT use `wt switch` — shallow clones do not support worktrees
|
|
reliably. Use `git checkout` directly when evaluating each PR.
|
|
|
|
### 3b. Launch subagents
|
|
|
|
Launch up to 5 subagents in parallel using the Task tool. Each
|
|
call must use:
|
|
- `subagent_type: "general-purpose"`
|
|
- The appropriate prompt below (library or actions)
|
|
|
|
Send all Task calls in a **single message** for parallel execution.
|
|
If more than 5 work units, wait for the current wave to complete
|
|
before launching the next.
|
|
|
|
Pass each subagent:
|
|
- The repo directory path
|
|
- The PR number(s) and title(s)
|
|
- The baseline dependency tree from Phase 1
|
|
- The repo's build and test commands discovered in Phase 1
|
|
|
|
### Subagent prompt: Library Dep Evaluation
|
|
|
|
Use this prompt for each library dep work unit.
|
|
|
|
---
|
|
|
|
You are evaluating dependabot PR(s) for merge safety. Work
|
|
in the repo directory: {repo_path}
|
|
|
|
**Repo:** $REPO
|
|
**PR(s) to evaluate:** {pr_numbers_and_titles}
|
|
**Baseline dependency tree from main:**
|
|
|
|
```
|
|
{baseline_dep_tree}
|
|
```
|
|
|
|
**Build command:** {build_command}
|
|
**Test command:** {test_command}
|
|
|
|
Execute every step. Do not skip steps. Do not ask for confirmation.
|
|
|
|
**STEP 1 — Checkout**
|
|
|
|
```bash
|
|
cd {repo_path}
|
|
git fetch origin pull/{number}/head:pr-{number}
|
|
git checkout pr-{number}
|
|
```
|
|
|
|
For batches (multiple PRs), create a temporary merge branch:
|
|
|
|
```bash
|
|
cd {repo_path}
|
|
git checkout -b test-batch-{batch_id} main
|
|
git fetch origin pull/{pr1}/head:pr-{pr1}
|
|
git fetch origin pull/{pr2}/head:pr-{pr2}
|
|
git merge pr-{pr1} pr-{pr2} --no-edit
|
|
```
|
|
|
|
If the merge has conflicts, report FAIL with the conflicting files
|
|
and stop.
|
|
|
|
**STEP 2 — Transitive dependency analysis**
|
|
|
|
Generate the full dependency tree using the same command that
|
|
produced the baseline. Compare against the baseline and report:
|
|
|
|
```
|
|
DIRECT CHANGES:
|
|
- {package}: {old_version} → {new_version}
|
|
|
|
TRANSITIVE CHANGES:
|
|
- {package}: {old_version} → {new_version} (depended on by: {parent})
|
|
|
|
NEW TRANSITIVE DEPS:
|
|
- {package} {version} (pulled in by: {parent})
|
|
|
|
REMOVED TRANSITIVE DEPS:
|
|
- {package} {version}
|
|
|
|
FLAGS:
|
|
- DOWNGRADE: {package} went from {higher} to {lower}
|
|
- MAJOR BUMP: {package} crossed a major version boundary
|
|
```
|
|
|
|
If there are zero flags and zero new/removed transitive deps,
|
|
note "Clean transitive dependency change."
|
|
|
|
**STEP 3 — Build**
|
|
|
|
Run the build command: {build_command}
|
|
|
|
If the build command was not provided (blank), discover it:
|
|
|
|
1. Read `.github/workflows/` for build steps
|
|
2. Check `Makefile` or `justfile` for a `build` target
|
|
3. Language-specific defaults:
|
|
|
|
| Manifest | Default |
|
|
|---|---|
|
|
| `Cargo.toml` | `cargo build` |
|
|
| `pyproject.toml` | `uv pip install -e ".[dev]"` |
|
|
| `package.json` | `pnpm install && pnpm build` |
|
|
| `go.mod` | `go build ./...` |
|
|
| `Gemfile` | `bundle install` |
|
|
|
|
If the build fails, report FAIL with exact error output and stop.
|
|
|
|
**STEP 4 — Test**
|
|
|
|
Run the test command: {test_command}
|
|
|
|
If the test command was not provided (blank), discover it using the
|
|
same approach as Step 3:
|
|
|
|
| Manifest | Default |
|
|
|---|---|
|
|
| `Cargo.toml` | `cargo test` |
|
|
| `pyproject.toml` | `pytest -q` |
|
|
| `package.json` | `pnpm test` |
|
|
| `go.mod` | `go test ./...` |
|
|
| `Gemfile` | `bundle exec rspec` or `bundle exec rake test` |
|
|
|
|
If tests fail, check whether the same tests also fail on the main
|
|
branch baseline. Pre-existing failures do not count against this PR.
|
|
|
|
If there are new test failures (pass on main, fail on this PR),
|
|
report FAIL with the failing test names and error output.
|
|
|
|
**STEP 5 — Build matrix gap analysis**
|
|
|
|
Read `.github/workflows/` for `strategy.matrix` blocks. For each
|
|
matrix dimension, report what was tested locally vs. what only
|
|
runs in CI:
|
|
|
|
| Dimension | Example values | Testable locally? |
|
|
|---|---|---|
|
|
| OS | ubuntu, macos, windows | Current OS only |
|
|
| Language version | python 3.9-3.12 | Installed version only |
|
|
| Dependency version | numpy 1.x, 2.x | PR's version only |
|
|
|
|
Report the matrix gaps and assess risk:
|
|
- **HIGH risk:** The dependency is known to have version-specific
|
|
behavior (e.g., numpy/scipy ABI, pytorch CUDA builds, native
|
|
extensions) and CI tests versions we couldn't test locally
|
|
- **LOW risk:** The matrix covers OS variants or formatting
|
|
differences unlikely to be affected by a dependency bump
|
|
|
|
If there is no matrix strategy in CI, report "No CI matrix — single
|
|
configuration build."
|
|
|
|
**STEP 6 — Verdict**
|
|
|
|
**PASS** — all conditions met:
|
|
- Build succeeds
|
|
- All tests pass (or only pre-existing failures)
|
|
- No transitive dependency flags (downgrades, major bumps)
|
|
- No high-risk matrix gaps
|
|
|
|
**WARN** — build and tests pass, but concerns exist:
|
|
- New transitive dependencies introduced
|
|
- Transitive dep crossed a major version boundary
|
|
- High-risk matrix gaps
|
|
- List each specific concern
|
|
|
|
**FAIL** — any of:
|
|
- Build fails
|
|
- New test failures
|
|
- Merge conflicts
|
|
|
|
Format the final report:
|
|
|
|
```
|
|
## Evaluation Report: PR #{number} — {title}
|
|
|
|
**Verdict: {PASS|WARN|FAIL}**
|
|
|
|
### Transitive Dependency Analysis
|
|
{step 2 output}
|
|
|
|
### Build Result
|
|
{pass/fail with output if failed}
|
|
|
|
### Test Result
|
|
{pass/fail with details}
|
|
|
|
### Matrix Gap Analysis
|
|
{step 5 output}
|
|
|
|
### Concerns
|
|
{list of concerns, or "None"}
|
|
```
|
|
|
|
---
|
|
|
|
### Subagent prompt: Actions Dep Evaluation
|
|
|
|
Use this prompt for each GitHub Actions version bump PR.
|
|
|
|
---
|
|
|
|
You are evaluating a GitHub Actions version bump for merge safety.
|
|
Work in the repo directory: {repo_path}
|
|
|
|
**Repo:** $REPO
|
|
**PR to evaluate:** #{number} — {title}
|
|
|
|
Execute every step.
|
|
|
|
**STEP 1 — Checkout**
|
|
|
|
```bash
|
|
cd {repo_path}
|
|
git fetch origin pull/{number}/head:pr-{number}
|
|
git checkout pr-{number}
|
|
```
|
|
|
|
**STEP 2 — Diff analysis**
|
|
|
|
Run `git diff main -- .github/` to see what changed. Identify:
|
|
- Which action(s) were bumped
|
|
- Old and new versions (or SHA pins)
|
|
- Whether this is a patch, minor, or major version bump
|
|
|
|
For major version bumps, use Exa (`mcp__exa__web_search_exa`) to
|
|
search for breaking changes:
|
|
`{action_name} v{old_major} to v{new_major} migration breaking changes`
|
|
|
|
**STEP 3 — Workflow validation**
|
|
|
|
Run: `actionlint .github/workflows/`
|
|
|
|
If `actionlint` is not installed, note this and skip to Step 4.
|
|
|
|
Distinguish pre-existing errors (also present on main) from new
|
|
errors introduced by the version bump. Only new errors count
|
|
against this PR.
|
|
|
|
**STEP 4 — Pin verification**
|
|
|
|
Check every `uses:` line in changed workflow files. Verify the
|
|
SHA-pin format:
|
|
|
|
```yaml
|
|
# GOOD:
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
|
|
# BAD (tag only):
|
|
uses: actions/checkout@v4
|
|
|
|
# BAD (SHA without version comment):
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
```
|
|
|
|
Flag tag-only references as a WARN concern (not FAIL).
|
|
|
|
**STEP 5 — Verdict**
|
|
|
|
**PASS** — all conditions met:
|
|
- actionlint clean (or only pre-existing warnings)
|
|
- No breaking changes for major version bumps
|
|
- Actions are SHA-pinned with version comments
|
|
|
|
**WARN** — concerns exist:
|
|
- Tag pin instead of SHA pin
|
|
- Major version bump with breaking changes that appear handled
|
|
|
|
**FAIL** — any of:
|
|
- New actionlint errors
|
|
- Major version bump with unhandled breaking changes
|
|
|
|
Format the report the same way as library dep evaluations.
|
|
|
|
---
|
|
|
|
## Phase 4: Sequential Merge
|
|
|
|
Collect all subagent evaluation reports. Process work units in the
|
|
dependency order established in Phase 2.
|
|
|
|
### 4a. Merge passing PRs
|
|
|
|
For each work unit with a **PASS** verdict, in order:
|
|
|
|
1. Approve the PR:
|
|
```bash
|
|
gh pr review --repo $REPO --approve {number} \
|
|
--body "Automated evaluation: build, tests, and transitive dependency analysis passed."
|
|
```
|
|
|
|
2. Force merge as admin:
|
|
```bash
|
|
gh pr merge --repo $REPO --squash --admin {number}
|
|
```
|
|
|
|
3. Verify the merge succeeded (gh pr merge produces no output on
|
|
success):
|
|
```bash
|
|
gh pr view --repo $REPO {number} --json state
|
|
```
|
|
Confirm `state` is `"MERGED"`. If not, report the error and
|
|
skip to the next work unit.
|
|
|
|
4. Update main locally:
|
|
```bash
|
|
git checkout main && git pull origin main
|
|
```
|
|
|
|
5. **Re-test the next work unit** before merging it. Checkout its
|
|
branch and rebase onto updated main:
|
|
```bash
|
|
git checkout pr-{next_number}
|
|
git merge main --no-edit
|
|
```
|
|
Re-run the build and test commands. If the re-test fails, mark
|
|
this work unit as **SKIPPED** with reason: "Passed independent
|
|
evaluation but failed after merging prior PRs. Likely conflicts
|
|
with: {previously merged PR numbers}." Continue to the next
|
|
work unit.
|
|
|
|
For **batched** work units, merge each PR in the batch
|
|
sequentially using the same approve-then-merge flow.
|
|
|
|
### 4b. Handle WARN and FAIL verdicts
|
|
|
|
- **WARN** — do not merge. Include in final report with specific
|
|
concerns. These need human review.
|
|
- **FAIL** — do not merge. Include full error context for diagnosis.
|
|
|
|
## Phase 5: Cleanup & Report
|
|
|
|
### 5a. Cleanup
|
|
|
|
Return to main and delete local PR branches:
|
|
|
|
```bash
|
|
git checkout main
|
|
git branch -D pr-{number} test-batch-{batch_id} # for each evaluated PR/batch
|
|
```
|
|
|
|
### 5b. Summary report
|
|
|
|
Print a summary table:
|
|
|
|
```
|
|
## Dependabot PR Summary for $REPO
|
|
|
|
| PR | Title | Type | Verdict | Action | Notes |
|
|
|----|-------|------|---------|--------|-------|
|
|
```
|
|
|
|
Include every evaluated PR with its verdict and outcome.
|
|
|
|
Below the table, print totals:
|
|
|
|
```
|
|
**Merged:** {count}
|
|
**Skipped (WARN — needs human review):** {count}
|
|
**Failed:** {count}
|
|
**Skipped (post-merge conflict):** {count}
|
|
```
|
|
|
|
If a dependabot config PR was created in Phase 0:
|
|
|
|
```
|
|
**Dependabot config PR:** #{number}
|
|
```
|
|
|
|
### 5c. Detailed reports for non-merged PRs
|
|
|
|
For each WARN, FAIL, or SKIPPED PR, print the full evaluation
|
|
report from the subagent so the user has all context needed to
|
|
decide or fix the issue.
|