Skip to content

Make the SDK pin job actually pin SDKs - #25

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/dependabot-ci-workflow-rollout-fbrhdn
Sep 16, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/dependabot-ci-workflow-rollout-fbrhdn

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

What

Replaces the per-repository update-sdks.yml with a shared reusable workflow and a tested script, and changes what the job is for: converging every ktsu.Sdk* reference in a repository on one version, rather than chasing the newest one.

The job has been a no-op in every repository

Not a suspicion — each of these was reproduced against the live NuGet feed with PowerShell 7.5 before writing a line of the replacement:

Defect Effect
[System.Version]::Parse on every published version Throws on 2.28.1-pre.1 (The input string '1-pre' was not in a correct format). The catch reported "Package may not be published to NuGet.org" and returned $null, which the caller read as "no update available". Every lookup, every week, for every package.
-like "ktsu.Sdk.*", and a ktsu\.Sdk\.\w+ csproj pattern "ktsu.Sdk" -like "ktsu.Sdk.*" is False. Both miss the bare ktsu.Sdk, the package nearly every repository pins. VST pins only that one, so the job reported "No ktsu SDKs found" and exited 0 — which is why it is on 2.8.0.
One version recorded per package, first seen wins, package skipped when that one is current A bump that updates some entries and not others leaves the rest behind permanently.
A [\d\.]+ replacement pattern Read 2.0.0 out of 2.0.0-pre.1 and rewrote only that part, producing a version that was never published.
-not $env:FORCE_UPDATE [bool]"false" is $true, so the input never forced anything.
git push origin main Hardcoded a branch name.
ConvertTo-Json -Depth 10 round-trip Reformatted the whole file to change one string.

Latest released ktsu.Sdk is 2.29.0. Four repositories sit on 2.25.0, one on 2.26.1, one on 2.27.0, VST on 2.8.0. That spread is what a weekly green no-op adds up to.

What replaces it

The unit of work is the reference, not the package. A repository whose global.json and project files disagree is repaired even when no newer version exists. That is the property actually worth having here: Dependabot already chases versions and its ktsu group raises them together, but nothing guaranteed a repository ended up on one of them, and a build resolving two versions of the same SDK is not reproducible.

Beyond that: semantic version comparison, so 2.9.0 sorts below 2.28.1; prereleases are never a target but a reference already ahead of the latest release becomes the target rather than being rolled backwards; edits are textual and scoped to the version after the package name, so key order, formatting, unrelated entries and the trailing newline survive, and a prerelease pin is replaced whole.

A lookup that fails now fails the run. Conflating "could not resolve" with "already up to date" is the specific thing that hid this for 35 runs.

Why a script rather than more YAML

250 lines of PowerShell embedded in a workflow can only be tested by merging it and waiting a week, which is the other half of why this went unnoticed. scripts/update-sdks.ps1 runs anywhere, and scripts/tests/update-sdks.tests.ps1 is the defect list above turned into cases, so the file doubles as the record of what was wrong.

The workflow fetches the script from this repository at release rather than checking it out, so it never lands in the workspace that the script scans and git status inspects.

Verification

  • scripts/tests/update-sdks.tests.ps1 — 7/7 pass on pwsh 7.5.4.
  • Live feed, against real fixtures: BlastMerge's global.json (9 entries at 2.25.0) converges to 2.29.0 with MSTest.Sdk, key order and formatting untouched; VST's (bare ktsu.Sdk at 2.8.0, the case that previously found nothing) converges too. The old code produced no change in either.
  • Mixed-state case — one entry at 2.29.0 and one at 2.25.0, the shape a partial group bump leaves — converges. The old code skipped the package entirely.
  • [System.Management.Automation.SemanticVersion] parses all 172 published ktsu.Sdk versions, orders 2.9.0 < 2.28.1 and 2.28.1-pre.1 < 2.28.1.
  • actionlint clean on the shared workflow and on the caller template; the template is byte-identical to the one in the docs, checked programmatically.
  • markdownlint clean on docs/sdk-pinning.md and docs/shared-ci.md; CLAUDE.md still reports its same 12 pre-existing findings.

Not in this PR

Rolling the caller out to the 37 repositories that have an update-sdks.yml, and adding it to the ones that should. That wave goes with the ci.yml fan-out.

🤖 Generated with Claude Code

https://claude.ai/code/session_014RABe2NufFc9hwm94iB3Rf


Generated by Claude Code

`update-sdks.yml` has been a silent no-op in every repository for as long as the
feed has carried a prerelease. Each defect below was confirmed against the live
NuGet feed rather than read off the source:

- `[System.Version]::Parse` throws on `2.28.1-pre.1`, and the `catch` reported
  "may not be published to NuGet.org" and returned null, which the caller read
  as "no update available". Every lookup, every week, for every package.
- `-like "ktsu.Sdk.*"` is False for `ktsu.Sdk`, and the csproj pattern required
  a suffix too, so the package nearly every repository pins was invisible. VST
  pins only that one, reported "No ktsu SDKs found", and sits on 2.8.0.
- One version was recorded per package, first seen wins, and the package was
  skipped when that one was already current. A dependency bump that updates some
  entries and not others therefore left the rest behind permanently.
- The `[\d\.]+` replacement read `2.0.0` out of `2.0.0-pre.1` and rewrote only
  that part, producing a version that had never been published.
- `[bool]"false"` is `$true`, so `force_update` never forced anything.
- `git push origin main` hardcoded a branch name.

The replacement makes the reference the unit of work rather than the package, so
a repository whose global.json and project files disagree is repaired even when
no newer version exists. That is the property worth having: Dependabot already
chases versions, but nothing guaranteed a repository ended up on one of them,
and a build resolving two versions of the same SDK is not reproducible.

The logic moves to scripts/update-sdks.ps1 so it can be run and tested outside
Actions, which is the other half of why this went unnoticed for so long. Its
tests are the defects above, each as a case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RABe2NufFc9hwm94iB3Rf
@matt-edmondson
matt-edmondson merged commit a5c2706 into main Sep 16, 2026
3 checks passed
@matt-edmondson
matt-edmondson deleted the claude/dependabot-ci-workflow-rollout-fbrhdn branch October 6, 2026 00:03
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