name: dependabot-ops
description: Add Dependabot config to a fleet of repos, triage auto-PRs that fail CI, and manage the auto-PR lifecycle. Covers: auto-detecting lockfile ecosystem (npm/pnpm/yarn/bun/composer/pip), writing .github/dependabot.yml per-repo, understanding why Dependabot PRs often fail CI (peer-dep conflicts, major version bumps), and the "billing cap vs Dependabot PR" classification trick. From a real 35-repo Dependabot rollout.
sources: dependabot, github-actions
report_count: 35
Dependabot Operations
Dependabot is GitHub's auto-PR tool for dependency updates. Most fleets have it off by default; adding it to a fleet is a single-config-per-repo job, but the operations don't end there. Dependabot PRs often fail CI on auto-generated dependency bumps, creating noise in the fleet status that has to be triaged.
This skill covers the full lifecycle: add the config, understand the PR failures, triage them, and decide which to merge vs close.
Adding Dependabot to a Fleet
Per-repo config (.github/dependabot.yml)
The config goes in .github/dependabot.yml at the repo root. The structure:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
groups:
production-dependencies:
dependency-type: "production"
development-dependencies:
dependency-type: "development"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
For PHP repos:
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 3
For Python repos:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 3
Auto-detecting the ecosystem
Before writing the config, check which lockfiles exist:
ecosystems = set()
for path in ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"]:
if file_exists(repo, path):
ecosystems.add("npm")
if file_exists(repo, "composer.json"):
ecosystems.add("composer")
for pip_file in ["requirements.txt", "pyproject.toml", "Pipfile"]:
if file_exists(repo, pip_file):
ecosystems.add("pip")
break
If no lockfiles are found (e.g., a repo with just static HTML files), skip Dependabot — it won't have anything to update.
Why Dependabot PRs often fail CI
Dependabot's automation creates a PR with the new version. It doesn't fix anything else. The PR's CI runs against:
- The new dep version
- The existing source code
- The existing test suite
The new version might:
- Have a breaking change (major version bump)
- Have a peer-dep conflict with another dep
- Require a config change (e.g., a Prisma 5→7 dep change requires a new
prisma.config.ts)
- Break a build (e.g., a new version of a library moved its exports)
The auto-PR's CI failure is expected in many cases. The fix:
- For breaking changes: update the source code in the PR, or close the PR and apply the upgrade manually
- For peer-dep conflicts: pin compatible versions
- For config changes: usually need a manual migration
"Billing cap vs Dependabot PR" classification
This is a fleet-audit pattern. When you see a red CI Build run, classify it:
-
Check the head branch:
- If it starts with
dependabot/, the failure is on a Dependabot auto-PR. Not a real issue — main branch is unaffected.
- If it's
main or master, the failure is on the main branch. Real issue.
-
Check the run conclusion:
cancelled → newer push superseded this run. Not an issue.
failure with annotation spending limit needs to be increased → billing cap. Not a code issue, user must increase limit.
failure with real error in the log → real code issue. Needs source fix.
-
Combine for the final classification:
main branch + spending limit annotation = billing cap
main branch + real error = real source issue
dependabot/* branch + any failure = Dependabot PR noise, can ignore for now
Triage Workflow for Dependabot PRs
When a Dependabot PR fails CI:
- Look at the PR's branch name: tells you which dep is being updated
- Look at the CI log: tells you why it failed
- Decide:
- Close without merging: the upgrade isn't critical, or you'd rather do it manually later
- Merge as-is: if the failure is a flaky test (rerun first to confirm)
- Fix forward in the PR: add a commit that fixes the issue, then merge
- Pin a different version: comment "@dependabot ignore this major version" or "@dependabot reopen with a different range"
Adding to a Fleet (Batch Recipe)
For 35+ repos in a fleet:
import json, base64, subprocess
def get_dependabot_config(repo):
"""Auto-detect which lockfiles exist and return the right config."""
ecosystems = set()
for path in ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"]:
r = subprocess.run(["gh", "api", f"/repos/camster91/{repo}/contents/{path}"], ...)
if r.returncode == 0:
ecosystems.add("npm")
r = subprocess.run(["gh", "api", f"/repos/camster91/{repo}/contents/composer.json"], ...)
if r.returncode == 0:
ecosystems.add("composer")
for pip_file in ["requirements.txt", "pyproject.toml", "Pipfile"]:
r = subprocess.run(["gh", "api", f"/repos/camster91/{repo}/contents/{pip_file}"], ...)
if r.returncode == 0:
ecosystems.add("pip")
break
if not ecosystems:
return None
config = "version: 2\nupdates:\n"
for eco in sorted(ecosystems):
if eco == "npm":
config += ''' - package-ecosystem: "npm"
directory: "/"
schedule: { interval: "weekly", day: "monday" }
open-pull-requests-limit: 5
groups:
production-dependencies: { dependency-type: "production" }
development-dependencies: { dependency-type: "development" }
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
'''
elif eco == "composer":
config += ''' - package-ecosystem: "composer"
directory: "/"
schedule: { interval: "weekly" }
open-pull-requests-limit: 3
'''
elif eco == "pip":
config += ''' - package-ecosystem: "pip"
directory: "/"
schedule: { interval: "weekly" }
open-pull-requests-limit: 3
'''
return config
for repo in fleet_repos:
config = get_dependabot_config(repo)
if not config:
continue
r = subprocess.run(["gh", "api", f"/repos/camster91/{repo}"], ...)
branch = json.loads(r.stdout).get("default_branch", "main")
body = {
"message": "ci: add Dependabot config (auto-detected package managers)",
"content": base64.b64encode(config.encode()).decode(),
"branch": branch,
}
r = subprocess.run(["gh", "api", "-X", "PUT", f"/repos/camster91/{repo}/contents/.github/dependabot.yml", "--input", json.dumps(body)], ...)
applied += 1
This pattern was used to add Dependabot to 35 repos in a fleet in one batch.
Common Pitfalls
- Don't enable
version-update:semver-major ignores — you might want major version bumps eventually, just not right now. Use groups: to batch updates instead.
- Don't set
open-pull-requests-limit too high — 10+ open PRs become unmanageable. Stick to 3-5.
- Don't ignore all Dependabot PRs that fail CI — sometimes the failure indicates a real issue with the new version. At least read the diff.
- Don't enable Dependabot on repos with no lockfiles — it can't do anything useful and creates noise.
- Don't commit the Dependabot config during a billing cap — the CI will run and fail, but the failure will look like a code issue, not a billing issue. Wait for the cap to reset first.
GitHub Actions Minutes Cost
Each Dependabot PR generates a CI run. If you have 35 repos with weekly Dependabot, that's:
- 35 PRs/week × ~3-5 min each = 105-175 min/week
- Plus regular push-triggered runs
GitHub Pro gives 2,000 min/month. A 35-repo fleet with Dependabot enabled is roughly 8-15% of the monthly budget. Within reason, but worth knowing.
Verification
- Config is pushed to
.github/dependabot.yml in the repo
- GitHub's Settings → Code security → Dependabot shows the config is parsed correctly
- Within 24 hours, the first scheduled run creates PRs (if any updates are available)
- Open PRs from Dependabot match the configured
open-pull-requests-limit
Related Skills & Chains
fleet-ci-audit — Use this first to identify which repos in the fleet need Dependabot. The audit's "no Dependabot" category is your work list.
spending-limit-detector — Before adding Dependabot, check the spending limit. Dependabot PRs eat into your monthly Actions minutes.
github-fleet-lint-cleanup — When Dependabot PRs fail CI on lint, the lint-cleanup pattern helps diagnose the root cause.
prisma-fleet-migration — Dependabot often flags Prisma major version bumps. The lazy-proxy migration is the right response.