[Upgrade] to .NET 10, System.CommandLine 2.0 GA, ClosedXML CVE fix#9
Merged
samatstariongroup merged 7 commits intoSTARIONGROUP:developmentfrom Apr 23, 2026
Merged
Conversation
Keeps .NET 8 (LTS, supported through Nov 2026) as the default target while adding .NET 10 (LTS, supported through Nov 2028) so that the global tool `vcdg` can be installed on machines with the .NET 10 SDK without requiring a fallback .NET 8 runtime to be present. .NET 9 is intentionally skipped as STS. No dependency changes required: Microsoft.Extensions.* 8.0.x packages are forward-compatible with net10 via the standard TFM compatibility rules.
…sions Instructs actions/setup-dotnet to install both 8.0.x and 10.0.x so the multi-targeted solution can be built and tested on both TFMs. Bumps actions/checkout, actions/setup-java, actions/setup-dotnet, and actions/upload-artifact to the current supported majors (checkout@v4, setup-java@v4, setup-dotnet@v4, upload-artifact@v4). actions/upload-artifact@v3 in particular was retired by GitHub in early 2025 and no longer functions on current runners, so this refresh is required for the workflow to run at all.
Fixes two high-severity CVEs on the transitive System.IO.Packaging 8.0.0 pulled in by ClosedXML 0.104.1: - GHSA-f32c-w444-8ppv - GHSA-qj66-m88j-hmgj dotnet list package --vulnerable --include-transitive reports no vulnerable packages after the bump on either TFM.
…g alpha
System.CommandLine.Hosting has been alpha for ~7 years and did not ship
a GA companion alongside System.CommandLine 2.0. Drop it and migrate the
parser to 2.0.6 GA.
- Program.cs now builds the generic Host directly and uses
rootCommand.SetAction(...) to resolve the handler from DI and bind
parsed option values.
- GenerateCommand.cs exposes options as public properties and adds
BindTo(Handler, ParseResult). Options use the 2.0 GA API
(new Option<T>(name, aliases...) { Description, Required,
DefaultValueFactory }).
- Handler no longer implements ICommandHandler; the throwing
Invoke(InvocationContext) is removed and InvokeAsync() takes no
parameters.
- A custom SynchronousCommandLineAction preserves the ASCII-logo-on-
--help behaviour.
- Test fixture drops the now-obsolete
Verify_that_GenerateCommand_Invoke_throws_exception and all
InvocationContext plumbing (12 tests per TFM, down from 13).
Microsoft.Extensions.Hosting becomes a direct reference (was transitive
via the removed .Hosting package). Microsoft.Extensions.Configuration.Json
8.0.0 -> 8.0.1 and Microsoft.Extensions.Logging.Abstractions 8.0.1 ->
8.0.2 patch-bumps resolve the version downgrade pulled in by
Microsoft.Extensions.Hosting 8.0.1. Same patch bump for
Microsoft.Extensions.Logging.Console in the test project.
Split Microsoft.Extensions.*, Serilog.Extensions.Hosting and Serilog.Settings.Configuration into two conditional ItemGroups so each build resolves its own runtime-aligned major: Package | net8.0 | net10.0 ---------------------------------------------|--------|-------- Microsoft.Extensions.Configuration.Json | 8.0.1 | 10.0.6 Microsoft.Extensions.Hosting | 8.0.1 | 10.0.6 Microsoft.Extensions.Logging.Abstractions | 8.0.2 | 10.0.6 Microsoft.Extensions.Logging.Console (tests) | 8.0.1 | 10.0.6 Serilog.Extensions.Hosting | 8.0.0 | 10.0.0 Serilog.Settings.Configuration | 8.0.2 | 10.0.0 Serilog bumped 4.0.2 -> 4.3.1 as a runtime-agnostic package because Serilog.Extensions.Hosting 10.0.0 requires Serilog >= 4.3.0.
- Program.cs: build the generic Host inside the SetAction callback instead of up-front so --help, --version and parse-error paths no longer pay for Serilog + DI container setup. - GenerateCommand.cs: drop DefaultValueFactory on --no-logo (bool already defaults to false). - GenerateCommand.cs: replace reflection-based source-directory default (Assembly.GetExecutingAssembly().Location) with AppContext.BaseDirectory. The reflection form returns an empty string under single-file publish, which vcdg is a plausible target for as a global tool.
samatstariongroup
requested changes
Apr 22, 2026
Removes net8.0 from target frameworks, drops the now-redundant TFM-conditional ItemGroups, and trims the CI dotnet matrix to 10.0.x. Also bumps copyright header year in Program.cs and GenerateCommand.cs.
Contributor
Author
|
@samatstariongroup thanks for the review — pushed
Local verification: |
samatstariongroup
approved these changes
Apr 23, 2026
5 tasks
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.
Prerequisites
Use case
We have a .NET 10 project that wants to use
vcdgas a global tool. On a machine whose only installed SDK is .NET 10,dotnet tool install -g vcdgcurrently fails at runtime:This PR switches
vcdgto net10 so it installs and runs on .NET 10 without having to side-load a net8 runtime.Framework choice
Single LTS target:
net10.0(Nov 2025, supported until Nov 2028). Per review feedback, net8.0 is dropped entirely rather than multi-targeted — the project moves forward on net10 only.Considered alternative:
<RollForward>Major</RollForward>Setting
RollForward=Majoron the net8 build would let the tool's apphost pick the net10 runtime at launch, satisfying the use case without changing the TFM. We chose against it because the tool would still ship only net8 assemblies (forgoing net10-aligned package fixes and keeping net8-line CVE liability), silent cross-major behavior changes (globalization,System.Text.Jsondefaults,Assembly.Locationunder single-file, TLS defaults) would surface only at runtime rather than at compile time, and the same config would also roll the tool forward into future unknown majors (net11, net12…) without a recompile. Moving the TFM verifies at build time that the code compiles and tests pass against the target runtime's reference assemblies — a stronger contract for a published NuGet tool with external consumers.Changes
Target
net10.0— both csprojs set<TargetFramework>net10.0</TargetFramework>.CI —
.github/workflows/CodeQuality.ymlinstalls10.0.xonly, and refreshesactions/checkout,setup-java,setup-dotnet,upload-artifactto their current majors. The@v3ofupload-artifactin particular was retired by GitHub in early 2025 and no longer functions on current runners, so this refresh is required for the workflow to run at all.Security —
ClosedXML0.104.1 → 0.105.0 resolves two High severity advisories (GHSA-f32c-w444-8ppv,GHSA-qj66-m88j-hmgj) on the transitiveSystem.IO.Packaging 8.0.0.System.CommandLinemigration — 2.0.0-beta4 (Jun 2022) → 2.0.6 GA, and dropsSystem.CommandLine.Hosting0.4.0-alpha (7 years in alpha, no GA companion):Program.csbuilds the genericHostlazily insideSetAction(so--help/--version/ parse errors don't pay for DI + Serilog setup) and resolves the handler from it to bind parsed option values.GenerateCommand.csexposes options as public properties and addsBindTo(Handler, ParseResult); all options use the 2.0 GA API (new Option<T>(name, aliases…) { Description, Required, DefaultValueFactory }).Handlerno longer implementsICommandHandler; the throwingInvoke(InvocationContext)is removed.SynchronousCommandLineActionpreserves the ASCII-logo-on---helpbehavior.Verify_that_GenerateCommand_Invoke_throws_exception(12 tests, down from 13).Package versions aligned to net10 — runtime-aligned packages move to their net10 majors:
Microsoft.Extensions.Configuration.JsonMicrosoft.Extensions.HostingMicrosoft.Extensions.Logging.AbstractionsMicrosoft.Extensions.Logging.Console(tests)Serilog.Extensions.HostingSerilog.Settings.ConfigurationRuntime-agnostic bump —
Serilog4.0.2 → 4.3.1 (required bySerilog.Extensions.Hosting 10.0.0).Copyright — header year bumped to 2026 in
Program.csandCommands/GenerateCommand.cs.Verification
Builds clean (0 warnings / 0 errors) and tests pass:
dotnet list package --vulnerable --include-transitivereports no vulnerable packages.Out of scope (pre-existing, flagged by review)
Kept for a separate cleanup PR to keep this one focused on the net10 migration:
Thread.Sleep(1500)blocking calls inside the async spinner lambda (GenerateCommand.cs, in the SpectreStatus.Startcallback) — theatrical UX delays.IEnumerable<T>.Count()on reader results (potential re-enumeration of LINQ chains).Task.WhenAll, currently serialized for spinner UX).FileInfo.Exists/DirectoryInfo.ExistsTOCTOU pre-checks inHandler.InvokeAsync.Handlerconstructor XML doc.Handler.InvokeAsync(method → try → spinner lambda → inner try/catch).Known CI caveat (pre-existing)
The workflow uses
SONAR_TOKENfrom repo secrets. GitHub does not pass secrets topull_requestworkflows triggered from a fork, so the SonarCloud steps will fail on the CI run against this PR with a missing-token error. This is a pre-existing fork-PR limitation unrelated to the changes in this PR. The build/test steps run fine regardless; only the Sonar begin/end steps will be affected.Commits
[Update] multi-target net8.0 and net10.0— initial csproj TFM change (later collapsed to net10-only in commit 7).[Update] CI to install .NET 8 and .NET 10 SDKs and refresh action versions— workflow changes (matrix trimmed to 10.0.x in commit 7).[Bump] ClosedXML to 0.105.0 to resolve high-severity advisories— security fix.[Migrate] System.CommandLine from beta to 2.0 GA, remove dead .Hosting alpha— parser migration + code rewrites.[Update] runtime-aligned packages with TFM-conditional ItemGroups— per-TFM package splitting (conditions removed in commit 7).[Simplify] defer Host build and drop redundant defaults— lazy Host build, drop redundantfalsefactory, replace reflection-based source-directory default withAppContext.BaseDirectory.[Drop] net8.0 support, collapse to net10-only per PR review— drops net8 target, removes TFM-conditional ItemGroups, trims CI matrix to 10.0.x, bumps copyright year.