Skip to content

Add automated per-plugin versioning (NBGV) with /version-bump + weekly backstop#813

Open
AbhitejJohn wants to merge 6 commits into
mainfrom
abhitejjohn/per-plugin-version-strategy
Open

Add automated per-plugin versioning (NBGV) with /version-bump + weekly backstop#813
AbhitejJohn wants to merge 6 commits into
mainfrom
abhitejjohn/per-plugin-version-strategy

Conversation

@AbhitejJohn

@AbhitejJohn AbhitejJohn commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Draft for review. Opens the versioning strategy we discussed: per‑plugin semantic versions, materialized into the checked‑in manifests, with two low‑touch automations and no auto‑editing of contributor PRs.

Why

Every client that surfaces these skills — Copilot CLI, Claude Code, Codex, Cursor — reads a plugin's version directly from its checked‑in manifest (plugin.json / .codex-plugin/plugin.json). Today those versions are static, so:

  • A plugin's behavior can change while its advertised version stays flat, and consumers never get a signal to re‑pull.
  • There's no human‑readable record of what changed when.
  • Bumping by hand across 15 plugins is error‑prone and easy to forget.

We want correct, current versions in the repo with minimal manual work, without bloating the marketplace clone and without taking on third‑party/external plugins (out of scope for now).

