Quieten + harden the dependency pipeline: semiannual grouped updates, cooldown, SCA gate (#224)#226
Conversation
Records the decision to keep the human-gated Dependabot flow and harden around it — lock files + CI-only locked-mode restore, a 14-day cooldown, and a hard-failing vulnerable-dependency CI gate — rather than switching to manual dependency bumps. Implementation tracked in #224. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#224) Three supply-chain assurances around the existing human merge gate: 1. Lock files + CI-only locked-mode restore. RestorePackagesWithLockFile commits a packages.lock.json per src/ project; RestoreLockedMode gated to CI=true fails restore (NU1004) if the resolved graph drifts from the lock, covering implicit publish/pack restores too. Local restores stay unlocked. Freezes the floating Roslynator [4.15.0, ) range as a side effect. 2. 14-day Dependabot cooldown so a same-day compromised publish ages out before a bump PR is opened. 3. Hard-failing SCA step in the PR build: dotnet list package --vulnerable --include-transitive, which exits 0 on findings, so the step greps its output and exits non-zero on any advisory. docs/RELEASING.md documents the release-runner locked-mode restore contract. The human review gate is unchanged: Dependabot still opens maintainer-assigned PRs and nothing auto-merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Locked-mode restore proved infeasible without pinning the SDK: IsAotCompatible pulls in the SDK-implicit Microsoft.NET.ILLink.Tasks, whose content hash is specific to the .NET SDK build. With global.json on rollForward: latestFeature, CI floats to a newer SDK feature band (10.0.301) than the lock was generated on (10.0.109), so locked-mode restore fails NU1403 on a legitimate tree. Pinning the SDK band across dev and CI to make lock files viable is an ongoing toolchain-coupling cost judged disproportionate for this project. Reverts the packages.lock.json files, the RestorePackagesWithLockFile / RestoreLockedMode properties, and the RELEASING.md locked-mode section. The 14-day Dependabot cooldown and the hard-failing SCA gate remain. CIR updated to record the attempt and why it was dropped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude Review this pull request. Analyse the code changes and provide feedback covering:
|
|
Claude finished @FrankRay78's task in 1m 54s —— View job Review: Harden dependency pipeline (#226)
A small, well-scoped PR — 40 lines across two config files plus a CIR. Overall this is solid, low-risk hardening. No bugs found; a couple of minor robustness points worth considering. Bugs / correctnessNothing incorrect. Verified:
SecurityThis is the point of the PR, and it's a genuine improvement:
One fragility worth flagging (not a blocker): the gate keys off matching the literal string CLAUDE.md / constitution adherence
Test coverageNo unit tests added, and none are needed — these are CI/YAML config changes with no C# surface, consistent with "Don't test... third-party library behaviour" and the general principle that TDD applies to production code, not pipeline config. The author's local verification (real CI run against a clean tree + a manual check against a known-vulnerable package) is the appropriate substitute here and is documented in the PR body. Spec/test-plan mismatchesNone found. No SummaryNo blocking issues. The one non-blocking suggestion: note the marker-string's brittleness against future |
Routine version updates were monthly and one-PR-per-package — maintenance noise disproportionate to this project. Now they run twice a year (semiannually) and collapse into a single grouped PR. Prompt patching of known advisories is carried by Dependabot security updates (repo-settings, advisory-driven, unaffected by this schedule or the cooldown) plus the in-CI SCA gate, so reducing routine cadence does not weaken the vulnerability path. Cooldown stays: it stops the scheduled run from grabbing a same-day publish. CIR updated to record the cadence decision. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude Please re-review, identify only showstoppers or important issues (P1/P2). |
|
Claude finished @FrankRay78's task in 3m 28s —— View job Re-review: P1/P2 only (#226)
Per request, this pass only reports showstopper/important issues — the rest of the first review (CIR quality, cooldown nesting, grouping syntax, gate script mechanics) still stands and isn't repeated here. P1 —
|
dotnet list package --format json is stable since the .NET 9 SDK (confirmed on the 10.0.x SDK this workflow pins). Switch the vulnerable-dependency check to parse the JSON — failing on any package with a non-empty vulnerabilities array — rather than grepping the sentence "has the following vulnerable packages", which is English prose that could be localised or reworded between SDK versions. On a finding the step now prints the offending package, version, severity, and advisory URL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude Please re-review, identify only showstoppers or important issues (P1/P2). |
|
Claude finished @FrankRay78's task in 1m 53s —— View job Re-review #2: P1/P2 only (#226)
Per request, this pass only reports showstopper/important issues. P2 from last pass — RESOLVED
This removes the locale/wording-fragility concern entirely. No longer a P2. P1 from last pass — still open, unresolved
I still cannot reach GitHub's docs from this sandbox (no network egress) to confirm definitively, so treat this as a strong flag rather than a proven failure. To the best of my knowledge, GitHub's documented If unsupported, the risk is exactly what the first pass described: the routine-update channel could silently never fire, which is strictly worse than the "too noisy" status quo this PR sets out to fix, since Dependabot security updates (the actual CVE-response channel) are unaffected either way, but routine bug/feature-fix updates would stop entirely without anyone noticing. Recommend before merge: confirm Everything elseNo new P1/P2 issues found. Cooldown nesting, groups syntax, and CIR reasoning still check out as covered in the first review pass. SummaryOne item remains before merge: verify |
Closes #224.
Reshapes NetPace's dependency-update pipeline to be both quiet (routine bumps stop being noise) and hardened (a bad version is caught), while keeping the human merge gate — Dependabot still opens maintainer-assigned PRs and nothing auto-merges.
Two independent channels
semiannually), all bumps collapsed into one grouped PR, and never a version younger than 14 days.github/dependabot.ymlBackstop: a hard-failing SCA gate in the PR build (
.github/workflows/dotnet.yml) runsdotnet list package --vulnerable --include-transitiveand fails the job on any advisory (the command exits 0 on findings, so the step inspects its output and exits non-zero itself).Net effect: routine maintenance drops from ~monthly per-package chore PRs to one batched PR every six months, while anything with a real advisory still gets patched promptly and the CI gate stops a vulnerable dependency merging unnoticed.
Changes
.github/dependabot.yml— routine cadencemonthly → semiannually; add agroups:block bundling all bumps into one PR; keep the 14-daycooldown(stops the scheduled run grabbing a same-day publish)..github/workflows/dotnet.yml— the SCA gate described above.The CIR (
docs/change-intent-records/2026-07-13-dependency-supply-chain-hardening.md) records the full reasoning, including the cadence decision.Scope change during implementation: lock files dropped
The original issue scoped a third change — committed
packages.lock.jsonfiles with--locked-moderestore on CI. It was implemented, then dropped after it failed CI on a legitimate tree (NU1403):IsAotCompatible=truepulls in the SDK-implicitMicrosoft.NET.ILLink.Tasks, whose content hash is specific to the .NET SDK build, andglobal.json'srollForward: latestFeaturelets CI float to a newer SDK band (10.0.301) than the lock was generated on (10.0.109). Making lock files viable would require pinning the SDK band across dev and CI — an ongoing toolchain-coupling cost judged disproportionate here. The CIR records the attempt and rationale (decision 5).Verification (local)
dotnet build srcanddotnet test srcpass with zero warnings; 616 tests, 0 skipped.dependabot.ymlvalidated as well-formed;interval: semiannually, oneall-dependenciesgroup,cooldown.default-days: 14.has the following vulnerable packages) confirmed against a known-vulnerable package (System.Net.Http 4.3.0→ GHSA-7jgj-8wvc-jh57); clean tree reports none.Required post-merge maintainer step (cannot be automated in-repo)
Enable Dependabot security alerts and security updates in repo Settings → Code security. This is the linchpin of the new posture — it's what lets routine updates go infrequent safely, because CVE patches then arrive on their own prompt channel independent of the semiannual schedule.
@claude please review.
🤖 Generated with Claude Code