Skip to content

Improve fallout-migrate: NuGet-scoped version pin, TFM warning/bump, confirmation prompt, async pipeline#509

Merged
dennisdoomen merged 9 commits into
mainfrom
actual-migration-improvements
Jul 20, 2026
Merged

Improve fallout-migrate: NuGet-scoped version pin, TFM warning/bump, confirmation prompt, async pipeline#509
dennisdoomen merged 9 commits into
mainfrom
actual-migration-improvements

Conversation

@dennisdoomen

@dennisdoomen dennisdoomen commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

A set of focused improvements to fallout-migrate: pin the migrated Fallout.Common reference to the latest NuGet release instead of the running tool's own version, warn and optionally bump an outdated build-project TFM, ask for confirmation before writing files, and convert the migration pipeline to async.

image

Changes

Bump Fallout.Migrate to the 10.x version line — version bump.

Resolve Fallout.Common version from NuGet, scoped to the tool's own majorResolveFalloutVersionStep now queries NuGet's flat-container index for the latest non-prerelease Fallout.Common release matching the running tool's own major (calendar year), instead of only reading the tool's own AssemblyInformationalVersion. Falls back to the tool's own version when NuGet can't be reached or has no matching-major stable release yet.

Warn when the build project targets an older TFM than .NET 10VerifyBuildTargetFrameworkStep reads _build.csproj's TargetFramework(s) and warns for any moniker older than net10.0.

Ask for confirmation before the migration writes any filesConfirmMigrationStep prompts via AnsiConsole.Confirm immediately before the first file-mutating step. Declining sets Summary.Cancelled and stops the pipeline. Skipped for --dry-run and when stdin is redirected.

Fold CsprojRewriter into RewriteCsprojsStepCsprojRewriter.Rewrite had a single caller; merged directly into the step.

Bump build project to net10.0 and global.json SDK to 10.0.100BumpDotNetVersionStep rewrites _build.csproj's TargetFramework and global.json's sdk.version when either is behind the minimum, leaving already-current or newer values untouched. Build projects don't multi-target, so only the singular TargetFramework element is matched.

Make IMigrationStep and the migration pipeline asynchronousIMigrationStep.Execute is now Task ExecuteAsync; replaces the blocking GetAsync(...).GetAwaiter().GetResult() in ResolveFalloutVersionStep with a proper await.

Deduplicate the TFM-minimum check shared by two migration steps — extract the modern-moniker regex and minimum-check logic duplicated between BumpDotNetVersionStep and VerifyBuildTargetFrameworkStep into a shared TargetFrameworkMonikers helper.

Reorder ResolveFalloutVersionStep by invocation order and tag NuGet requests — reorder private methods to match invocation order; add a User-Agent header to the NuGet flat-container HttpClient so requests aren't sent as anonymous script traffic.

Combined effect

fallout-migrate now pins migrated references to a real, published Fallout.Common release, flags/fixes an outdated build-project TFM, confirms before mutating files, and runs its pipeline asynchronously.

@dennisdoomen dennisdoomen added enhancement New feature or request target/vCurrent Targets the current version labels Jul 18, 2026
Comment thread src/Fallout.Migrate/Steps/BumpDotNetVersionStep.cs
Comment thread src/Fallout.Migrate/Steps/ConfirmMigrationStep.cs
Comment thread src/Fallout.Migrate/Steps/ResolveFalloutVersionStep.cs Outdated
@dennisdoomen dennisdoomen changed the title Improve fallout-migrate: NuGet-scoped version pin, TFM warning, confirmation prompt Improve fallout-migrate: NuGet-scoped version pin, TFM warning/bump, confirmation prompt, async pipeline Jul 19, 2026

@ChrisonSimtian ChrisonSimtian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

some small things :-)

Comment thread src/Fallout.Migrate/Common/TargetFrameworkMonikers.cs
/// <summary>
/// The version to fall back to when the assembly carries no build-metadata suffix.
/// </summary>
private const string Fallback = "10.3.49";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

how do we remember to bump this with a new release?
Should this be more central? collection of constants somewhere grouped together as a one-stop-shop maybe?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think there is no urgent need to bump this on every release. Since this is a fallback, only used when this very assembly not carrying a version (and only the major part is used to lookup from nuget), I think once on major releases should suffice.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@ITaluone is right. And when we're shipping a major release of Fallout, we'll also ship a major release of Fallout.Migrate, which will have the v11 moniker. So, in reality, we'll never have to bump the version.


/// <inheritdoc />
public void Execute(MigrationContext context, Summary summary)
public async Task ExecuteAsync(MigrationContext context, Summary summary)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no CancellationToken?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Nah. It's a CLI that completes in seconds, so I didn't bother. Do you feel it's important?

string localVersion = ResolveFromAssembly();
int localMajor = ExtractMajor(localVersion);

string nuGetVersion = await ResolveFromNuGetAsync(localMajor);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no CancellationToken?

/// <param name="major">The major version (calendar year) to restrict the lookup to.</param>
/// <returns>The latest published stable version within <paramref name="major"/>, or <c>null</c>
/// if NuGet couldn't be reached or has no matching-major stable version.</returns>
private static async Task<string> ResolveFromNuGetAsync(int major)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no CancellationToken?

