DEV Community

Cover image for Dependabot was watching 2 of my 6 updatable surfaces, and the config looked complete
Juan Camilo Auriti
Juan Camilo Auriti

Posted on AI-assisted

Dependabot was watching 2 of my 6 updatable surfaces, and the config looked complete

My dependabot.yml, which I'd have described as done:

version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule: { interval: "weekly" }
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule: { interval: "weekly" }
Enter fullscreen mode Exit fullscreen mode

Python dependencies, GitHub Actions. It's a Python project with CI. Looks complete.

I ran an inventory of everything in the repo that a bot could update:

./pyproject.toml
./frontend/package.json
./integrations/astro-geoready/package.json
./Dockerfile
./Dockerfile.web
./action.yml
./.github/actions/geo-audit/action.yml
./.github/workflows/*.yml   (6 files)
Enter fullscreen mode Exit fullscreen mode

Six distinct surfaces. Two covered.

What was missing, and why each was easy to miss

frontend/package.json — npm, not in the config at all. Dependabot is per-directory, and there was no npm entry anywhere. A whole frontend of dependencies, unwatched. This is the one I'd defend least: I just never added it.

integrations/astro-geoready/package.json — a second npm surface. Adding one npm entry for /frontend wouldn't have caught this. Every directory needs its own entry, and this one is a subpackage I forgot existed while writing the config for the one I was thinking about.

Dockerfile and Dockerfile.web — no docker ecosystem entry. Base image tags never get bumped. Both live in the root, so one entry covers them — but zero entries covered either.

.github/actions/geo-audit/action.yml — the interesting one. My github-actions entry with directory: "/" covers .github/workflows/ and the root action.yml. It does not cover a composite action nested in its own subdirectory. That file pins third-party actions, same as any workflow, and it was outside the watched set while sitting inside .github/, which is precisely why I'd assumed it was covered.

The audit command

Run this in any repo and compare the two lists by eye:

# every updatable surface
find . \( -path ./node_modules -o -path ./.venv -o -path ./dist \) -prune -o \
  \( -name 'package.json' -o -name 'requirements*.txt' -o -name 'pyproject.toml' \
     -o -name 'Gemfile' -o -name 'go.mod' -o -name 'Cargo.toml' \
     -o -name 'Dockerfile*' -o -name 'action.yml' -o -name 'action.yaml' \) -print | sort

# what dependabot is told to watch
grep -E 'package-ecosystem|directory' .github/dependabot.yml
Enter fullscreen mode Exit fullscreen mode

Two commands, and the gap is the answer. The -prune matters: without it node_modules buries the signal under thousands of vendored package.json files.

The mapping, so you can read one list against the other:

file found ecosystem directory
pyproject.toml, requirements*.txt pip dir containing it
package.json npm dir containing it
Dockerfile* docker dir containing it
.github/workflows/* github-actions /
nested action.yml github-actions the action's own dir

That last row is the one nobody writes down.

The other half: a skipped PR doesn't come back

While fixing the config I found stale PRs from months earlier and closed a few as superseded — a patch bump that a later minor had already overtaken.

Dependabot does not reopen a PR you closed, and does not re-propose a version you rejected. Closing "the old one" tells it you've decided about that update. If the newer PR isn't actually open, that dependency now sits at its current version indefinitely, with no open PR and no error.

So: close superseded PRs only after confirming the superseding one exists and is open. And if you closed a batch during a cleanup, dependabot.yml won't tell you — the only signal is a dependency that stopped producing PRs, which looks exactly like a dependency that's up to date.

Why it looked complete

Because dependabot.yml describes what you asked for, and nothing anywhere describes what you should have asked for. There's no warning for an unwatched manifest. Adding an ecosystem is opt-in per directory, and the file reads as finished the moment it covers the ecosystems you were thinking about when you wrote it.

The generalisation: an allow-list has no error state. Reviewing it will always tell you it's correct — it lists exactly what it lists. The only way to audit one is from the outside, against the set it's supposed to cover.

Which is the same reason the audit above is two commands instead of one. One command reads the config. The second is the only one that can disagree with it.

Top comments (1)

Collapse
 
dev2023-op profile image
Owen Ross •

very detailed post