ci: make the NuGet lock-file refresh a one-command chore - #86
Merged
Merged
Conversation
Dependabot refreshes packages.lock.json only for the project whose .csproj it edited. Api and Tests reference Application/Infrastructure transitively, so any backend bump leaves their lock files stale and ci.yml's restore --locked-mode fails with NU1004 -- which is why five NuGet PRs sat red for weeks. Rather than weaken --locked-mode (it is deliberate supply-chain protection), make the refresh trivial and rare: - dependabot.yml groups all NuGet updates into one weekly PR (majors separately), so the refresh is a weekly chore instead of a per-package one. - scripts/refresh-lockfiles.ps1 runs the solution-wide --force-evaluate restore, using a local SDK when present and the pinned SDK container otherwise, and refuses to leave behind changes to anything but the lock files. - refresh-lockfiles.yml does the same in CI for a given PR number, for when no local toolchain is available. Note: Central Package Management would not have helped -- NuGet still writes one lock file per project and Dependabot still refreshes only what it edited.
This was referenced Aug 31, 2026
thomas-lg
added a commit
that referenced
this pull request
Sep 13, 2026
Dependabot refreshes packages.lock.json only for the project whose .csproj it edited. Api and Tests reference Application/Infrastructure transitively, so any backend bump leaves their lock files stale and ci.yml's restore --locked-mode fails with NU1004 -- which is why five NuGet PRs sat red for weeks. Rather than weaken --locked-mode (it is deliberate supply-chain protection), make the refresh trivial and rare: - dependabot.yml groups all NuGet updates into one weekly PR (majors separately), so the refresh is a weekly chore instead of a per-package one. - scripts/refresh-lockfiles.ps1 runs the solution-wide --force-evaluate restore, using a local SDK when present and the pinned SDK container otherwise, and refuses to leave behind changes to anything but the lock files. - refresh-lockfiles.yml does the same in CI for a given PR number, for when no local toolchain is available. Note: Central Package Management would not have helped -- NuGet still writes one lock file per project and Dependabot still refreshes only what it edited.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Five of the sixteen Dependabot PRs open this morning were red for the same non-reason:
backend/Directory.Build.propsenables NuGet lock files andci.ymlrestores with--locked-mode. NuGet writes one lock file per project, so a bump inApplicationorInfrastructurealso invalidatesApiandTests, which reference them transitively — but Dependabot only refreshes the lock file of the project whose.csprojit edited. Every backend bump therefore arrives broken and needs a manual--force-evaluatepass.Weakening
--locked-modewould make the symptom go away, but that gate is deliberate supply-chain protection: a dependency bump must not land without a reviewed lock file. So this makes the chore trivial and rare instead.What
dependabot.yml— all NuGet updates group into one weekly PR (patterns: ["*"]), with majors in their own group so they stay reviewable on their own merits. One refresh per week instead of one per package.scripts/refresh-lockfiles.ps1— the solution-wide--force-evaluaterestore in one command. Uses a local .NET SDK when there is one, the SDK container pinned to the same digest as the Dockerfile otherwise, so it works on a host with no SDK.-Checkreproduces CI's--locked-moderestore. It compares content hashes before/after and refuses to leave behind changes to anything but the lock files — a changed.csprojmeans the restore resolved something unintended.refresh-lockfiles.yml— the same job in CI:gh workflow run refresh-lockfiles.yml -f pr=<number>.README.md— a short section so the next NU1004 doesn't need re-diagnosing.Two constraints are encoded in the workflow's header comment because they're easy to trip over: Dependabot-triggered runs get a read-only
GITHUB_TOKENand no secrets (so this can't be an automaticpull_requestfix-up), and aGITHUB_TOKENpush raises no workflow events (so the last step re-triggers the required checks explicitly).Considered and rejected
Central Package Management. It would shrink the
.csprojdiffs, but it does not fix NU1004 — NuGet still writes one lock file per project and Dependabot still refreshes only the one it edited. Not worth a migration on a false premise.Verified
refresh-lockfiles.ps1 -Checkpasses on a clean tree; the default mode reportsLock files were already up to date; nothing to commit.(The first version of the guard wrongly flagged unrelated working-tree edits — caught by running it, fixed by hash-comparing only what the restore touched.)