{
try
{
using HttpResponseMessage response = await httpClient.GetAsync(FlatContainerIndex);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pretty sure this one lets us pass through a token, mind having a look at that and passing it all the way through if so?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What token do you mean? The tool doesn't offer that option.

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.

CancellationToken for sure :)

But I want to see this guy who can cancel the ongoing migration 😂 (the whole migration takes less than a second)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ah, I was somehow thinking of a NuGet token of some sort. 🤦‍♂️

/// script traffic.
/// </summary>
/// <returns>A configured <see cref="HttpClient"/>.</returns>
private static HttpClient CreateHttpClient()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: can we make this a factory instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

A factory? For what?

@dennisdoomen
dennisdoomen force-pushed the migrate-ansiconsole-output branch from 38d0437 to 8affb45 Compare July 19, 2026 15:14
Base automatically changed from migrate-ansiconsole-output to main July 19, 2026 15:28
@dennisdoomen
dennisdoomen marked this pull request as ready for review July 19, 2026 18:12
@dennisdoomen
dennisdoomen requested a review from a team as a code owner July 19, 2026 18:12
Comment thread src/Fallout.Migrate/Steps/ScriptRewriter.cs

@ChrisonSimtian ChrisonSimtian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i think i forgot to greentick this last time ... sorry bit forgetful right now :-)

@ChrisonSimtian ChrisonSimtian added the Can Merge These PRs are just waiting to be merged, nothing more to do and maintainer has approved label Jul 20, 2026
dennisdoomen and others added 9 commits July 20, 2026 20:00
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ajor

ResolveFalloutVersionStep now queries NuGet's flat-container index for the
latest non-prerelease Fallout.Common release matching the running tool's
own major (calendar year), instead of only reading the tool's own
AssemblyInformationalVersion. This keeps a migration pinned to what's
actually installable without jumping to a newer, potentially breaking
yearly major the tool wasn't built against.

Falls back to the tool's own version when NuGet can't be reached or has
no matching-major stable release yet, and logs via AnsiConsole which path
was taken so the resolved version is never a silent guess.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Fallout's tooling is built and tested against .NET 10; a build project
still on an older TFM can hit tool incompatibilities that this
migration's other, purely textual rewrites won't catch.

VerifyBuildTargetFrameworkStep reads _build.csproj's TargetFramework(s)
and adds a warning for any moniker that isn't a modern net10.0+ (catching
net8.0, net48, netstandard2.0, netcoreapp3.1, etc.).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ConfirmMigrationStep runs immediately before RewriteCsprojsStep, the
first step that mutates files, and prompts via AnsiConsole.Confirm
before letting the migration proceed. Declining sets the new
Summary.Cancelled flag, which Migration.Run checks to stop executing
further steps, and MigrateCommand returns exit code 1 without printing
the (now meaningless) summary.

The prompt is skipped for --dry-run (nothing would be written) and
when stdin is redirected (CI, piped input, automated tests) since
there's nothing to prompt against.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CsprojRewriter.Rewrite had a single caller (RewriteCsprojsStep). Move its
regex fields and Rewrite method directly into the step and delete the
now-empty class. Rename the spec file/class to match.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add BumpDotNetVersionStep: rewrites _build.csproj's TargetFramework and
global.json's sdk.version to net10.0 / 10.0.100 during migration, matching
the versions Fallout's own tooling targets — but only when what's there is
behind those minimums, so an already up-to-date or newer build project is
left untouched. Build projects don't multi-target, so only the singular
TargetFramework element is matched. The minimum TFM major is parsed from
TargetFramework itself rather than duplicated as a separate constant, so
there's a single source of truth.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the blocking GetAsync(...).GetAwaiter().GetResult() in
ResolveFalloutVersionStep with a proper await. IMigrationStep.Execute
is now Task ExecuteAsync; Migration.Run is Migration.RunAsync;
MigrateCommand is now an AsyncCommand<MigrateSettings>; Program.Main
awaits CommandApp.RunAsync. ConfirmMigrationStep now awaits
ConfirmationPrompt.ShowAsync instead of the blocking AnsiConsole.Confirm.
Steps with no async work simply return Task.CompletedTask.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Extract the modern-moniker regex and IsOlderThanMinimumSupported logic
duplicated between BumpDotNetVersionStep and VerifyBuildTargetFrameworkStep
into a shared TargetFrameworkMonikers helper. VerifyBuildTargetFrameworkStep
now reads BumpDotNetVersionStep.MinimumSupportedMajor instead of
maintaining its own separate constant.

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

Reorder the private methods to match the order they're invoked from
ExecuteAsync (assembly, then major, then NuGet), and add a User-Agent
header to the NuGet flat-container HttpClient so requests aren't sent
as anonymous script traffic.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@dennisdoomen
dennisdoomen force-pushed the actual-migration-improvements branch from 7872deb to 9d0b642 Compare July 20, 2026 18:09
@dennisdoomen
dennisdoomen merged commit 161ca5f into main Jul 20, 2026
4 checks passed
@dennisdoomen
dennisdoomen deleted the actual-migration-improvements branch July 20, 2026 18:59
@dennisdoomen

Copy link
Copy Markdown
Collaborator Author

@ChrisonSimtian As you already approved the PR, I assume your comments were suggestions. If you feel they need addressing, let me know. More than fine creating another PR.

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

Labels

Can Merge These PRs are just waiting to be merged, nothing more to do and maintainer has approved enhancement New feature or request target/vCurrent Targets the current version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants