Dependabot
-
Six Dependabot PRs, Zero Merged
Dependabot opened six pull requests against my wiki repo over the last two weeks. I merged none of them. I closed all six and replaced them with two hand-assembled PRs, and then I wrote a unit test so it wouldn’t happen a third time.
The scoreboard, in order:
PR Update Outcome #108 @aws-sdk/client-s33.1090.0 to 3.1113.0closed #109 auth1.6.23 to 1.7.1closed #110 typescript6.0.3 to 7.0.2closed #114 all three, by hand merged #115 astroand@astrojs/nodeclosed #116 lucide-react1.17.0 to 1.33.0closed #117 @types/node25.9.1 to 26.2.0closed #119 all three, by hand merged Same shape twice, eight days apart. That’s the part that made me stop and look at the config instead of just closing tabs again.
Why none of them could land alone
Dependabot’s default model is one PR per dependency, and that model quietly assumes your dependencies are independent. Mine weren’t.
The TypeScript bump is the cleanest example. Going from 6 to 7 isn’t a version string change in this repo, because TypeScript 7 doesn’t accept
baseUrl. Mytsconfig.jsonhad it. A PR that changes only the version number fails typecheck immediately, and Dependabot has no way to also delete the config line that made the old version work. It can bump. It can’t migrate.The Better Auth pair was the same problem wearing a different hat: the runtime and the CLI have to move together or you end up with two Better Auth versions resolved in one tree, which then desynchronizes the generated schema and the proof scripts that check it against the live database.
Astro was the third variant. Astro 7 changed the default value of
compressHTMLfromtrueto'jsx', which strips whitespace using JSX rules instead of HTML-aware rules. Whitespace between inline elements that used to survive now collapses. A bare version bump is a rendering change to every page in the wiki, and nothing in the PR would tell you that.Three failures, three different mechanisms, one common cause: an upgrade is a code change, and the bot only sends you the version string.
The config was making it worse
Here’s what my
.github/dependabot.ymlsaid before:ignore: - dependency-name: "*" update-types: ["version-update:semver-patch"] labels: - "dependencies"That
ignoreblock is a common piece of advice, and it’s backwards. Patch releases are the ones you want landing on their own, because they’re where the security fixes go and they almost never break anything. Suppressing them means the only PRs that reach you are the majors and minors, which are exactly the ones that need coordination. I’d configured the bot to send me nothing but the hard cases, one at a time, in whatever order it felt like.The replacement groups everything:
groups: npm-dependencies: applies-to: "version-updates" patterns: - "*" update-types: - "major" - "minor" - "patch" npm-security-updates: applies-to: "security-updates" patterns: - "*"applies-totakes one value, and defaults to version updates when you leave it off, so grouping version updates and security updates means two named groups rather than one clever one. That caught me on the first pass.Then I did the part I’d argue about
I wrote a test that asserts the contents of my Dependabot config. It’s 94 lines of Vitest in
scripts/dependency-policy.test.ts, and it parses YAML files as fixtures:expect(versionGroup).toMatchObject({ 'applies-to': 'version-updates', patterns: ['*'], 'update-types': ['major', 'minor', 'patch'], }); expect(npmUpdate?.ignore ?? []).not.toEqual( expect.arrayContaining([ expect.objectContaining({ 'dependency-name': '*', 'update-types': expect.arrayContaining(['version-update:semver-patch']), }), ]), );That second assertion is the whole point. It doesn’t check that the config is good, it checks that the specific bad config I already shipped once can’t come back. Someone pastes the ignore-all-patches snippet from a blog post, and the suite goes red with a test name explaining why.
Two more assertions came along for the ride. One keeps the Astro major aligned with the Vite major I force through
overrides, since those two are joined at the hip and a bot doesn’t know that. The other is a single line:it('preserves Astro 6 HTML whitespace behavior under Astro 7', () => { expect(readProjectFile('astro.config.mjs')).toContain('compressHTML: true'); });An upstream default changed underneath me, I made a deliberate choice to opt out of the new one, and now that choice is pinned by a test that names the reason. In six months when I’m reading
compressHTML: trueand wondering why it’s set to the value the docs call the old behavior, the test name answers me.Did it work
PR #120 opened this morning: “bump the npm-dependencies group with 10 updates.” One pull request, ten packages, one CI run, one review. Whether it merges is a separate question. But I’m reviewing a coherent upgrade instead of triaging ten pieces of one.
Worth noting what did merge cleanly two weeks ago, before any of this: three bot PRs bumping
happy-dom,biome, andtsx. All dev dependencies, all leaves, nothing else in the tree cares what version they are. Dependabot’s default model works fine for those. It fell over the instant it touched something load-bearing, which is the same instant I actually needed help.Testing your CI config sounds like a joke until you’ve watched a YAML file silently revert twice.
Sources
- Dependabot options reference —
groups,applies-to,patterns,update-types - Optimizing PR creation for version updates — GitHub’s own case for grouping
- Astro 7.0.0 release notes — the
compressHTMLdefault change - Whitespace in Astro 7.0 — what the JSX whitespace rules actually do to your markup
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
- Dependabot options reference —