Skip to content

chore(deps): keep major bumps out of dependabot's grouped PRs (#871) - #872

Open
isadeks wants to merge 1 commit into
mainfrom
chore/871-dependabot-group-split
Open

chore(deps): keep major bumps out of dependabot's grouped PRs (#871)#872
isadeks wants to merge 1 commit into
mainfrom
chore/871-dependabot-group-split

Conversation

@isadeks

@isadeks isadeks commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #871.

Rewritten. The first version of this PR routed majors into a second group per ecosystem, which raised the open-PR ceiling from 4 to 8. That drew the reasonable objection that it trades one problem for PR sprawl. This version excludes majors instead, so the steady state is one grouped PR per ecosystem — the same count as today, but without the wedge. Groups are now left exactly as they are on main; the diff is four limit bumps plus ignore rules.

Problem

Every ecosystem groups all updates into one PR and allows one open PR:

open-pull-requests-limit: 1
groups:
  all-npm:
    patterns: ["*"]

With update-types unset the group takes majors, minors and patches together. A breaking major then freezes the whole ecosystem — the group PR cannot merge, and the limit of 1 stops Dependabot opening a second PR for the safe remainder.

This is not a theoretical cost. #860 carries astro 7.1.37.2.9, which fixes GHSA-26w7-cxv4-gfx2 (CVSS 9.8), and it has been unmergeable since 2026-09-05 — blocked by typescript ^6.0.3^7.0.2 and cdk-nag ^2.38.2^3.0.2 in the same PR. #870 exists to hand-write that astro bump plus six other advisory fixes: work Dependabot had already computed and delivered, in a PR nobody can merge. #859 is the same shape, holding 10 python updates behind mcp 1.28.12.1.1.

Why exclude majors rather than group them separately

This is the part worth checking, because the obvious fix is wrong. A group restricted to update-types: ["minor", "patch"] leaves majors ungrouped — and an ungrouped update gets its own PR. So restricting the group without also ignoring majors would produce one PR per major dependency, which is strictly worse than today. Ignoring them is what holds the queue at one PR per ecosystem.

Change

  • Ignore version-update:semver-major for * in all four ecosystems. Groups are untouched from main.
  • open-pull-requests-limit 1 → 2. One grouped PR plus one slot of headroom, so a lone ungrouped or security PR cannot starve the group. With a single group per ecosystem, Dependabot still opens at most one grouped PR — the limit is a ceiling, not a target.
  • Ignore python minor on the docker ecosystem (chore(agent): upgrade Python 3.13 → 3.14 #105). 3.133.14 has to land with agent/mise.toml, agent/.python_version, the ruff target-version and ty python-version targets in agent/pyproject.toml, the lockfile, and three prose sites that name the base tag. chore(deps): docker: bump the all-docker group across 1 directory with 3 updates #827 moved only the Dockerfile. Digest and patch refreshes still flow.
  • Ignore claude-agent-sdk, mirroring the existing cedarpy treatment. It bundles the claude CLI, which is pinned separately by an npm install -g @anthropic-ai/claude-code@<version> line in agent/Dockerfile. That line is in no package.json — verified with grep -rn "anthropic-ai/claude-code" --include=package.json, which returns nothing — so no ecosystem tracks it and only a human can move it. An automated SDK bump therefore diverges the bundled CLI from the installed one and stales the version named in agent/pyproject.toml:19's own comment, with no CI check able to notice. chore(deps): uv: bump the all-python group across 1 directory with 10 updates #859 does exactly this: SDK to 0.2.148, Dockerfile untouched.
  • State semver-major explicitly on each per-dependency ignore even though the blanket rule covers it. Deliberate overlap: the blanket rule is a policy and may be revisited, whereas each per-dependency block is an invariant — a lockstep partner that must not move alone. Stating them fully means relaxing the policy cannot silently degrade an invariant.

Trade-off, stated plainly

A major that fixes an advisory will no longer arrive as a PR. Detection is unaffected: //:security:deps runs osv-scanner over yarn.lock, agent/uv.lock and the jira-forge-app lockfile and fails the build on any advisory regardless of Dependabot. But the upgrade has to be driven by hand.

That is the trade for not having a breaking major freeze the ecosystem it lands in. Today's config does not avoid the work — it just hides it behind a PR that cannot merge, while still costing a weekly rebase and a 13-minute CI run. Majors become issues instead; #105 already exists for the Python interpreter, and TypeScript 7, cdk-nag 3 and mcp 2 need filing.

Testing

  • YAML parses. Asserted programmatically that each ecosystem has exactly one group, that no group restricts update-types (which would leave majors ungrouped and produce per-dependency PRs — the failure mode above), that every ecosystem carries the blanket major ignore, and that each per-dependency ignore states major explicitly.
  • mise run security:gh-actions (zizmor over .github) gives identical output on this branch and on clean main — 53 findings, 6 ignored, 46 suppressed, 1 medium. The medium is secrets-outside-env at .github/workflows/auto-approve.yml:48, untouched here; it is pre-existing and a local-only artefact of a newer zizmor than CI's pin. Zero findings added.
  • No mise task or workflow reads .github/dependabot.yml (grep -rn dependabot mise.toml .github/workflows/ scripts/ is empty), so GitHub's own validator on the Dependabot page is the authoritative check after merge.
  • Rebased onto 1c296c0b (current main, which includes fix(deps): clear 7 osv advisories blocking the merge queue #870 and chore(deps): docker: bump mise digest and golang 1.26.4 to 1.27.1 #873). Diff against main is one file, .github/dependabot.yml — confirmed with git diff upstream/main --stat.

A note on the earlier red CI here, since the history is public: an intermediate push of this branch reverted #870. I had used git reset --soft to re-parent onto the newer main, which keeps the old worktree, so the commit captured a tree predating #870 and rolled astro back from 7.2.8 to 7.1.3. //:security:deps correctly failed on the seven advisories that reappeared, and I initially misread that as advisories inherited from main. It was self-inflicted. The commit now contains only .github/dependabot.yml, and astro is 7.2.8 at this head with e96dc21e (#870) verified as an ancestor.

Follow-up

Once this merges, closing #859 and #860 lets Dependabot regenerate a single grouped PR per ecosystem carrying only the routine updates — recovering the 45 npm and 6 python bumps currently held behind majors. Deliberately sequenced that way: with the limit still 1 and no major ignore in place, closing them today just returns the same frozen groups on the next Saturday run.

@isadeks
isadeks requested a review from a team as a code owner September 9, 2026 19:37
@isadeks
isadeks force-pushed the chore/871-dependabot-group-split branch from fef993a to 51ed4a0 Compare September 10, 2026 13:29
@isadeks
isadeks requested review from a team and backgroundagents as code owners September 10, 2026 13:29
@isadeks
isadeks force-pushed the chore/871-dependabot-group-split branch from 51ed4a0 to 1ba93cc Compare September 10, 2026 13:30
@isadeks isadeks changed the title chore(deps): split dependabot majors from minor/patch groups (#871) chore(deps): keep major bumps out of dependabot's grouped PRs (#871) Sep 10, 2026
Each ecosystem groups every update into one PR with a PR limit of 1, so a
breaking major freezes that ecosystem outright: the group PR cannot merge and
the limit stops a second PR being opened for the safe remainder. #860 has held
47 npm updates since 2026-09-05 behind typescript 6->7 and cdk-nag 2->3, and
#859 has held 10 python updates behind mcp 1->2.

The cost was concrete: #860 carries astro 7.1.3 -> 7.2.9, which fixes
GHSA-26w7-cxv4-gfx2 (CVSS 9.8), and it sat unmergeable for five days. #870 had
to hand-write that same bump plus six other advisory fixes, duplicating work
dependabot had already done.

Exclude majors from the grouped PR instead of routing them to a second group.
A group restricted to minor/patch would leave majors *ungrouped*, and an
ungrouped update gets its own PR — that would mean one PR per major dependency,
strictly worse than today. Ignoring them keeps the steady state at one grouped
PR per ecosystem, the same count as now but without the wedge.

- Ignore `version-update:semver-major` for `*` in all four ecosystems. Groups
  are left exactly as they are on main.
- Raise `open-pull-requests-limit` 1 -> 2: one grouped PR plus a slot of
  headroom, so a lone ungrouped or security PR cannot starve the group.
- Ignore `python` minor on docker (#105). 3.13 -> 3.14 must land with
  agent/mise.toml, agent/.python_version, the ruff and ty targets in
  agent/pyproject.toml, the lockfile, and three prose sites naming the base
  tag. #827 moved only the Dockerfile. Digest and patch refreshes still flow.
- Ignore `claude-agent-sdk`, mirroring cedarpy. It bundles the `claude` CLI,
  pinned separately by an `npm install -g` line in agent/Dockerfile that is in
  no package.json — so no ecosystem tracks it and only a human can move it. An
  automated SDK bump diverges bundled from installed CLI and stales the version
  in pyproject's own comment (#215).
- State `semver-major` explicitly on each per-dependency ignore even though the
  blanket rule covers it. The blanket rule is a policy that may be revisited;
  each per-dependency block is an invariant. Relaxing the policy must not
  silently degrade one.

Trade-off: a major that fixes an advisory no longer arrives as a PR. Detection
is unaffected — `//:security:deps` fails the build on any advisory regardless
of dependabot — but the upgrade is driven by hand. Majors become issues.
@isadeks
isadeks force-pushed the chore/871-dependabot-group-split branch from 1ba93cc to 8ebb6e5 Compare September 10, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(deps): dependabot catch-all groups let one breaking major block every other update in the ecosystem

1 participant