Skip to content

build(deps): group Dependabot updates so related bumps arrive together - #126

Merged
bgard68 merged 1 commit into
mainfrom
deps/group-dependabot-updates
Aug 17, 2026
Merged

build(deps): group Dependabot updates so related bumps arrive together#126
bgard68 merged 1 commit into
mainfrom
deps/group-dependabot-updates

Conversation

@bgard68

@bgard68 bgard68 commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Stops today's two structural failures recurring. Both produced pull requests that were born unmergeable — red on creation, with no rebase or re-run that could ever clear them.

What ungrouped updates cost today

codeql-action — 8 pull requests, all permanently red. init, analyze and upload-sarif must run the same version, or the job fails:

Loaded a configuration file for version '4.37.6', but running version '4.37.4'

Dependabot opens one pull request per sub-action, so each one creates that mismatch on its own branch. None could go green alone; whichever merged first would break CI for the others. They were closed and replaced by three hand-written commits (#118, #119, #122) that moved the sub-actions together — the only shape that works.

NuGet lock files — 4 more. One bump changes every packages.lock.json that resolves it transitively:

src/TodoApp.Application/packages.lock.json           80 ++--
src/TodoApp.Infrastructure/packages.lock.json       176 ++--
src/TodoApp.WebApi/packages.lock.json               142 ++--
tests/TodoApp.IntegrationTests/packages.lock.json   192 ++--
tests/TodoApp.UnitTests/packages.lock.json          266 ++--

Dependabot regenerates only the first, so dotnet restore --locked-mode fails with NU1004. Replaced by #120 and #121.

The change

ecosystem target group update types
github-actions (default) github-actions all
nuget (default) nuget minor, patch
github-actions dapper github-actions all
nuget dapper nuget minor, patch
npm frontend npm minor, patch
github-actions frontend github-actions all

Actions group across all update types, because the sub-action version constraint holds whether the bump is major, minor or patch. NuGet and npm group minor and patch only, so a major still arrives on its own and gets read properly — consistent with the existing ignore blocks for the licence-change majors.

What this does not fix

Grouping does not fully solve the NuGet case. A grouped bump can still leave a dependent project's lock file stale, needing:

dotnet restore TodoApp.sln --force-evaluate

and a commit of the regenerated files. What changes is that it's one pull request to fix rather than five, and the group lands consistent rather than half-applied. Saying so here rather than overselling it.

The ignore: blocks are untouched — grouping and ignoring are independent, and an ignored major stays ignored inside a group. Verified after the edit: all three blocks still parse with their original contents.

Separate finding, not fixed here

The copies of this file on dapper and frontend have drifted from main's, despite frontend's copy stating it is "kept byte-identical to main's so the two cannot contradict each other":

  • main has the ignore: blocks; dapper and frontend do not
  • frontend has the NOTE header explaining it is inert; main does not

No behavioural impact — Dependabot only reads the default branch, so main's copy is the live one and its ignore rules are in force. But the byte-identical claim is currently false, and the inert copies are stale. Worth a follow-up to either sync them or delete them; I have not touched them here since this pull request targets main only.

🤖 Generated with Claude Code

Ungrouped, two classes of update are born unmergeable, and both cost a day on
2026-08-17.

codeql-action's init, analyze and upload-sarif must run the same version or the
job fails with "Loaded a configuration file for version X, but running version
Y". Dependabot opens one pull request per sub-action, so each one creates that
mismatch on its own branch: none can go green alone, and no rebase helps, because
nothing about the base branch is wrong. Eight such pull requests were open across
three branches, all permanently red, all eventually closed and replaced by three
hand-written commits that moved the sub-actions together.

A NuGet bump changes every packages.lock.json that resolves it transitively -
five of them for one Microsoft.EntityFrameworkCore bump. Dependabot regenerates
only the lock file belonging to the project it edited, so restore fails with
NU1004. Four more pull requests, same fate.

Actions are grouped across all update types, because the sub-action constraint
holds whether the bump is major, minor or patch. NuGet and npm group minor and
patch only, so a major still arrives alone and gets read properly - which is what
the existing ignore blocks are already protecting against for the licence-change
majors.

Grouping does not fully solve the NuGet case: a bump can still leave a dependent
project's lock file stale, needing `dotnet restore TodoApp.sln --force-evaluate`
and a commit of the regenerated files. What it changes is that this is one pull
request to fix rather than five, and the group lands consistent rather than
half-applied.

The ignore blocks are unchanged - grouping and ignoring are independent, and an
ignored major stays ignored inside a group.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bgard68
bgard68 merged commit f56aa4e into main Aug 17, 2026
10 checks passed
bgard68 added a commit that referenced this pull request Aug 17, 2026
…onTests (dapper) (#129)

The two test projects on this branch had drifted apart:

  Microsoft.NET.Test.Sdk         18.9.0 (Integration) vs 18.8.1 (Unit)
  xunit.runner.visualstudio       4.0.0 (Integration) vs  3.1.5 (Unit)

Main has both projects on 18.9.0 and 4.0.0. This is residue from the
pre-grouping era: #114 and #115 came off
dependabot/nuget/tests/TodoApp.IntegrationTests/dapper branches and edited only
the project each was named for, leaving UnitTests behind.

The grouped sweep introduced by #126 would have caught this up - it reads
dependabot.yml from the default branch, so the dapper target-branch entries
apply here too. That sweep ran on 2026-08-17 as run 32038120956 and died in
setup, unable to download github/dependabot-action through a 429 during the
GitHub partial outage that afternoon. Dependabot's own update jobs are not
retryable ("This workflow run cannot be retried"), so the sweep never produced a
pull request and the drift stayed put. Hence this by hand.

Nothing was broken by the split: the two test projects do not reference each
other, so there is no NU1605 downgrade to trip on and dapper's CI was green
throughout. The cost was quieter - unit tests were running on a test runner a
major version behind the integration tests.

Only UnitTests' lock file changes. `dotnet restore --force-evaluate` rewrites
all six on Windows, but the other five differ by line endings alone and were
reverted rather than committed as churn.

Verified locally on the 10.0.100 SDK, pinned via a temporary global.json because
CI floats on 10.x and the machine defaults to a 10.0.400 preview: locked-mode
restore passes with no NU1004, Release build is clean under
TreatWarningsAsErrors, and all 98 tests pass - including the 65 unit tests now
discovered and run by the 4.0.0 runner, which is the only real risk in a major
runner bump.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
bgard68 added a commit that referenced this pull request Aug 17, 2026
… PRs (#130)

Dependabot regenerates packages.lock.json only for the projects whose .csproj
it edits; every project consuming the bumped package transitively keeps its old
lock file, and locked-mode restore fails the solution with NU1004. Grouping
(#126) turned five such pull requests into one - this closes the loop on that
one. The failure is not hypothetical: it broke three workflows across two
branches on 2026-08-17 alone, and the fix each time was the same mechanical
`dotnet restore --force-evaluate` done by hand.

dependabot-lockfix.yml (default branch only, by workflow_run's rules) fires on
any failed API/CodeQL/container run of a dependabot/nuget/** branch, regenerates
the lock files, pushes the fix, and re-dispatches every required check on the
fixed commit. The three GitHub constraints that dictate the shape are documented
in the file: dependabot-actor runs get read-only tokens (hence workflow_run),
GITHUB_TOKEN pushes trigger no workflows (hence explicit re-dispatch), and
workflow_dispatch is the documented exception that makes the re-dispatch land.

api-ci-cd.yml gains a `deploy` input (default true) and the deploy gate becomes

  github.ref == 'refs/heads/main' ||
    (github.event_name == 'workflow_dispatch' && inputs.deploy)

Until now ANY manual dispatch deployed, so an automated checks-only rerun of a
Dependabot branch would have shipped that branch to the App Service. Default
true keeps the existing operator flow: Run workflow on main with the box ticked
deploys exactly as before. Dependabot branches forked before this commit reject
a deploy=false dispatch with 422 Unexpected inputs - a loud no-op rather than a
deploy, gone as soon as branches re-fork off the updated base.

Verified: both files parse; the gate expression is push-safe (inputs is empty
on push events, so main still auto-deploys via the first clause). The full loop
gets its first live test on the next grouped nuget PR; until then the workflow
is also manually dispatchable against any branch.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@bgard68
bgard68 deleted the deps/group-dependabot-updates branch August 17, 2026 19:08
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.

1 participant