What this adds

  • Per‑plugin SemVer via Nerdbank.GitVersioning (NBGV). Each plugin gets a version.json whose pathFilters exclude the generated manifests and version.json itself, so the computed version height tracks real content changes only (editing a manifest or the version file doesn't inflate the height).
  • Versions are materialized into the checked‑in manifests, so consumers read a current value with no build step on their side.
  • One workhorse scripteng/version/Sync-PluginVersions.ps1 — resolves the changed‑plugin set from a git diff, computes each version with nbgv (predicting the squash‑merge height for PRs), and either reports or stamps.

Automations (two, low‑touch by design)

Workflow Trigger What it does
/version-bump A maintainer comments /version-bump on a PR Stamps the affected plugins on the PR branch. Gated on collaborator permission (admin/write/maintain); forks rejected before any privileged step.
weekly-version-sync Mondays 09:00 UTC + workflow_dispatch Backstop: stamps any drift on main, opens/updates a single bot PR, and explains the per‑plugin reason. Self‑heals anything merged without a bump.

We deliberately did not auto‑edit contributor PRs or add a noisy advisory‑comment bot — maintainers stay in control and the signal stays clean. (No official skills repo we surveyed auto‑mutates contributor PRs for versioning; the opt‑in command + scheduled backstop pattern is the conservative norm.)

Security — multi‑model adversarial review (GPT‑5.5 + Gemini 3.1 Pro)

  • Supply‑chain RCE (High — flagged independently by both models). dotnet tool restore would have honored a nuget.config authored in the PR tree, letting an attacker remap the nbgv package source to a malicious feed and execute code in the privileged contents: write context. This was genuinely reachable — no nuget.config is tracked anywhere in the repo today, so a PR could add one. Fix: a trusted eng/version/nuget.config (<clear/> + nuget.org‑only + packageSourceMapping), overlaid from main and consumed via --configfile so PR‑supplied configs are ignored entirely.
  • TOCTOU (Medium). /version-bump now checks out the authorized head SHA rather than the mutable branch name; if the branch advances after authorization, the push fails non‑fast‑forward — the safe outcome.
  • Injection hardening. Set-ManifestVersion uses a MatchEvaluator (not a replacement string), so a $‑bearing version can't re‑expand, plus a strict ^\d+\.\d+\.\d+$ guard that throws on a malformed base and leaves manifests untouched.
  • Correctness. A base‑only version.json bump (0.1 → 0.2) is now detected and stamped.

Verification

Exercised end‑to‑end against a real NBGV git harness (the repo global.json pins an SDK that isn't installable locally, so testing uses an isolated harness):

  • content‑scoped predict → only the touched plugin bumps;
  • base‑only bump → x.y.0;
  • docs‑only change → [] (no bump);
  • weekly -OnlyChanged → stamps drift authoritatively;
  • malformed base → throws, manifest intact;
  • dotnet tool restore --configfile → exit 0.

actionlint passes on both workflows.

Files

  • eng/version/Sync-PluginVersions.ps1, eng/version/nuget.config, .config/dotnet-tools.json
  • .github/workflows/version-bump-command.yml, .github/workflows/weekly-version-sync.yml
  • plugins/*/version.json (×15), CONTRIBUTING.md

Notes for reviewers

  • The issue_comment/scheduled workflows run the YAML from main, so /version-bump and the weekly job only take effect once this is merged — expected, not a bootstrap bug.
  • Open question for discussion: cadence/branch‑protection interplay for the weekly bot PR.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

…y backstop

WHY
Tools that surface skills (Copilot CLI, Claude Code, Codex, Cursor) read a
plugin's version directly from its checked-in manifest. With no versioning
discipline, a plugin's behavior can change while its advertised version stays
flat, so clients never learn to re-pull, and there is no human-readable signal
of what changed. We want correct, current versions in the repo with minimal
manual work and without bloating the marketplace clone.

WHAT
- Per-plugin semantic versioning via Nerdbank.GitVersioning (NBGV). Each plugin
  owns a version.json whose pathFilters exclude the generated manifests and the
  version.json itself, so version height tracks real content changes only.
- The computed version is materialized into the checked-in manifests
  (plugin.json and .codex-plugin/plugin.json) so every consumer reads a current
  value with no build step on their side.
- eng/version/Sync-PluginVersions.ps1 is the single workhorse. It resolves the
  set of changed plugins from a git diff, computes each version with nbgv
  (predicting the squash-merge height for PRs), and either reports or stamps.

AUTOMATIONS (two, low-touch by design)
- /version-bump: an admin/maintainer comments the command on a PR and the
  affected plugins are stamped on the PR branch. Gated on collaborator
  permission (admin/write/maintain); forks are rejected before any privileged
  step. No other PRs are auto-modified.
- weekly-version-sync: a Monday backstop (and workflow_dispatch) that stamps any
  drift on main, opens/updates a single bot PR, and explains the per-plugin
  reason. This self-heals anything that merged without a bump.

We deliberately did NOT auto-edit contributor PRs or add a noisy advisory
comment bot; maintainers stay in control and the signal stays clean.

SECURITY (multi-model adversarial review: GPT-5.5 + Gemini 3.1 Pro)
- Supply chain (High, both models): dotnet tool restore would have honored a
  nuget.config authored in the PR tree, letting an attacker remap the nbgv
  package source to a malicious feed and run code in the privileged
  contents:write context. Mitigated with a trusted eng/version/nuget.config
  (clear + nuget.org-only + packageSourceMapping), overlaid from main and used
  via --configfile so PR-supplied configs are ignored. No nuget.config is
  tracked in the repo today, so this path was genuinely exploitable.
- TOCTOU (Medium): /version-bump now checks out the authorized head SHA rather
  than the mutable branch name; a racing push fails non-fast-forward, which is
  the safe outcome.
- Injection: Set-ManifestVersion uses a MatchEvaluator (not a replacement
  string) so a "$"-bearing version cannot re-expand, plus a strict
  major.minor.patch guard that throws on a malformed base, leaving manifests
  untouched.
- A base-only version.json bump (0.1 -> 0.2) is correctly detected and stamped.

VERIFIED
End-to-end against a real NBGV git harness: content-scoped predict, base-only
bump -> x.y.0, docs-only -> [], weekly drift stamping, malformed-base guard,
and --configfile restore (exit 0). actionlint passes on both workflows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Skill Coverage Report

Plugin Skill Covered Coverage
dotnet-blazor author-component 0/1 0%
dotnet-blazor collect-user-input 0/4 0%
dotnet-blazor configure-auth 0/3 0%
dotnet-blazor coordinate-components 0/2 0%
dotnet-blazor fetch-and-send-data 1/5 20%
dotnet-blazor plan-ui-change 0/1 0%
dotnet-blazor support-prerendering 0/3 0%
dotnet-blazor use-js-interop 0/2 0%
dotnet-data optimizing-ef-core-queries 4/17 23.5%
⚠️ dotnet-msbuild check-bin-obj-clash 9/13 69.2%
dotnet-msbuild directory-build-organization 0/1 0%
dotnet-msbuild extension-points 0/1 0%
dotnet-msbuild msbuild-modernization 6/7 85.7%
⚠️ dotnet-msbuild msbuild-server 7/9 77.8%
dotnet-msbuild property-patterns 0/1 0%
dotnet-msbuild resolve-project-references 5/6 83.3%
dotnet-template-engine template-authoring 5/16 31.2%
dotnet-template-engine template-comparison 8/10 80%
dotnet-template-engine template-discovery 10/12 83.3%
⚠️ dotnet-template-engine template-instantiation 11/16 68.8%
⚠️ dotnet-template-engine template-smart-defaults 6/8 75%
⚠️ dotnet-template-engine template-validation 5/10 50%
⚠️ dotnet-ai mcp-csharp-create 18/25 72%
dotnet-ai mcp-csharp-debug 16/18 88.9%
⚠️ dotnet-ai mcp-csharp-publish 10/15 66.7%
⚠️ dotnet-ai mcp-csharp-test 11/18 61.1%
dotnet-ai technology-selection 20/24 83.3%
dotnet-test assertion-quality 22/22 100%
dotnet-test code-testing-agent 5/5 100%
dotnet-test crap-score 6/6 100%
dotnet-test detect-static-dependencies 15/15 100%
dotnet-test filter-syntax 1/1 100%
dotnet-test generate-testability-wrappers 22/22 100%
dotnet-test grade-tests 28/28 100%
dotnet-test migrate-static-to-wrapper 21/21 100%
dotnet-test mtp-hot-reload 16/16 100%
dotnet-test run-tests 16/16 100%
dotnet-test test-anti-patterns 21/21 100%
dotnet-test test-gap-analysis 24/24 100%
dotnet-test test-smell-detection 26/26 100%
dotnet-test test-tagging 28/28 100%
dotnet-test writing-mstest-tests 45/45 100%
⚠️ dotnet-maui maui-collectionview 7/9 77.8%
⚠️ dotnet-maui maui-data-binding 6/10 60%
dotnet-maui maui-dependency-injection 0/1 0%
dotnet-maui maui-safe-area 0/2 0%
dotnet-maui maui-theming 0/1 0%
dotnet-nuget convert-to-cpm 16/16 100%
dotnet-test-migration migrate-mstest-v1v2-to-v3 13/16 81.2%
dotnet-test-migration migrate-mstest-v3-to-v4 38/41 92.7%
dotnet-test-migration migrate-vstest-to-mtp 21/25 84%
⚠️ dotnet-test-migration migrate-xunit-to-mstest 31/46 67.4%
dotnet-test-migration migrate-xunit-to-xunit-v3 14/16 87.5%
⚠️ dotnet-aspnetcore configuring-opentelemetry-dotnet 13/21 61.9%
⚠️ dotnet-aspnetcore convert-blazor-server-to-webapp 17/27 63%
dotnet-aspnetcore dotnet-webapi 38/43 88.4%
⚠️ dotnet-aspnetcore minimal-api-file-upload 4/8 50%
dotnet-experimental exp-mock-usage-analysis 12/13 92.3%
⚠️ dotnet-experimental exp-test-maintainability 10/18 55.6%
⚠️ dotnet-upgrade dotnet-aot-compat 6/10 60%
dotnet-upgrade migrate-dotnet10-to-dotnet11 6/6 100%
dotnet-upgrade migrate-dotnet8-to-dotnet9 6/6 100%
dotnet-upgrade migrate-dotnet9-to-dotnet10 6/6 100%
⚠️ dotnet-upgrade migrate-nullable-references 20/27 74.1%
dotnet-upgrade thread-abort-migration 21/22 95.5%
dotnet setup-local-sdk 3/8 37.5%
⚠️ dotnet-diag analyzing-dotnet-performance 12/21 57.1%
dotnet-diag android-tombstone-symbolication 5/5 100%
dotnet-diag apple-crash-symbolication 5/5 100%
dotnet-diag clr-activation-debugging 16/19 84.2%
dotnet-diag dotnet-trace-collect 15/17 88.2%
dotnet-diag microbenchmarking 3/3 100%
Uncovered: dotnet-blazor/author-component
  • [CodePattern] [Parameter] (line 31)
Uncovered: dotnet-blazor/collect-user-input
  • [CodePattern] [Required] (line 135)
  • [CodePattern] [Range] (line 135)
  • [CodePattern] [SupplyParameterFromForm] (line 31)
  • [CodePattern] [CascadingParameter] (line 162)
Uncovered: dotnet-blazor/configure-auth
  • [CodePattern] [CascadingParameter] (line 51)
  • [CodePattern] [ExcludeFromInteractiveRouting] (line 152)
  • [CodePattern] [Authorize] (line 101)
Uncovered: dotnet-blazor/coordinate-components
  • [CodePattern] readonly (line 137)
  • [CodePattern] [CascadingParameter] (line 63)
Uncovered: dotnet-blazor/fetch-and-send-data
  • [CodePattern] [Parameter] (line 159)
  • [CodePattern] [PersistentState] (line 84)
  • [CodePattern] [SupplyParameterFromQuery] (line 159)
  • [CodePattern] [StreamRendering] (line 74)
Uncovered: dotnet-blazor/plan-ui-change
  • [CodePattern] [Parameter] (line 64)
Uncovered: dotnet-blazor/support-prerendering
  • [CodePattern] [CascadingParameter] (line 159)
  • [CodePattern] [PersistentState] (line 39)
  • [CodePattern] [ExcludeFromInteractiveRouting] (line 148)
Uncovered: dotnet-blazor/use-js-interop
  • [CodePattern] sealed (line 118)
  • [CodePattern] readonly (line 118)
Uncovered: dotnet-data/optimizing-ef-core-queries
  • [Validation] SQL logging shows expected number of queries (no N+1) (line 164)
  • [Validation] Read-only queries use AsNoTracking() (line 165)
  • [Validation] Hot-path queries use compiled queries (line 166)
  • [Validation] No client-side evaluation warnings in logs (line 167)
  • [Validation] Include/split strategy matches data shape (line 168)
  • [Pitfall] Global query filters forgotten in perf analysis (line 175)
  • [Pitfall] DbContext kept alive too long (line 176)
  • [Pitfall] String interpolation in FromSqlRaw (line 178)
  • [WorkflowStep] Step 1: Enable query logging to see the actual SQL (line 31)
  • [WorkflowStep] Step 4: Use compiled queries for hot paths (line 120)
  • [WorkflowStep] Step 5: Avoid common query traps (line 134)
  • [WorkflowStep] Step 6: Use raw SQL or FromSql for complex queries (line 144)
  • [CodePattern] readonly (line 122)
Uncovered: dotnet-msbuild/check-bin-obj-clash
  • [WorkflowStep] Step 2: Get an overview and list projects (line 48)
  • [WorkflowStep] Step 5: Check for double writes (line 62)
  • [WorkflowStep] Step 2: Replay the Binary Log to Text (line 78)
  • [WorkflowStep] Step 3: List All Projects (line 84)
Uncovered: dotnet-msbuild/directory-build-organization
  • [CodePattern] [MSBuild] (line 122)
Uncovered: dotnet-msbuild/extension-points
  • [CodePattern] [MSBuild] (line 195)
Uncovered: dotnet-msbuild/msbuild-modernization
  • [WorkflowStep] Step 6: Remove Unnecessary Boilerplate (line 217)
Uncovered: dotnet-msbuild/msbuild-server
  • [Validation] MSBUILDUSESERVER=1 is set in the shell (line 59)
  • [Validation] Second sequential build is faster than the first (line 60)
Uncovered: dotnet-msbuild/property-patterns
  • [CodePattern] [MSBuild] (line 33)
Uncovered: dotnet-msbuild/resolve-project-references
  • [Validation] ResolveProjectReferences was not set as the optimization target (line 64)
Uncovered: dotnet-template-engine/template-authoring
  • [Validation] Template identity and shortName are unique and meaningful (line 102)
  • [Validation] All parameters have descriptions and appropriate defaults (line 103)
  • [Validation] Template can be installed, dry-run, and instantiated successfully (line 104)
  • [Validation] Created projects build cleanly with dotnet build (line 105)
  • [Validation] Conditional content produces correct output for all parameter combinations (line 106)
  • [Pitfall] Identity format issues (line 112)
  • [Pitfall] Missing parameter descriptions (line 114)
  • [Pitfall] Not testing all parameter combinations (line 115)
  • [Pitfall] Not setting classifications (line 117)
  • [WorkflowStep] Step 3: Refine the template (line 80)
  • [WorkflowStep] Step 4: Test the template locally (line 90)
Uncovered: dotnet-template-engine/template-comparison
  • [Validation] Differences relevant to the user's scenario are called out explicitly (line 90)
  • [WorkflowStep] Step 3: Recommend (line 81)
Uncovered: dotnet-template-engine/template-discovery
  • [Validation] User understands what the template produces before proceeding to creation (line 145)
  • [Pitfall] A dotnet new call fails with a "mutex"/"persistence" error and you return nothing (line 154)
Uncovered: dotnet-template-engine/template-instantiation
  • [Validation] If CPM is active, .csproj has no version attributes and Directory.Packages.props has matching entries (line 128)
  • [Pitfall] Creating projects without specifying the framework (line 137)
  • [WorkflowStep] Step 2: Analyze the workspace (line 50)
  • [WorkflowStep] Step 3: Preview the creation (line 59)
  • [WorkflowStep] Step 6: Template package management (line 109)
Uncovered: dotnet-template-engine/template-smart-defaults
  • [Validation] No parameter the user set explicitly was overridden (line 62)
  • [Validation] Only unset parameters were filled (line 63)
Uncovered: dotnet-template-engine/template-validation
  • [Pitfall] ShortName = "list" or "search" (line 150)
  • [Pitfall] Invalid datatype value (line 153)
  • [Pitfall] Parameter prefix collision (Auth vs AuthMode) (line 155)
  • [Pitfall] Source condition without parentheses (line 156)
  • [WorkflowStep] Step 1: Locate the template.json (line 124)
Uncovered: dotnet-ai/mcp-csharp-create
  • [Validation] Project builds with no errors (dotnet build) (line 232)
  • [Validation] HTTP: app.MapMcp() is called in Program.cs (line 237)
  • [Validation] Server starts successfully with dotnet run (line 238)
  • [Pitfall] WithToolsFromAssembly() fails in AOT (line 247)
  • [WorkflowStep] Step 5: Add prompts and resources (optional) (line 151)
  • [CodePattern] CancellationToken (line 90)
  • [CodePattern] [Description] (line 90)
Uncovered: dotnet-ai/mcp-csharp-debug
  • [Validation] Breakpoints hit when debugging in IDE (line 185)
  • [Pitfall] HTTP server returns 404 at MCP endpoint (line 195)
Uncovered: dotnet-ai/mcp-csharp-publish
  • [Validation] NuGet: Package installs and runs via dnx PackageId@version (line 243)
  • [Validation] Azure: Server is reachable and tools respond (line 245)
  • [Validation] MCP Registry: Server appears at registry.modelcontextprotocol.io (line 246)
  • [Validation] MCP client can connect and call tools on the deployed server (line 247)
  • [Pitfall] Docker container exits immediately (line 255)
Uncovered: dotnet-ai/mcp-csharp-test
  • [Validation] All tests pass: dotnet test (line 165)
  • [Validation] Tests run in CI without manual setup (line 166)
  • [Pitfall] Full test suite runs are slow (line 176)
  • [WorkflowStep] Step 4: Run tests (line 138)
  • [CodePattern] [InlineData] (line 57)
  • [CodePattern] [Theory] (line 57)
  • [CodePattern] [Fact] (line 57)
Uncovered: dotnet-ai/technology-selection
  • [Validation] Non-deterministic outputs have validation and fallback paths (line 319)
  • [Validation] dotnet build -c Release -warnaserror completes cleanly (line 320)
  • [Pitfall] Over-engineering with LLMs (line 348)
  • [Pitfall] Cold start latency on ML.NET models (line 354)
Uncovered: dotnet-maui/maui-collectionview
  • [Pitfall] EmptyView doesn't render correctly (line 329)
  • [Pitfall] Poor scroll performance (line 330)
Uncovered: dotnet-maui/maui-data-binding
  • [Pitfall] Specifying redundant Mode=OneWay / Mode=TwoWay (line 375)
  • [Pitfall] Mutating ObservableCollection off the UI thread (line 377)
  • [Pitfall] Complex converter chains in hot paths (line 378)
  • [Pitfall] Binding to non-public properties (line 380)
Uncovered: dotnet-maui/maui-dependency-injection
  • [CodePattern] readonly (line 102)
Uncovered: dotnet-maui/maui-safe-area
  • [CodePattern] readonly (line 65)
  • [CodePattern] [Flags] (line 48)
Uncovered: dotnet-maui/maui-theming
  • [CodePattern] [Activity] (line 226)
Uncovered: dotnet-test-migration/migrate-mstest-v1v2-to-v3
  • [Validation] Project builds with zero errors (line 185)
  • [Validation] All tests pass (dotnet test) -- compare pass/fail counts to pre-migration baseline (line 186)
  • [CodePattern] Assert.AreNotEqual (line 144)
Uncovered: dotnet-test-migration/migrate-mstest-v3-to-v4
  • [CodePattern] [TestMethodAttribute] (line 172)
  • [CodePattern] [TestMethod] (line 265)
  • [CodePattern] [Timeout] (line 212)
Uncovered: dotnet-test-migration/migrate-vstest-to-mtp
  • [Validation] dotnet build completes with zero errors (line 361)
  • [Validation] Test executable runs directly (e.g., ./bin/Debug/net8.0/MyTests.exe) (line 363)
  • [Validation] No vstest.console.exe invocations remain in CI scripts (line 366)
  • [CodePattern] [trait] (line 252)
Uncovered: dotnet-test-migration/migrate-xunit-to-mstest
  • [Validation] using Xunit; and using Xunit.Abstractions; removed (line 521)
  • [Validation] xunit.runner.json removed; equivalent config in .runsettings / [assembly: Parallelize] (line 522)
  • [Validation] Project builds with zero errors (line 524)
  • [CodePattern] [TestClass] (line 277)
  • [CodePattern] [ClassCleanup] (line 277)
  • [CodePattern] [TestMethod] (line 320)
  • [CodePattern] [DoNotParallelize] (line 434)
  • [CodePattern] Assert.False (line 347)
  • [CodePattern] Assert.IsFalse (line 347)
  • [CodePattern] sealed (line 277)
  • [CodePattern] readonly (line 262)
  • [CodePattern] [Fact] (line 308)
  • [CodePattern] [ClassInitialize] (line 277)
  • [CodePattern] Parallelize (line 405)
  • [CodePattern] DoNotParallelize (line 434)
Uncovered: dotnet-test-migration/migrate-xunit-to-xunit-v3
  • [WorkflowStep] Step 13: Build and verify (line 199)
  • [CodePattern] sealed (line 118)
Uncovered: dotnet-aspnetcore/configuring-opentelemetry-dotnet
  • [Validation] Traces appear in the observability backend (Jaeger, Aspire dashboard, etc.) (line 272)
  • [Validation] HTTP requests automatically create spans with correct verb, URL, status code (line 273)
  • [Validation] Health check endpoints are filtered from traces (line 277)
  • [Validation] Exception details appear on error spans (line 278)
  • [Pitfall] Missing HTTP client spans (line 286)
  • [Pitfall] High cardinality tags (line 287)
  • [CodePattern] readonly (line 124)
  • [CodePattern] [key] (line 241)
Uncovered: dotnet-aspnetcore/convert-blazor-server-to-webapp
  • [Validation] No references to AddServerSideBlazor remain (line 261)
  • [Validation] No references to MapBlazorHub remain (line 262)
  • [Validation] No references to MapFallbackToPage("/_Host") remain (line 263)
  • [Validation] No references to blazor.server.js remain (line 264)
  • [Validation] Pages/_Host.cshtml has been deleted (line 265)
  • [Validation] App builds and runs successfully on the target framework (line 272)
  • [Pitfall] Not migrating AddServerSideBlazor circuit options (line 283)
  • [Pitfall] CSS isolation bundle link has wrong assembly name (line 285)
  • [WorkflowStep] Step 1: Update the project file (line 48)
  • [WorkflowStep] Step 6: Recommended improvements (optional) (line 234)
Uncovered: dotnet-aspnetcore/dotnet-webapi
  • [Validation] DELETE endpoints return 204 No Content (line 464)
  • [Validation] A .http file exists with a request for every new endpoint (line 474)
  • [Validation] dotnet build passes with zero errors and zero warnings (line 475)
  • [CodePattern] [HttpGet] (line 248)
  • [CodePattern] [Range] (line 147)
Uncovered: dotnet-aspnetcore/minimal-api-file-upload
  • [WorkflowStep] Step 1: CRITICAL — Understand IFormFile Binding in Minimal APIs (line 29)
  • [WorkflowStep] Step 5: CRITICAL — Streaming Large Files Without Buffering (line 170)
  • [CodePattern] [RequestSizeLimit] (line 54)
  • [CodePattern] [FromForm] (line 31)
Uncovered: dotnet-experimental/exp-mock-usage-analysis
  • [Validation] Production code was read and execution paths were traced (not just test code reviewed) (line 86)
Uncovered: dotnet-experimental/exp-test-maintainability
  • [Validation] Every finding shows the actual duplicated code, not just a description (line 188)
  • [Validation] Every suggestion includes a concrete before/after example (line 189)
  • [Validation] Findings are filtered through the 3+ occurrence threshold (line 190)
  • [Validation] Simple constructors are not flagged (line 191)
  • [Validation] If tests are clean, the report says so upfront (line 193)
  • [Pitfall] Suggesting extraction for 2 occurrences (line 200)
  • [Pitfall] Flagging simple new X() as boilerplate (line 203)
  • [CodePattern] [TestMethod] (line 67)
Uncovered: dotnet-upgrade/dotnet-aot-compat
  • [CodePattern] [JsonSerializerContext] (line 169)
  • [CodePattern] [MSBuild] (line 67)
  • [CodePattern] [DynamicallyAccessedMembers] (line 119)
  • [CodePattern] [RequiresUnreferencedCode] (line 195)
Uncovered: dotnet-upgrade/migrate-nullable-references
  • [Validation] Project file(s) contain <Nullable>enable</Nullable> (or #nullable enable per-file for file-by-file strategy) (line 235)
  • [Validation] <WarningsAsErrors>nullable</WarningsAsErrors> added to project file to prevent regressions (line 237)
  • [Validation] No #nullable disable directives remain unless justified with a comment (line 239)
  • [Validation] Public API signatures accurately reflect null contracts (line 241)
  • [Validation] For public libraries: breaking changes documented in nullable-breaking-changes.md and reviewed by the user (line 242)
  • [Pitfall] Multi-target projects and older TFMs (line 265)
  • [Pitfall] Warnings reappear after upgrading a dependency (line 266)
Uncovered: dotnet-upgrade/thread-abort-migration
  • [Validation] No SYSLIB0006 pragma suppressions remain (line 117)
Uncovered: dotnet/setup-local-sdk
  • [Pitfall] paths ignored (line 382)
  • [Pitfall] dotnet app.dll wrong runtime (line 386)
  • [CodePattern] [ordered] (line 301)
  • [CodePattern] [guid] (line 104)
  • [CodePattern] [pscustomobject] (line 301)
Uncovered: dotnet-diag/analyzing-dotnet-performance
  • [Validation] All critical patterns were checked (from reference files or inline recipes) (line 178)
  • [Validation] Topic-specific recipes run only when matching signals detected (line 179)
  • [Validation] Each finding includes a concrete code fix (line 180)
  • [Validation] Scan execution checklist is complete (all recipes run) (line 181)
  • [Validation] Summary table included at end (line 182)
  • [Pitfall] Suggesting ConfigureAwait(false) in app code (line 191)
  • [Pitfall] Recommending ValueTask everywhere (line 192)
  • [Pitfall] Flagging new HttpClient() in DI services (line 193)
  • [Pitfall] Suggesting CollectionsMarshal.AsSpan broadly (line 195)
Uncovered: dotnet-diag/clr-activation-debugging
  • [Validation] The entry point for each problematic activation was identified (line 289)
  • [Validation] SEM_FAILCRITICALERRORS state was noted for FOD-related issues (line 292)
  • [Validation] Multiple activations within a single log were individually traced (line 293)
Uncovered: dotnet-diag/dotnet-trace-collect
  • [Validation] The recommended tool is compatible with the developer's runtime, OS, and deployment topology (line 235)
  • [Validation] The output file is generated in the expected location (line 237)

@AbhitejJohn AbhitejJohn requested a review from Evangelink June 24, 2026 17:12
@AbhitejJohn

Copy link
Copy Markdown
Collaborator Author

Curious to hear thoughts on this versioning strategy. Goal was to keep this with as less ceremony as possible. At the moment Claude code users do not really get updates if they've already pulled down a plugin earlier because we haven't changed our versions.

@AbhitejJohn AbhitejJohn marked this pull request as ready for review June 24, 2026 17:14
Copilot AI review requested due to automatic review settings June 24, 2026 17:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces automated, per-plugin semantic versioning for the plugins/* tree using Nerdbank.GitVersioning (NBGV), and adds two GitHub Actions automations to materialize the computed versions into the checked-in plugin manifests (so downstream consumers read current versions directly from the repo).

Changes:

  • Add per-plugin plugins/<plugin>/version.json files to scope NBGV version height to each plugin subtree.
  • Add eng/version/Sync-PluginVersions.ps1 plus a locked-down eng/version/nuget.config and a local tool manifest for nbgv.
  • Add two workflows: /version-bump (issue_comment-driven, same-repo PRs) and a scheduled weekly backstop PR that stamps any drift on main.
Show a summary per file
File Description
plugins/dotnet11/version.json Adds NBGV per-plugin version configuration for dotnet11.
plugins/dotnet/version.json Adds NBGV per-plugin version configuration for dotnet.
plugins/dotnet-upgrade/version.json Adds NBGV per-plugin version configuration for dotnet-upgrade.
plugins/dotnet-test/version.json Adds NBGV per-plugin version configuration for dotnet-test.
plugins/dotnet-template-engine/version.json Adds NBGV per-plugin version configuration for dotnet-template-engine.
plugins/dotnet-nuget/version.json Adds NBGV per-plugin version configuration for dotnet-nuget.
plugins/dotnet-msbuild/version.json Adds NBGV per-plugin version configuration for dotnet-msbuild.
plugins/dotnet-maui/version.json Adds NBGV per-plugin version configuration for dotnet-maui.
plugins/dotnet-experimental/version.json Adds NBGV per-plugin version configuration for dotnet-experimental.
plugins/dotnet-diag/version.json Adds NBGV per-plugin version configuration for dotnet-diag.
plugins/dotnet-data/version.json Adds NBGV per-plugin version configuration for dotnet-data.
plugins/dotnet-blazor/version.json Adds NBGV per-plugin version configuration for dotnet-blazor.
plugins/dotnet-aspnetcore/version.json Adds NBGV per-plugin version configuration for dotnet-aspnetcore.
plugins/dotnet-ai/version.json Adds NBGV per-plugin version configuration for dotnet-ai.
eng/version/Sync-PluginVersions.ps1 Core PowerShell implementation to compute/stamp versions and emit a JSON report.
eng/version/nuget.config Locked-down NuGet config used by workflows to prevent PR-controlled feed remapping during tool restore.
.config/dotnet-tools.json Adds local nbgv tool dependency for consistent version computation in CI.
.github/workflows/version-bump-command.yml Implements the maintainer-triggered /version-bump workflow.
.github/workflows/weekly-version-sync.yml Implements the scheduled backstop that opens/updates a single “weekly sync” PR on drift.
CONTRIBUTING.md Documents the new per-plugin versioning model and contributor expectations.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 20/20 changed files
  • Comments generated: 4

Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread .github/workflows/weekly-version-sync.yml Outdated
Comment thread eng/version/Sync-PluginVersions.ps1
@github-actions github-actions Bot added the waiting-on-author PR state label label Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

👋 @AbhitejJohn — this PR has 4 unresolved review thread(s). When you're ready, please address the feedback and push an update; the triage bot will pick up the next state automatically. (Add the no-stale label to silence further pings.)

- Add missing plugins/dotnet-test-migration/version.json so it participates
  in versioning (it was the only plugin without one; manifests are at 0.1.0).
- CONTRIBUTING: the two manifests are not byte-identical; say the version is
  duplicated across two manifest files instead.
- weekly-version-sync: include version.json in commit attribution so a
  base-only bump is explained rather than showing 'no attributable commits'.
- Get-NbgvInfo: capture nbgv stderr and include it in the thrown error so CI
  failures are diagnosable, while keeping stdout clean for JSON parsing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink enabled auto-merge (squash) June 25, 2026 07:57
@github-actions github-actions Bot added waiting-on-review PR state label and removed waiting-on-author PR state label labels Jun 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Evaluation passed for 766115f. cc @webreidi @AbhitejJohn @ViktorHofer @JanKrivanek @dotnet/aspnet @Redth @jfversluis @dotnet/skills-maui-reviewers @dotnet/skills-msbuild-reviewers @dotnet/area-infrastructure-libraries @kartheekp-ms @YuliiaKovalova @dotnet/dotnet-testing @ManishJayaswal — please review.

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: per-plugin NBGV versioning

Really like the overall design — per-plugin pathFilters, materializing into the checked-in manifests, the opt-in /version-bump + weekly backstop split, and the security hardening on the privileged workflow (SHA-pinned actions, fail-closed permission gate, fork rejection before any privileged step, --configfile source-mapped NuGet, trusted-tooling overlay from main, injection-safe MatchEvaluator). The security posture of version-bump-command.yml looks sound to me.

My concern is that the privileged workflow's git plumbing and the tool runtime pin look like they'll prevent /version-bump (and likely the weekly sync) from actually running. Because both workflows are issue_comment/schedule-triggered they run from main and get no CI signal on this PR, so these can only be caught by review. Two of the inline findings below were reproduced in a scratch repo.

This was a combined pass (two independent reviewers); we converged on the same set with no contradictions. Inline comments have details + suggested fixes. Summary:

Blocking / CI-breaking

  • git fetch --depth=1 origin main makes the whole repo shallow → NBGV refuses to compute height. Reproduced: a depth-limited fetch writes .git/shallow and flips the entire repo to shallow even though the head was checked out with fetch-depth: 0, so every nbgv get-version in the ordinary-content path fails.
  • git merge-base $BASE_SHA $head returns empty when the PR is behind main. Reproduced (PR 3 commits behind → empty merge-base, exit 1 → script throws -PredictSquashMerge requires -BaseCommit). Fixed by the same full-fetch change, plus an empty-merge-base guard.
  • nbgv (net8) under rollForward: false on a net11-preview-only runtime. setup-dotnet installs only the global.json runtime; a net8-targeted tool won't run, and even rollForward: true won't pick a preview runtime without DOTNET_ROLL_FORWARD_TO_PRERELEASE=1. Best fix: explicitly install an 8.0.x runtime in both workflows.

Correctness

  • Squash prediction over-bumps on version.json-only (non-base) edits, then the weekly backstop computes the true (lower) height and corrects downward — a non-monotonic version regression visible to consumers.

Minor

  • Format guard validates the computed value but not the 2-part base shape in version.json (a malformed 3-part base slips through the weekly path).
  • Concurrent /version-bump on the same plugin can stamp colliding patch numbers until the weekly sync reconciles — worth a doc note.
  • PR description says version.json ×14; there are 15 (dotnet-test-migration was added in commit 2).

Comment thread .github/workflows/version-bump-command.yml Outdated
Comment thread .github/workflows/version-bump-command.yml
Comment thread .config/dotnet-tools.json
Comment thread eng/version/Sync-PluginVersions.ps1 Outdated
Comment thread eng/version/Sync-PluginVersions.ps1
@github-actions github-actions Bot added waiting-on-author PR state label and removed waiting-on-review PR state label labels Jun 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

👋 @AbhitejJohn — this PR has 5 unresolved review thread(s). When you're ready, please address the feedback and push an update; the triage bot will pick up the next state automatically. (Add the no-stale label to silence further pings.)

Address @Evangelink's review on the per-plugin versioning workflows. These
issue_comment/schedule workflows run from main and get no CI signal on the PR,
so each fix is validated with a local NBGV git harness instead.

Workflow runtime/git plumbing (version-bump-command.yml, weekly-version-sync.yml):
- Drop --depth=1 from the "pin trusted tooling from main" fetch. A shallow fetch
  writes .git/shallow and flips the whole repo shallow (proven locally), and NBGV
  refuses to compute height on a shallow repo. fetch-depth: 0 already fetched main,
  so the full fetch is cheap and keeps the supply-chain pin intact.
- Add a guard that throws if `git merge-base BASE_SHA head` is empty, instead of
  letting nbgv fail with an opaque "-PredictSquashMerge requires -BaseCommit".
- setup-dotnet: install dotnet-version 8.0.x alongside global-json-file. nbgv
  targets net8.0; global.json pins an SDK that ships only a net11 preview runtime,
  onto which a release-targeted tool will not roll forward. setup-dotnet installs
  both inputs, giving nbgv a runtime to run on.

Prediction correctness (Sync-PluginVersions.ps1):
- Only add +1 to the predicted height when a height-bearing file actually changed.
  A version.json-only non-base edit is height-neutral, so predicting +1 over-bumps
  and the weekly sync would later correct it downward — a visible version
  regression. New Test-HeightBearingChange checks the diff with the canonical
  exclusions.
- Validate the version.json base is major.minor; a 3-part base (e.g. 0.1.0) was
  silently normalized by NBGV into a passing SimpleVersion on the weekly path.
- Reject non-canonical pathFilters (raised in cross-model review). The predict
  height math assumes version.json and the two stamped manifests are excluded and
  that the filter set is stable between base and head; a hand-edited pathFilters
  would silently diverge the post-merge height from the prediction. The
  height-excluded file list is now a single source of truth shared by the guard
  and Test-HeightBearingChange.

CONTRIBUTING.md: note that concurrently bumped PRs can predict the same patch and
that the weekly sync reconciles the collision.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 30, 2026 18:12
@AbhitejJohn

Copy link
Copy Markdown
Collaborator Author

@Evangelink thanks for the thorough pass — all five findings are addressed in 22133dc, and since these issue_comment/schedule workflows run from main (no PR CI signal), I validated each one with a local NBGV git harness rather than relying on CI:

Finding Fix Proof
Shallow --depth=1 fetch breaks NBGV Drop --depth=1; fetch-depth: 0 already has main Local repro: --depth=1.git/shallow present; plain fetch → non-shallow
Empty merge-base when PR is behind Full fetch resolves it; explicit empty-merge-base guard as backstop
nbgv (net8.0) won't run on preview-only runtime setup-dotnet installs 8.0.x and the global.json SDK setup-dotnet docs: "versions from both inputs will be installed"
version.json-only edit over-bumps in predict +1 only when a height-bearing file changed (Test-HeightBearingChange) Harness: height-neutral edit → changed:false (was +1)
3-part base slips through on weekly path Base-shape guard ^\d+\.\d+$ before both branches Harness: 0.1.0 base now throws on weekly path

One extra hardening came out of a cross-model adversarial review of the over-bump fix: a non-canonical pathFilters edit would still diverge the post-merge height from the prediction, so the script now rejects any pathFilters that aren't the canonical generated set (both paths), with the height-excluded file list as a single source of truth. PR description plugin count corrected to ×15.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 21/21 changed files
  • Comments generated: 2

Comment thread .config/dotnet-tools.json
Comment thread CONTRIBUTING.md Outdated
Clarify in CONTRIBUTING that the two generated manifests and version.json are
excluded from NBGV height, so editing only manifest metadata won't change the
patch number or be picked up by /version-bump or the weekly sync.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

👋 @AbhitejJohn — this PR has 7 unresolved review thread(s). When you're ready, please address the feedback and push an update; the triage bot will pick up the next state automatically. (Add the no-stale label to silence further pings.)

Copilot AI review requested due to automatic review settings July 6, 2026 05:41
@github-actions github-actions Bot added waiting-on-review PR state label and removed waiting-on-author PR state label labels Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

✅ Evaluation passed for 284695d. cc @webreidi @AbhitejJohn @ViktorHofer @JanKrivanek @dotnet/aspnet @Redth @jfversluis @dotnet/skills-maui-reviewers @dotnet/skills-msbuild-reviewers @dotnet/area-infrastructure-libraries @kartheekp-ms @YuliiaKovalova @dotnet/dotnet-testing @ManishJayaswal — please review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 21/21 changed files
  • Comments generated: 6

Comment on lines +2 to +4
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.1",
"pathFilters": [
Comment on lines +2 to +4
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.1",
"pathFilters": [
Comment on lines +66 to +71
if ($PredictSquashMerge -and -not $BaseCommit) {
throw '-PredictSquashMerge requires -BaseCommit (the PR merge base).'
}
if ($HeadCommit -and -not $BaseCommit) {
throw '-HeadCommit requires -BaseCommit (the diff is BaseCommit..HeadCommit).'
}
Comment on lines +73 to +75
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..' '..')).Path
$pluginsRoot = Join-Path $repoRoot 'plugins'

Comment on lines +179 to +183
$Plugins = Get-ChildItem -Path $pluginsRoot -Directory |
Where-Object { Test-Path (Join-Path $_.FullName 'version.json') } |
Select-Object -ExpandProperty Name |
Sort-Object
}
Comment thread CONTRIBUTING.md
Comment on lines +78 to +81
Versioning is automated with [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning).
A per-plugin `plugins/<plugin>/version.json` scopes the git height to that plugin's subtree, so the
**patch** number is derived from history — you do not edit it by hand. The two generated manifests
(`plugin.json`, `.codex-plugin/plugin.json`) and `version.json` itself are excluded from that height
@github-actions github-actions Bot added waiting-on-author PR state label and removed waiting-on-review PR state label labels Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

👋 @AbhitejJohn — this PR has 6 unresolved review thread(s). When you're ready, please address the feedback and push an update; the triage bot will pick up the next state automatically. (Add the no-stale label to silence further pings.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-author PR state label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants