From 23e1525a570ff6f0817f6fd0a70b27fc0c1bd786 Mon Sep 17 00:00:00 2001 From: Alexander Nachtmann Date: Sat, 3 Oct 2026 22:00:54 +0200 Subject: [PATCH 1/4] Rename the IHaz* component interfaces to IHas* in fallout-migrate Co-Authored-By: Claude Fable 5.1 --- .../Steps/RewriteCsFilesStep.cs | 17 ++++++++- .../RewriteCsFilesStepSpecs.cs | 35 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/Fallout.Migrate/Steps/RewriteCsFilesStep.cs b/src/Fallout.Migrate/Steps/RewriteCsFilesStep.cs index 2b6064b12..dd6ad9315 100644 --- a/src/Fallout.Migrate/Steps/RewriteCsFilesStep.cs +++ b/src/Fallout.Migrate/Steps/RewriteCsFilesStep.cs @@ -7,7 +7,8 @@ namespace Fallout.Migrate.Steps; /// /// Rewrites every *.cs file under the repository root: Nuke.* namespace prefixes become /// Fallout., the bare NukeBuild/INukeBuild types become -/// FalloutBuild/IFalloutBuild, and the solution-model namespace (which moved out of +/// FalloutBuild/IFalloutBuild, the IHaz* component interfaces become +/// IHas*, and the solution-model namespace (which moved out of /// *.Common.ProjectModel in v11) becomes Fallout.Solutions. /// internal sealed class RewriteCsFilesStep : IMigrationStep @@ -32,6 +33,14 @@ internal sealed class RewriteCsFilesStep : IMigrationStep private static readonly Regex nukeBuildType = new(@"\bNukeBuild\b", RegexOptions.Compiled); private static readonly Regex iNukeBuildType = new(@"\bINukeBuild\b", RegexOptions.Compiled); + // NUKE's IHaz* component interfaces are IHas* in Fallout.Components. The [Obsolete] + // IHaz* aliases ship only in the Nuke.Components shim, which the namespace swap above + // moves the code away from. Limited to the shipped names, so a user-defined IHaz* + // interface keeps its name. + private static readonly Regex iHazComponentType = new( + @"\bIHaz(?=(?:Artifacts|Changelog|Configuration|GitRepository|GitVersion|NerdbankGitVersioning|Reports|Solution|TwitterCredentials)\b)", + RegexOptions.Compiled); + /// public Task ExecuteAsync(MigrationContext context, Summary summary) { @@ -77,6 +86,12 @@ private static RewriteResult Rewrite(string original) return "FalloutBuild"; }); + content = iHazComponentType.Replace(content, _ => + { + edits++; + return "IHas"; + }); + return new RewriteResult(content, edits); } } diff --git a/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs b/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs index 8cc688983..92f80f206 100644 --- a/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs +++ b/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs @@ -157,4 +157,39 @@ public async Task ProjectModel_as_part_of_another_identifier_is_not_truncated() var buildCs = (tempDirectory / "Build.cs").ReadAllText().Trim(); buildCs.Should().Be("using Fallout.Common.ProjectModelFoo;"); } + + [Fact] + public async Task IHaz_component_interfaces_are_renamed_to_IHas() + { + // Arrange + (tempDirectory / "Build.cs").WriteAllText(""" + interface IBuildPaths : IHazSolution, IHazArtifacts; + var solution = From().Solution; + """); + + // Act + await new RewriteCsFilesStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(3); + var buildCs = (tempDirectory / "Build.cs").ReadAllText(); + buildCs.Should().Contain("IBuildPaths : IHasSolution, IHasArtifacts;"); + buildCs.Should().Contain("From()"); + buildCs.Should().NotContain("IHaz"); + } + + [Fact] + public async Task User_defined_IHaz_interfaces_keep_their_name() + { + // Arrange + (tempDirectory / "Build.cs").WriteAllText("interface IHazSourcePaths : IHazSolution;"); + + // Act + await new RewriteCsFilesStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(1); + var buildCs = (tempDirectory / "Build.cs").ReadAllText().Trim(); + buildCs.Should().Be("interface IHazSourcePaths : IHasSolution;"); + } } From 7aa9ec89e8a621de75a4bdf42b8e659373e5e247 Mon Sep 17 00:00:00 2001 From: Alexander Nachtmann Date: Sat, 3 Oct 2026 22:00:58 +0200 Subject: [PATCH 2/4] Rewrite bootstrap scripts outside the repository root in fallout-migrate Co-Authored-By: Claude Fable 5.1 --- .../Steps/CleanupBootstrapScriptsStep.cs | 13 ++-- .../Steps/RewriteBootstrapScriptsStep.cs | 6 +- .../RewriteBootstrapScriptsStepSpecs.cs | 71 +++++++++++++++++++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs b/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs index a3fc79bdc..bb767f552 100644 --- a/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs +++ b/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Fallout.Common.IO; using Fallout.Migrate.Common; namespace Fallout.Migrate.Steps; @@ -15,14 +14,14 @@ internal partial class CleanupBootstrapScriptsStep : IMigrationStep { public Task ExecuteAsync(MigrationContext context, Summary summary) { - foreach (var file in new[] + // The scripts can sit in a subdirectory (NukeScriptDirectory), so search the whole tree. + foreach (var name in new[] { "build.sh", "build.ps1" }) { - var path = context.RootDirectory / file; - if (path.FileExists()) + foreach (var path in MigrationFileOperations.EnumerateFiles(context.RootDirectory, name)) { MigrationFileOperations.ApplyRewrite(context, path, Cleanup, summary); } @@ -55,6 +54,12 @@ private static RewriteResult Cleanup(string content) var endOfIfBlock = lines.FindIndex(indexOfEnterpriseEnvVarCheck, line => line.Trim() == "}" || line.Trim() == "fi"); + // No closing `fi`/`}` after the token: not the generated block, so leave the file alone. + if (endOfIfBlock < 0) + { + return new RewriteResult(content, 0); + } + // this is "just" to remove the empty line after the if block if (lines.Count > endOfIfBlock + 1 && lines[endOfIfBlock + 1].Trim() == "") { diff --git a/src/Fallout.Migrate/Steps/RewriteBootstrapScriptsStep.cs b/src/Fallout.Migrate/Steps/RewriteBootstrapScriptsStep.cs index 741b118bc..c0bfc01bd 100644 --- a/src/Fallout.Migrate/Steps/RewriteBootstrapScriptsStep.cs +++ b/src/Fallout.Migrate/Steps/RewriteBootstrapScriptsStep.cs @@ -1,6 +1,5 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; -using Fallout.Common.IO; using Fallout.Migrate.Common; namespace Fallout.Migrate.Steps; @@ -33,6 +32,8 @@ private static readonly (Regex Pattern, string Replacement)[] patterns = /// public Task ExecuteAsync(MigrationContext context, Summary summary) { + // The scripts can sit in a subdirectory (NukeScriptDirectory), so search the whole tree. + // The patterns only match NUKE-specific text, and a script without any is not written. foreach (var name in new[] { "build.cmd", @@ -40,8 +41,7 @@ public Task ExecuteAsync(MigrationContext context, Summary summary) "build.sh" }) { - var path = context.RootDirectory / name; - if (path.FileExists()) + foreach (var path in MigrationFileOperations.EnumerateFiles(context.RootDirectory, name)) { MigrationFileOperations.ApplyRewrite(context, path, Rewrite, summary); } diff --git a/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs b/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs index b437f0995..56782efd3 100644 --- a/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs +++ b/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs @@ -196,4 +196,75 @@ public async Task Leaves_bootstrapper_scripts_without_leftovers_alone() "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" """); } + + [Fact] + public async Task Bootstrap_scripts_in_a_subdirectory_are_rewritten() + { + // Arrange + (tempDirectory / "eng" / "build.sh").WriteAllText(""" + TEMP_DIRECTORY="$SCRIPT_DIR/../.nuke/temp" + dotnet nuke "$@" + """, eofLineBreak: false); + + // Act + await new RewriteBootstrapScriptsStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(2); + var buildSh = (tempDirectory / "eng" / "build.sh").ReadAllText(); + buildSh.Should().Contain("/.fallout/temp"); + buildSh.Should().Contain("dotnet fallout"); + } + + [Fact] + public async Task Unrelated_build_scripts_are_left_untouched() + { + // Arrange + var original = "#!/bin/sh\nmake all\n"; + (tempDirectory / "tools" / "build.sh").WriteAllText(original, eofLineBreak: false); + + // Act + await new RewriteBootstrapScriptsStep().ExecuteAsync(context, summary); + await new CleanupBootstrapScriptsStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(0); + (tempDirectory / "tools" / "build.sh").ReadAllText().Should().Be(original); + } + + [Fact] + public async Task Enterprise_feed_block_in_a_subdirectory_script_is_removed() + { + // Arrange + (tempDirectory / "eng" / "build.ps1").WriteAllText(""" + if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { + & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null + } + + ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } + """, eofLineBreak: false); + + // Act + await new CleanupBootstrapScriptsStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(1); + (tempDirectory / "eng" / "build.ps1").ReadAllText().Should().Be( + "ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }"); + } + + [Fact] + public async Task Enterprise_token_without_a_closing_block_is_left_alone() + { + // Arrange + var original = "export NUKE_ENTERPRISE_TOKEN=\"$1\"\ndotnet nuke \"$@\""; + (tempDirectory / "build.sh").WriteAllText(original, eofLineBreak: false); + + // Act + await new CleanupBootstrapScriptsStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(0); + (tempDirectory / "build.sh").ReadAllText().Should().Be(original); + } } From 4deb8d932686e7d90fa319049d1595b0e83b8349 Mon Sep 17 00:00:00 2001 From: Alexander Nachtmann Date: Sat, 3 Oct 2026 22:01:03 +0200 Subject: [PATCH 3/4] Rename the NukeBuild definition in the moved build.schema.json Co-Authored-By: Claude Fable 5.1 --- .../Steps/RenameNukeDirectoryStep.cs | 34 ++++++- .../RenameNukeDirectoryStepSpecs.cs | 90 +++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs diff --git a/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs b/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs index a473895ea..b0e7c7cf2 100644 --- a/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs +++ b/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Fallout.Common.IO; using Fallout.Migrate.Common; @@ -7,16 +8,30 @@ namespace Fallout.Migrate.Steps; /// /// Renames the repository's .nuke/ directory to .fallout/, or records a warning if -/// both already exist and need a manual merge. +/// both already exist and need a manual merge. The build.schema.json inside is rewritten +/// first, so the moved file names its base definition FalloutBuild. /// internal sealed class RenameNukeDirectoryStep : IMigrationStep { + // NUKE's schema has the layout Fallout reads; only the base definition is named + // NukeBuild. CompletionUtility (fallout completion and :secrets) looks up FalloutBuild, + // so rename the definition key and its $ref. The next build rewrites the whole file. + private static readonly Regex nukeBuildDefinition = + new(@"(?<=""|#/definitions/)NukeBuild(?="")", RegexOptions.Compiled); + /// public Task ExecuteAsync(MigrationContext context, Summary summary) { var legacy = context.RootDirectory / ".nuke"; var canonical = context.RootDirectory / ".fallout"; + // Also repairs a repository that an earlier fallout-migrate moved without rewriting. + var schema = (legacy.DirectoryExists() ? legacy : canonical) / "build.schema.json"; + if (schema.FileExists()) + { + MigrationFileOperations.ApplyRewrite(context, schema, RewriteSchema, summary); + } + if (!legacy.DirectoryExists()) { return Task.CompletedTask; @@ -43,4 +58,21 @@ public Task ExecuteAsync(MigrationContext context, Summary summary) summary.DirectoriesRenamed++; return Task.CompletedTask; } + + /// + /// Renames the NukeBuild definition and its $ref in . + /// + /// The original build.schema.json content. + /// The rewritten content and the number of edits made. + private static RewriteResult RewriteSchema(string original) + { + var edits = 0; + var content = nukeBuildDefinition.Replace(original, _ => + { + edits++; + return "FalloutBuild"; + }); + + return new RewriteResult(content, edits); + } } diff --git a/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs b/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs new file mode 100644 index 000000000..234e20f81 --- /dev/null +++ b/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Fallout.Common.IO; +using Fallout.Migrate.Common; +using Fallout.Migrate.Steps; +using FluentAssertions; +using Xunit; + +namespace Fallout.Migrate.Specs; + +public class RenameNukeDirectoryStepSpecs : IDisposable +{ + private const string NukeSchema = """ + { + "definitions": { + "Host": { "type": "string" }, + "NukeBuild": { "properties": { "Verbosity": { "type": "string" } } } + }, + "allOf": [ + { "properties": { "Configuration": { "type": "string" } } }, + { "$ref": "#/definitions/NukeBuild" } + ] + } + """; + + private readonly AbsolutePath tempDirectory; + private readonly Summary summary = new(); + + public RenameNukeDirectoryStepSpecs() + { + tempDirectory = AbsolutePath.Temp("fallout-migrate-test"); + } + + public void Dispose() + { + tempDirectory.DeleteDirectory(); + } + + [Fact] + public async Task Moved_schema_names_its_base_definition_FalloutBuild() + { + // Arrange + (tempDirectory / ".nuke" / "build.schema.json").WriteAllText(NukeSchema, eofLineBreak: false); + var context = new MigrationContext(tempDirectory, dryRun: false, TextWriter.Null); + + // Act + await new RenameNukeDirectoryStep().ExecuteAsync(context, summary); + + // Assert + summary.DirectoriesRenamed.Should().Be(1); + summary.EditCount.Should().Be(2); + var schema = (tempDirectory / ".fallout" / "build.schema.json").ReadAllText(); + schema.Should().Contain("\"FalloutBuild\": {"); + schema.Should().Contain("\"$ref\": \"#/definitions/FalloutBuild\""); + schema.Should().NotContain("NukeBuild"); + } + + [Fact] + public async Task Schema_of_an_already_migrated_repo_is_repaired() + { + // Arrange + (tempDirectory / ".fallout" / "build.schema.json").WriteAllText(NukeSchema, eofLineBreak: false); + var context = new MigrationContext(tempDirectory, dryRun: false, TextWriter.Null); + + // Act + await new RenameNukeDirectoryStep().ExecuteAsync(context, summary); + + // Assert + summary.DirectoriesRenamed.Should().Be(0); + summary.EditCount.Should().Be(2); + (tempDirectory / ".fallout" / "build.schema.json").ReadAllText().Should().NotContain("NukeBuild"); + } + + [Fact] + public async Task Dry_run_leaves_the_schema_unchanged() + { + // Arrange + (tempDirectory / ".nuke" / "build.schema.json").WriteAllText(NukeSchema, eofLineBreak: false); + var context = new MigrationContext(tempDirectory, dryRun: true, TextWriter.Null); + + // Act + await new RenameNukeDirectoryStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(2); + (tempDirectory / ".nuke" / "build.schema.json").ReadAllText().Should().Be(NukeSchema); + (tempDirectory / ".fallout").DirectoryExists().Should().BeFalse(); + } +} From 910fdd892f956e5b8f84f1a26c0def91b5cb4a27 Mon Sep 17 00:00:00 2001 From: Alexander Nachtmann Date: Thu, 8 Oct 2026 10:56:10 +0200 Subject: [PATCH 4/4] Remove the enterprise token block only when it starts with the if line in fallout-migrate Review feedback on the IHaz*, bootstrap script and schema changes: - Leave a script alone when the first NUKE_ENTERPRISE_TOKEN line is not the generated `if` line. Before, an `export` line was removed together with everything up to an unrelated `fi` or `}` later in the file. - When both .nuke/ and .fallout/ exist, rewrite only the .fallout/ schema. The .nuke/ schema is left for the manual merge. - Add a spec that every IHaz* alias in the Nuke.Components shim is renamed, so a new alias that the hard-coded list misses fails a test. - Explain why the broad NukeBuild match in the schema is safe. Co-Authored-By: Claude Opus 5.5 --- .../Steps/CleanupBootstrapScriptsStep.cs | 10 ++++++++ .../Steps/RenameNukeDirectoryStep.cs | 9 +++++-- .../RenameNukeDirectoryStepSpecs.cs | 19 ++++++++++++++ .../RewriteBootstrapScriptsStepSpecs.cs | 15 +++++++++++ .../RewriteCsFilesStepSpecs.cs | 25 +++++++++++++++++++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs b/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs index bb767f552..3b8faaa6b 100644 --- a/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs +++ b/src/Fallout.Migrate/Steps/CleanupBootstrapScriptsStep.cs @@ -49,6 +49,16 @@ private static RewriteResult Cleanup(string content) // here, we get the index of the line that contains the environment variable check var indexOfEnterpriseEnvVarCheck = lines.FindIndex(line => line.Contains(envVarToCheck)); + // The generated block starts with the `if` line that checks the token. Any other first use, + // for example `export NUKE_ENTERPRISE_TOKEN=...`, is not the generated block, so leave the + // file alone. Otherwise an unrelated `fi`/`}` later in the file would end the removal. + var checkLine = lines[indexOfEnterpriseEnvVarCheck].TrimStart(); + if (!checkLine.StartsWith("if ", StringComparison.OrdinalIgnoreCase) && + !checkLine.StartsWith("if(", StringComparison.OrdinalIgnoreCase)) + { + return new RewriteResult(content, 0); + } + // here, we get the index of the line that ends the if block // which is fi on bash and a simple "}" in powershell var endOfIfBlock = lines.FindIndex(indexOfEnterpriseEnvVarCheck, diff --git a/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs b/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs index b0e7c7cf2..2f8e43c2b 100644 --- a/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs +++ b/src/Fallout.Migrate/Steps/RenameNukeDirectoryStep.cs @@ -16,6 +16,8 @@ internal sealed class RenameNukeDirectoryStep : IMigrationStep // NUKE's schema has the layout Fallout reads; only the base definition is named // NukeBuild. CompletionUtility (fallout completion and :secrets) looks up FalloutBuild, // so rename the definition key and its $ref. The next build rewrites the whole file. + // The lookbehind matches any quoted "NukeBuild", not only these two. That is safe because + // the schema is machine-generated, and NukeBuild appears only as the key and in the $ref. private static readonly Regex nukeBuildDefinition = new(@"(?<=""|#/definitions/)NukeBuild(?="")", RegexOptions.Compiled); @@ -25,8 +27,11 @@ public Task ExecuteAsync(MigrationContext context, Summary summary) var legacy = context.RootDirectory / ".nuke"; var canonical = context.RootDirectory / ".fallout"; - // Also repairs a repository that an earlier fallout-migrate moved without rewriting. - var schema = (legacy.DirectoryExists() ? legacy : canonical) / "build.schema.json"; + // Rewrite the schema in the directory that ends up as .fallout/: .nuke/ when it is moved, + // otherwise .fallout/. When both exist, .nuke/ is left for the manual merge. This also + // repairs a repository that an earlier fallout-migrate moved without rewriting. + var moveLegacy = legacy.DirectoryExists() && !canonical.DirectoryExists(); + var schema = (moveLegacy ? legacy : canonical) / "build.schema.json"; if (schema.FileExists()) { MigrationFileOperations.ApplyRewrite(context, schema, RewriteSchema, summary); diff --git a/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs b/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs index 234e20f81..21278365d 100644 --- a/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs +++ b/tests/Fallout.Migrate.Specs/RenameNukeDirectoryStepSpecs.cs @@ -72,6 +72,25 @@ public async Task Schema_of_an_already_migrated_repo_is_repaired() (tempDirectory / ".fallout" / "build.schema.json").ReadAllText().Should().NotContain("NukeBuild"); } + [Fact] + public async Task Only_the_fallout_schema_is_rewritten_when_both_directories_exist() + { + // Arrange + (tempDirectory / ".nuke" / "build.schema.json").WriteAllText(NukeSchema, eofLineBreak: false); + (tempDirectory / ".fallout" / "build.schema.json").WriteAllText(NukeSchema, eofLineBreak: false); + var context = new MigrationContext(tempDirectory, dryRun: false, TextWriter.Null); + + // Act + await new RenameNukeDirectoryStep().ExecuteAsync(context, summary); + + // Assert + summary.DirectoriesRenamed.Should().Be(0); + summary.EditCount.Should().Be(2); + summary.Warnings.Should().ContainSingle().Which.Should().Contain("merge their contents manually"); + (tempDirectory / ".fallout" / "build.schema.json").ReadAllText().Should().NotContain("NukeBuild"); + (tempDirectory / ".nuke" / "build.schema.json").ReadAllText().Should().Be(NukeSchema); + } + [Fact] public async Task Dry_run_leaves_the_schema_unchanged() { diff --git a/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs b/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs index 56782efd3..57247ed40 100644 --- a/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs +++ b/tests/Fallout.Migrate.Specs/RewriteBootstrapScriptsStepSpecs.cs @@ -267,4 +267,19 @@ public async Task Enterprise_token_without_a_closing_block_is_left_alone() summary.EditCount.Should().Be(0); (tempDirectory / "build.sh").ReadAllText().Should().Be(original); } + + [Fact] + public async Task Enterprise_token_outside_an_if_line_is_left_alone() + { + // Arrange + var original = "export NUKE_ENTERPRISE_TOKEN=\"$1\"\necho \"start\"\nif [ -f x ]; then echo y; fi\ndotnet nuke \"$@\""; + (tempDirectory / "build.sh").WriteAllText(original, eofLineBreak: false); + + // Act + await new CleanupBootstrapScriptsStep().ExecuteAsync(context, summary); + + // Assert + summary.EditCount.Should().Be(0); + (tempDirectory / "build.sh").ReadAllText().Should().Be(original); + } } diff --git a/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs b/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs index 92f80f206..86e56c0f5 100644 --- a/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs +++ b/tests/Fallout.Migrate.Specs/RewriteCsFilesStepSpecs.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Fallout.Common.IO; using Fallout.Migrate.Common; @@ -192,4 +194,27 @@ public async Task User_defined_IHaz_interfaces_keep_their_name() var buildCs = (tempDirectory / "Build.cs").ReadAllText().Trim(); buildCs.Should().Be("interface IHazSourcePaths : IHasSolution;"); } + + [Fact] + public async Task Every_IHaz_alias_in_the_Nuke_Components_shim_is_renamed() + { + // The rename list is hard-coded. This spec fails when the shim gains an IHaz* alias + // that the list doesn't have. + // Arrange + var shimDirectory = AbsolutePath.Create(AppContext.BaseDirectory) + .FindParentOrSelf(x => (x / "fallout.slnx").FileExists()) / "src" / "Shims" / "Nuke.Components"; + var aliases = shimDirectory.GetFiles("*.cs") + .SelectMany(x => Regex.Matches(x.ReadAllText(), @"\binterface\s+(?IHaz\w+)")) + .Select(x => x.Groups["name"].Value) + .ToList(); + (tempDirectory / "Build.cs").WriteAllText(string.Join("\n", aliases), eofLineBreak: false); + + // Act + await new RewriteCsFilesStep().ExecuteAsync(context, summary); + + // Assert + aliases.Should().NotBeEmpty(); + (tempDirectory / "Build.cs").ReadAllText() + .Should().Be(string.Join("\n", aliases.Select(x => "IHas" + x["IHaz".Length..]))); + } }