diff --git a/src/Cli/dotnet/Commands/Build/BuildCommand.cs b/src/Cli/dotnet/Commands/Build/BuildCommand.cs index c97289de5f88..cde474ec73c2 100644 --- a/src/Cli/dotnet/Commands/Build/BuildCommand.cs +++ b/src/Cli/dotnet/Commands/Build/BuildCommand.cs @@ -13,7 +13,7 @@ public static class BuildCommand { public static CommandBase FromArgs(string[] args, string? msbuildPath = null) { - var parseResult = Parser.Parse(["dotnet", "build", ..args]); + var parseResult = Parser.Parse(["dotnet", "build", .. args]); return FromParseResult(parseResult, msbuildPath); } diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs index ee22ae1d3900..d1772e247a4f 100644 --- a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs +++ b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs @@ -13,7 +13,7 @@ public sealed class CleanCommand(MSBuildArgs msbuildArgs, string? msbuildPath = { public static CommandBase FromArgs(string[] args, string? msbuildPath = null) { - var result = Parser.Parse(["dotnet", "clean", ..args]); + var result = Parser.Parse(["dotnet", "clean", .. args]); return FromParseResult(result, msbuildPath); } diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index c1d47a523067..6aada0de96d0 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -1102,6 +1102,9 @@ Your project targets multiple frameworks. Specify which framework to run using ' The --solution-folder and --in-root options cannot be used together; use only one of the options. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution Folder(s) diff --git a/src/Cli/dotnet/Commands/Hidden/Complete/CompleteCommand.cs b/src/Cli/dotnet/Commands/Hidden/Complete/CompleteCommand.cs index f1b14ac9eeda..564dc3ceaedc 100644 --- a/src/Cli/dotnet/Commands/Hidden/Complete/CompleteCommand.cs +++ b/src/Cli/dotnet/Commands/Hidden/Complete/CompleteCommand.cs @@ -19,7 +19,7 @@ public static int Run(ParseResult parseResult) public static int RunWithReporter(string[] args, IReporter reporter) { - var result = Parser.Parse(["dotnet", "complete", ..args]); + var result = Parser.Parse(["dotnet", "complete", .. args]); return RunWithReporter(result, reporter); } diff --git a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs index 7bf9f15236a3..c6a6d87ff9df 100644 --- a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs +++ b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs @@ -26,7 +26,7 @@ public static int Run(ParseResult parseResult) public static void ProcessInputAndSendTelemetry(string[] args, ITelemetry telemetry) { - var result = Parser.Parse(["dotnet", "internal-reportinstallsuccess", ..args]); + var result = Parser.Parse(["dotnet", "internal-reportinstallsuccess", .. args]); ProcessInputAndSendTelemetry(result, telemetry); } diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs index ff5d7e4a6066..57a922005fc2 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs @@ -25,7 +25,7 @@ public class MSBuildCommand( { public static MSBuildCommand FromArgs(string[] args, string? msbuildPath = null) { - var result = Parser.Parse(["dotnet", "msbuild", ..args]); + var result = Parser.Parse(["dotnet", "msbuild", .. args]); return FromParseResult(result, msbuildPath); } diff --git a/src/Cli/dotnet/Commands/Pack/PackCommand.cs b/src/Cli/dotnet/Commands/Pack/PackCommand.cs index bba916eb775f..ab7d0ba0542b 100644 --- a/src/Cli/dotnet/Commands/Pack/PackCommand.cs +++ b/src/Cli/dotnet/Commands/Pack/PackCommand.cs @@ -25,7 +25,7 @@ public class PackCommand( { public static CommandBase FromArgs(string[] args, string? msbuildPath = null) { - var parseResult = Parser.Parse(["dotnet", "pack", ..args]); + var parseResult = Parser.Parse(["dotnet", "pack", .. args]); return FromParseResult(parseResult, msbuildPath); } @@ -97,14 +97,14 @@ public static int RunPackCommand(ParseResult parseResult) if (args.Count != 1) { - Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed); + Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed); return 1; } var nuspecPath = args[0]; var packArgs = new PackArgs() - { + { Logger = new NuGetConsoleLogger(), Exclude = new List(), OutputDirectory = parseResult.GetValue(definition.OutputOption), diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs index db75885e6da0..bb68394da143 100644 --- a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs +++ b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs @@ -22,7 +22,7 @@ private PublishCommand( public static CommandBase FromArgs(string[] args, string? msbuildPath = null) { - var parseResult = Parser.Parse(["dotnet", "publish", ..args]); + var parseResult = Parser.Parse(["dotnet", "publish", .. args]); return FromParseResult(parseResult); } diff --git a/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs b/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs index a79d38066986..2743d1ffe048 100644 --- a/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs +++ b/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs @@ -13,7 +13,7 @@ public static class RestoreCommand { public static CommandBase FromArgs(string[] args, string? msbuildPath = null) { - var result = Parser.Parse(["dotnet", "restore", ..args]); + var result = Parser.Parse(["dotnet", "restore", .. args]); return FromParseResult(result, msbuildPath); } diff --git a/src/Cli/dotnet/Commands/Restore/RestoringCommand.cs b/src/Cli/dotnet/Commands/Restore/RestoringCommand.cs index bf75c95e16a6..f87701357182 100644 --- a/src/Cli/dotnet/Commands/Restore/RestoringCommand.cs +++ b/src/Cli/dotnet/Commands/Restore/RestoringCommand.cs @@ -37,7 +37,7 @@ public RestoringCommand( string? msbuildPath = null, string? userProfileDir = null, bool? advertiseWorkloadUpdates = null) - : base(GetCommandArguments(msbuildArgs, noRestore), msbuildPath) + : base(GetCommandArguments(msbuildArgs, noRestore), msbuildPath) { userProfileDir = CliFolderPathCalculator.DotnetUserProfileFolderPath; Task.Run(() => WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync(userProfileDir)); @@ -118,13 +118,13 @@ private static MSBuildArgs GetCommandArguments( ReadOnlyDictionary restoreProperties = msbuildArgs.GlobalProperties? .Where(kvp => !IsPropertyExcludedFromRestore(kvp.Key))? - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase) is { } filteredList ? new(filteredList): ReadOnlyDictionary.Empty; + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase) is { } filteredList ? new(filteredList) : ReadOnlyDictionary.Empty; var restoreMSBuildArgs = MSBuildArgs.FromProperties(RestoreOptimizationProperties) .CloneWithAdditionalTargets("Restore") .CloneWithExplicitArgs([.. newArgumentsToAdd, .. existingArgumentsToForward]) .CloneWithAdditionalProperties(restoreProperties); - if (msbuildArgs.Verbosity is {} verbosity) + if (msbuildArgs.Verbosity is { } verbosity) { restoreMSBuildArgs = restoreMSBuildArgs.CloneWithVerbosity(verbosity); } @@ -171,7 +171,7 @@ private static bool HasPropertyToExcludeFromRestore(MSBuildArgs msbuildArgs) private static readonly List FlagsThatTriggerSilentSeparateRestore = [.. ComputeFlags(FlagsThatTriggerSilentRestore)]; - private static readonly List PropertiesToExcludeFromSeparateRestore = [ .. PropertiesToExcludeFromRestore ]; + private static readonly List PropertiesToExcludeFromSeparateRestore = [.. PropertiesToExcludeFromRestore]; /// /// We investigate the arguments we're about to send to a separate restore call and filter out diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index ee372bd0b269..d2804cc0111b 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -46,7 +46,7 @@ public SolutionAddCommand(ParseResult parseResult) _solutionFolderPath = parseResult.GetValue(Definition.SolutionFolderOption); _includeReferences = parseResult.GetValue(Definition.IncludeReferencesOption); SolutionArgumentValidator.ParseAndValidateArguments(_fileOrDirectory, _projects, SolutionArgumentValidator.CommandType.Add, _inRoot, _solutionFolderPath); - _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory); + _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true); } public override int Execute() @@ -59,14 +59,22 @@ public override int Execute() // Get project paths from the command line arguments PathUtility.EnsureAllPathsExist(_projects, CliStrings.CouldNotFindProjectOrDirectory, true); - IEnumerable fullProjectPaths = _projects.Select(project => + List fullProjectPaths = _projects.Select(project => { var fullPath = Path.GetFullPath(project); return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath) : fullPath; - }); + }).ToList(); - // Add projects to the solution - AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); + // Check if we're working with a solution filter file + if (_solutionFileFullPath.HasExtension(SlnfFileHelper.SlnfExtension)) + { + AddProjectsToSolutionFilter(fullProjectPaths); + } + else + { + // Add projects to the solution + AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); + } return 0; } @@ -233,4 +241,68 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio } } } + + private void AddProjectsToSolutionFilter(IEnumerable projectPaths) + { + // Solution filter files don't support --in-root or --solution-folder options + if (_inRoot || !string.IsNullOrEmpty(_solutionFolderPath)) + { + throw new GracefulException(CliCommandStrings.SolutionFilterDoesNotSupportFolderOptions); + } + + // Load the filtered solution to get the parent solution path and existing projects + SolutionModel filteredSolution = SlnFileFactory.CreateFromFilteredSolutionFile(_solutionFileFullPath); + string parentSolutionPath = filteredSolution.Description!; // The parent solution path is stored in Description + + // Load the parent solution to validate projects exist in it + SolutionModel parentSolution = SlnFileFactory.CreateFromFileOrDirectory(parentSolutionPath); + + // Get existing projects in the filter (already normalized to OS separator by CreateFromFilteredSolutionFile) + var existingProjects = filteredSolution.SolutionProjects.Select(p => p.FilePath).ToHashSet(); + + // Get solution-relative paths for new projects + var newProjects = ValidateAndGetNewProjects(projectPaths, parentSolution, parentSolutionPath, existingProjects); + + // Add new projects to the existing list and save + var allProjects = existingProjects.Concat(newProjects).OrderBy(p => p); + SlnfFileHelper.SaveSolutionFilter(_solutionFileFullPath, parentSolutionPath, allProjects); + } + + private List ValidateAndGetNewProjects( + IEnumerable projectPaths, + SolutionModel parentSolution, + string parentSolutionPath, + HashSet existingProjects) + { + var newProjects = new List(); + string parentSolutionDirectory = Path.GetDirectoryName(parentSolutionPath) ?? string.Empty; + + foreach (var projectPath in projectPaths) + { + string parentSolutionRelativePath = Path.GetRelativePath(parentSolutionDirectory, projectPath); + + // Normalize to OS separator for consistent comparison + parentSolutionRelativePath = SlnfFileHelper.NormalizePathSeparatorsToOS(parentSolutionRelativePath); + + // Check if project exists in parent solution + var projectInParent = parentSolution.FindProject(parentSolutionRelativePath); + if (projectInParent is null) + { + Reporter.Error.WriteLine(CliStrings.ProjectNotFoundInTheSolution, parentSolutionRelativePath, parentSolutionPath); + continue; + } + + // Check if project is already in the filter + if (existingProjects.Contains(parentSolutionRelativePath)) + { + Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, parentSolutionRelativePath); + continue; + } + + newProjects.Add(parentSolutionRelativePath); + Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, parentSolutionRelativePath); + } + + return newProjects; + } } diff --git a/src/Cli/dotnet/Commands/Solution/Migrate/SolutionMigrateCommand.cs b/src/Cli/dotnet/Commands/Solution/Migrate/SolutionMigrateCommand.cs index 9e32962ad7cc..47ef19bdf6dc 100644 --- a/src/Cli/dotnet/Commands/Solution/Migrate/SolutionMigrateCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Migrate/SolutionMigrateCommand.cs @@ -35,7 +35,9 @@ public override int Execute() { ConvertToSlnxAsync(slnFileFullPath, slnxFileFullPath, CancellationToken.None).Wait(); return 0; - } catch (Exception ex) { + } + catch (Exception ex) + { throw new GracefulException(ex.Message, ex); } } diff --git a/src/Cli/dotnet/Commands/Solution/Remove/SolutionRemoveCommand.cs b/src/Cli/dotnet/Commands/Solution/Remove/SolutionRemoveCommand.cs index 28a3e8c26315..8e18861ec3ac 100644 --- a/src/Cli/dotnet/Commands/Solution/Remove/SolutionRemoveCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Remove/SolutionRemoveCommand.cs @@ -27,7 +27,7 @@ public SolutionRemoveCommand(ParseResult parseResult) public override int Execute() { - string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory); + string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true); if (_projects.Count == 0) { throw new GracefulException(CliStrings.SpecifyAtLeastOneProjectToRemove); @@ -43,7 +43,15 @@ public override int Execute() ? MsbuildProject.GetProjectFileFromDirectory(p) : p)); - RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); + // Check if we're working with a solution filter file + if (solutionFileFullPath.HasExtension(SlnfFileHelper.SlnfExtension)) + { + RemoveProjectsFromSolutionFilter(solutionFileFullPath, relativeProjectPaths); + } + else + { + RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult(); + } return 0; } catch (Exception ex) when (ex is not GracefulException) @@ -124,10 +132,41 @@ private static async Task RemoveProjectsAsync(string solutionFileFullPath, IEnum { solution.RemoveFolder(folder); // After removal, adjust index and continue to avoid skipping folders after removal - i--; + i--; } } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); } + + private static void RemoveProjectsFromSolutionFilter(string slnfFileFullPath, IEnumerable projectPaths) + { + // Load the filtered solution to get the parent solution path and existing projects + SolutionModel filteredSolution = SlnFileFactory.CreateFromFilteredSolutionFile(slnfFileFullPath); + string parentSolutionPath = filteredSolution.Description!; // The parent solution path is stored in Description + + // Get existing projects in the filter + // Use case-insensitive comparer on Windows for file path comparison + var existingProjects = filteredSolution.SolutionProjects.Select(p => p.FilePath).ToHashSet(); + + // Remove specified projects + foreach (var projectPath in projectPaths) + { + // Normalize the path to be relative to parent solution + string normalizedPath = projectPath; + + // Try to find and remove the project + if (existingProjects.Remove(normalizedPath)) + { + Reporter.Output.WriteLine(CliStrings.ProjectRemovedFromTheSolution, normalizedPath); + } + else + { + Reporter.Output.WriteLine(CliStrings.ProjectNotFoundInTheSolution, normalizedPath); + } + } + + // Save updated filter + SlnfFileHelper.SaveSolutionFilter(slnfFileFullPath, parentSolutionPath, existingProjects.OrderBy(p => p)); + } } diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationActionQueue.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationActionQueue.cs index 41ee319317e7..4496703ace28 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationActionQueue.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationActionQueue.cs @@ -78,7 +78,7 @@ private async Task Read(BuildOptions buildOptions, TestOptions testOptions, Term { result = ExitCode.GenericFailure; } - + lock (_lock) { if (_aggregateExitCode is null) diff --git a/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs b/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs index 2dc2bc8b6906..8ec4d74f8564 100644 --- a/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs +++ b/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs @@ -179,7 +179,7 @@ private static int ForwardToVSTestConsole(ParseResult parseResult, string[] args public static TestCommand FromArgs(string[] args, string? testSessionCorrelationId = null, string? msbuildPath = null) { - var parseResult = Parser.Parse(["dotnet", "test", ..args]); + var parseResult = Parser.Parse(["dotnet", "test", .. args]); // settings parameters are after -- (including --), these should not be considered by the parser string[] settings = [.. args.SkipWhile(a => a != "--")]; @@ -266,7 +266,8 @@ private static TestCommand FromParseResult(ParseResult result, string[] settings Dictionary variables = VSTestForwardingApp.GetVSTestRootVariables(); - foreach (var (rootVariableName, rootValue) in variables) { + foreach (var (rootVariableName, rootValue) in variables) + { testCommand.EnvironmentVariable(rootVariableName, rootValue); VSTestTrace.SafeWriteTrace(() => $"Root variable set {rootVariableName}:{rootValue}"); } diff --git a/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs b/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs index fb81e15466f9..26a021485c97 100644 --- a/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs +++ b/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs @@ -20,7 +20,7 @@ public VSTestForwardingApp(IEnumerable argsToForward) WithEnvironmentVariable(rootVariableName, rootValue); VSTestTrace.SafeWriteTrace(() => $"Root variable set {rootVariableName}:{rootValue}"); } - + VSTestTrace.SafeWriteTrace(() => $"Forwarding to '{GetVSTestExePath()}' with args \"{argsToForward?.Aggregate((a, b) => $"{a} | {b}")}\""); } diff --git a/src/Cli/dotnet/Commands/Tool/List/ToolListJsonHelper.cs b/src/Cli/dotnet/Commands/Tool/List/ToolListJsonHelper.cs index 4f5f01726fa2..e45176ae4a1b 100644 --- a/src/Cli/dotnet/Commands/Tool/List/ToolListJsonHelper.cs +++ b/src/Cli/dotnet/Commands/Tool/List/ToolListJsonHelper.cs @@ -10,12 +10,12 @@ namespace Microsoft.DotNet.Cli.Commands.Tool.List; internal sealed class VersionedDataContract { - /// - /// The version of the JSON format for dotnet tool list. - /// + /// + /// The version of the JSON format for dotnet tool list. + /// [JsonPropertyName("version")] public int Version { get; init; } = 1; - + [JsonPropertyName("data")] public required TContract Data { get; init; } } @@ -24,10 +24,10 @@ internal class ToolListJsonContract { [JsonPropertyName("packageId")] public required string PackageId { get; init; } - + [JsonPropertyName("version")] public required string Version { get; init; } - + [JsonPropertyName("commands")] public required string[] Commands { get; init; } } diff --git a/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs b/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs index b1c3b3f4ed52..1377a97cb006 100644 --- a/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs +++ b/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs @@ -109,7 +109,7 @@ private static bool ManifestCommandMatchesActualInPackage( IReadOnlyList toolPackageCommands) { ToolCommandName[] commandsFromPackage = [.. toolPackageCommands.Select(t => t.Name)]; -return !commandsFromManifest.Any(cmd => !commandsFromPackage.Contains(cmd)) && !commandsFromPackage.Any(cmd => !commandsFromManifest.Contains(cmd)); + return !commandsFromManifest.Any(cmd => !commandsFromPackage.Contains(cmd)) && !commandsFromPackage.Any(cmd => !commandsFromManifest.Contains(cmd)); } public bool PackageHasBeenRestored( diff --git a/src/Cli/dotnet/Commands/Tool/Store/StoreCommand.cs b/src/Cli/dotnet/Commands/Tool/Store/StoreCommand.cs index 512480da7163..1345eb26f2c7 100644 --- a/src/Cli/dotnet/Commands/Tool/Store/StoreCommand.cs +++ b/src/Cli/dotnet/Commands/Tool/Store/StoreCommand.cs @@ -20,7 +20,7 @@ private StoreCommand(IEnumerable msbuildArgs, string msbuildPath = null) public static StoreCommand FromArgs(string[] args, string msbuildPath = null) { - var result = Parser.Parse(["dotnet", "store", ..args]); + var result = Parser.Parse(["dotnet", "store", .. args]); return FromParseResult(result, msbuildPath); } diff --git a/src/Cli/dotnet/Commands/Tool/Uninstall/ToolUninstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/Commands/Tool/Uninstall/ToolUninstallGlobalOrToolPathCommand.cs index b523f4d47f4d..f8a02960c380 100644 --- a/src/Cli/dotnet/Commands/Tool/Uninstall/ToolUninstallGlobalOrToolPathCommand.cs +++ b/src/Cli/dotnet/Commands/Tool/Uninstall/ToolUninstallGlobalOrToolPathCommand.cs @@ -70,7 +70,7 @@ public override int Execute() TransactionalAction.Run(() => { shellShimRepository.RemoveShim(package.Command); - + toolPackageUninstaller.Uninstall(package.PackageDirectory); }); diff --git a/src/Cli/dotnet/Commands/Tool/Update/ToolUpdateGlobalOrToolPathCommand.cs b/src/Cli/dotnet/Commands/Tool/Update/ToolUpdateGlobalOrToolPathCommand.cs index b8880fbe9e61..0afc696e73ae 100644 --- a/src/Cli/dotnet/Commands/Tool/Update/ToolUpdateGlobalOrToolPathCommand.cs +++ b/src/Cli/dotnet/Commands/Tool/Update/ToolUpdateGlobalOrToolPathCommand.cs @@ -4,12 +4,12 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.Commands.Tool.Install; +using Microsoft.DotNet.Cli.ShellShim; +using Microsoft.DotNet.Cli.ToolPackage; using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.EnvironmentAbstractions; -using Microsoft.DotNet.Cli.ToolPackage; using CreateShellShimRepository = Microsoft.DotNet.Cli.Commands.Tool.Install.CreateShellShimRepository; -using Microsoft.DotNet.Cli.ShellShim; -using Microsoft.DotNet.Cli.Commands.Tool.Install; namespace Microsoft.DotNet.Cli.Commands.Tool.Update; diff --git a/src/Cli/dotnet/Commands/Workload/History/WorkloadHistoryCommand.cs b/src/Cli/dotnet/Commands/Workload/History/WorkloadHistoryCommand.cs index b4eb5e84b389..dbc522dd4ff1 100644 --- a/src/Cli/dotnet/Commands/Workload/History/WorkloadHistoryCommand.cs +++ b/src/Cli/dotnet/Commands/Workload/History/WorkloadHistoryCommand.cs @@ -4,11 +4,11 @@ #nullable disable using System.CommandLine; +using Microsoft.Deployment.DotNet.Releases; +using Microsoft.DotNet.Cli.Commands.Workload.Install; using Microsoft.DotNet.Cli.NuGetPackageDownloader; using Microsoft.DotNet.Cli.Utils; using Microsoft.NET.Sdk.WorkloadManifestReader; -using Microsoft.Deployment.DotNet.Releases; -using Microsoft.DotNet.Cli.Commands.Workload.Install; namespace Microsoft.DotNet.Cli.Commands.Workload.History; diff --git a/src/Cli/dotnet/Commands/Workload/Restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/Commands/Workload/Restore/WorkloadRestoreCommand.cs index b605a34d3caa..8451a9de5457 100644 --- a/src/Cli/dotnet/Commands/Workload/Restore/WorkloadRestoreCommand.cs +++ b/src/Cli/dotnet/Commands/Workload/Restore/WorkloadRestoreCommand.cs @@ -63,7 +63,7 @@ public override int Execute() }); workloadInstaller.Shutdown(); - + return 0; } diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index aca54ec20616..44c673cf91c0 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -1631,6 +1631,11 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch .slnx soubor {0} byl vygenerován. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Parametry --solution-folder a --in-root nejdou použít společně; použijte jenom jeden z nich. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 553a73dc90fc..0719d287f094 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -1631,6 +1631,11 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Die SLNX-Datei "{0}" wurde generiert. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Die Optionen "--solution-folder" und "--in-root" können nicht zusammen verwendet werden; verwenden Sie nur eine der Optionen. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index 91c32ff85daa..47c50cd46876 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -1631,6 +1631,11 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa Archivo .slnx {0} generado. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Las opciones --in-root y --solution-folder no se pueden usar juntas. Utilice solo una de ellas. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 63eae5969a2f..c2eceda0a1fb 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -1631,6 +1631,11 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à Fichier .slnx {0} généré. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. N'utilisez pas en même temps les options --solution-folder et --in-root. Utilisez uniquement l'une des deux options. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 916c4b11c60e..f64197fa190d 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -1631,6 +1631,11 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire File .slnx {0} generato. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Non è possibile usare contemporaneamente le opzioni --solution-folder e --in-root. Usare una sola delle opzioni. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 0f07011cd52d..1eea643b54f1 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -1631,6 +1631,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx ファイル {0} が生成されました。 + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. --solution-folder オプションと --in-root オプションを一緒に使用することはできません。いずれかのオプションだけを使用します。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index 20b68d1a3182..7ab4346d6bd4 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -1631,6 +1631,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx 파일 {0}이(가) 생성되었습니다. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. --solution-folder와 --in-root 옵션을 함께 사용할 수 없습니다. 옵션을 하나만 사용하세요. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index dda65dfdf1e3..9d224a5f711b 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -1631,6 +1631,11 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u Wygenerowano plik .slnx {0}. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Opcji --solution-folder i --in-root nie można używać razem; użyj tylko jednej z tych opcji. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index c32d0c5c5c99..8c6a47bde7c1 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -1631,6 +1631,11 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa Arquivo .slnx {0} gerado. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. As opções --solution-folder e --in-root não podem ser usadas juntas. Use somente uma das opções. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 4d108ae99863..959a58cded78 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -1631,6 +1631,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' Файл SLNX {0} создан. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. Параметры --solution-folder и --in-root options не могут быть использованы одновременно; оставьте только один из параметров. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 5ec84a882619..55109daa7e69 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -1631,6 +1631,11 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' .slnx dosyası {0} oluşturuldu. + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. --solution-folder ve --in-root seçenekleri birlikte kullanılamaz; seçeneklerden yalnızca birini kullanın. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index f1756cdf26a6..74a02b86242e 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -1631,6 +1631,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 已生成 .slnx 文件 {0}。 + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. --solution-folder 和 --in-root 选项不能一起使用;请仅使用其中一个选项。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index e739b31721f7..29d35fb26239 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -1631,6 +1631,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 產生的 .slnx 檔案 {0}。 + + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + Solution filter files (.slnf) do not support the --in-root or --solution-folder options. + + The --solution-folder and --in-root options cannot be used together; use only one of the options. 不可同時使用 --solution-folder 和 --in-root 選項; 請只使用其中一個選項。 diff --git a/src/Cli/dotnet/DotNetCommandFactory.cs b/src/Cli/dotnet/DotNetCommandFactory.cs index ea5eb912e8f6..dcb70b05e6c9 100644 --- a/src/Cli/dotnet/DotNetCommandFactory.cs +++ b/src/Cli/dotnet/DotNetCommandFactory.cs @@ -38,7 +38,7 @@ private static bool TryGetBuiltInCommand(string commandName, out Func Parser.Invoke([commandName, ..args]); + commandFunc = (args) => Parser.Invoke([commandName, .. args]); return true; } commandFunc = null; diff --git a/src/Cli/dotnet/NugetPackageDownloader/INuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/INuGetPackageDownloader.cs index a5e54ba06bb9..0c606c61dbf7 100644 --- a/src/Cli/dotnet/NugetPackageDownloader/INuGetPackageDownloader.cs +++ b/src/Cli/dotnet/NugetPackageDownloader/INuGetPackageDownloader.cs @@ -43,4 +43,4 @@ Task GetBestPackageVersionAsync(PackageId packageId, Task<(NuGetVersion version, PackageSource source)> GetBestPackageVersionAndSourceAsync(PackageId packageId, VersionRange versionRange, PackageSourceLocation packageSourceLocation = null); -} +} diff --git a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs index a311e88c646d..a0ce16fe6d0b 100644 --- a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs +++ b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs @@ -75,7 +75,7 @@ public NuGetPackageDownloader( _retryTimer = timer; _sourceRepositories = new(); // If windows or env variable is set, verify signatures - _verifySignatures = verifySignatures && (OperatingSystem.IsWindows() ? true + _verifySignatures = verifySignatures && (OperatingSystem.IsWindows() ? true : bool.TryParse(Environment.GetEnvironmentVariable(NuGetSignatureVerificationEnabler.DotNetNuGetSignatureVerification), out var shouldVerifySignature) ? shouldVerifySignature : OperatingSystem.IsLinux()); _cacheSettings = new SourceCacheContext @@ -122,7 +122,7 @@ public async Task DownloadPackageAsync(PackageId packageId, throw new ArgumentException($"Package download folder must be specified either via {nameof(NuGetPackageDownloader)} constructor or via {nameof(downloadFolder)} method argument."); } var pathResolver = new VersionFolderPathResolver(resolvedDownloadFolder); - + string nupkgPath = pathResolver.GetPackageFilePath(packageId.ToString(), resolvedPackageVersion); Directory.CreateDirectory(Path.GetDirectoryName(nupkgPath)); diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 582eaf14d36d..7755fa463787 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -216,7 +216,8 @@ public readonly struct DependentCommandOptions(IEnumerable? slnOrProject { return projectData; } - }; + } + ; return null; } diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 788752df3cff..723195a66e36 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -91,6 +91,8 @@ public static SolutionModel CreateFromFilteredSolutionFile(string filteredSoluti { JsonElement root = JsonDocument.Parse(File.ReadAllText(filteredSolutionPath)).RootElement; originalSolutionPath = Uri.UnescapeDataString(root.GetProperty("solution").GetProperty("path").GetString()); + // Normalize path separators to OS-specific for cross-platform compatibility + originalSolutionPath = SlnfFileHelper.NormalizePathSeparatorsToOS(originalSolutionPath); filteredSolutionProjectPaths = [.. root.GetProperty("solution").GetProperty("projects").EnumerateArray().Select(p => p.GetString())]; originalSolutionPathAbsolute = Path.GetFullPath(originalSolutionPath, Path.GetDirectoryName(filteredSolutionPath)); } diff --git a/src/Cli/dotnet/SlnfFileHelper.cs b/src/Cli/dotnet/SlnfFileHelper.cs new file mode 100644 index 000000000000..87d93b75d3d5 --- /dev/null +++ b/src/Cli/dotnet/SlnfFileHelper.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli; + +/// +/// Utilities for working with solution filter (.slnf) files +/// +public static class SlnfFileHelper +{ + /// + /// File extension for solution filter files + /// + public const string SlnfExtension = ".slnf"; + + /// + /// Normalizes path separators from backslashes to the OS-specific directory separator + /// + /// The path to normalize + /// Path with OS-specific separators + public static string NormalizePathSeparatorsToOS(string path) + { + return path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar); + } + + /// + /// Normalizes path separators to backslashes (as used in .slnf files) + /// + /// The path to normalize + /// Path with backslash separators + public static string NormalizePathSeparatorsToBackslash(string path) + { + return path.Replace(Path.DirectorySeparatorChar, '\\'); + } + + private class SlnfSolution + { + [JsonPropertyName("path")] + public string Path { get; set; } + + [JsonPropertyName("projects")] + public List Projects { get; set; } = new(); + } + + private class SlnfRoot + { + [JsonPropertyName("solution")] + public SlnfSolution Solution { get; set; } = new(); + } + + /// + /// Creates a new solution filter file + /// + /// Path to the solution filter file to create + /// Path to the parent solution file + /// List of project paths to include (relative to the parent solution) + public static void CreateSolutionFilter(string slnfPath, string parentSolutionPath, IEnumerable projects = null) + { + var slnfDirectory = Path.GetDirectoryName(Path.GetFullPath(slnfPath)) ?? string.Empty; + var parentSolutionFullPath = Path.GetFullPath(parentSolutionPath, slnfDirectory); + var relativeSolutionPath = Path.GetRelativePath(slnfDirectory, parentSolutionFullPath); + + // Normalize path separators to backslashes (as per slnf format) + relativeSolutionPath = NormalizePathSeparatorsToBackslash(relativeSolutionPath); + + var root = new SlnfRoot + { + Solution = new SlnfSolution + { + Path = relativeSolutionPath, + Projects = projects?.Select(NormalizePathSeparatorsToBackslash).ToList() ?? new List() + } + }; + + var options = new JsonSerializerOptions + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.Never + }; + + var json = JsonSerializer.Serialize(root, options); + File.WriteAllText(slnfPath, json); + } + + /// + /// Saves a solution filter file with the given projects + /// + /// Path to the solution filter file + /// Path to the parent solution (stored in the slnf file) + /// List of project paths (relative to the parent solution) + public static void SaveSolutionFilter(string slnfPath, string parentSolutionPath, IEnumerable projects) + { + var slnfDirectory = Path.GetDirectoryName(Path.GetFullPath(slnfPath)) ?? string.Empty; + + // Normalize the parent solution path to be relative to the slnf file + var relativeSolutionPath = parentSolutionPath; + if (Path.IsPathRooted(parentSolutionPath)) + { + relativeSolutionPath = Path.GetRelativePath(slnfDirectory, parentSolutionPath); + } + + // Normalize path separators to backslashes (as per slnf format) + relativeSolutionPath = NormalizePathSeparatorsToBackslash(relativeSolutionPath); + + var root = new SlnfRoot + { + Solution = new SlnfSolution + { + Path = relativeSolutionPath, + Projects = projects.Select(NormalizePathSeparatorsToBackslash).ToList() + } + }; + + var options = new JsonSerializerOptions + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.Never + }; + + var json = JsonSerializer.Serialize(root, options); + File.WriteAllText(slnfPath, json); + } +} diff --git a/src/Cli/dotnet/Telemetry/DevDeviceIDGetter.cs b/src/Cli/dotnet/Telemetry/DevDeviceIDGetter.cs index 015af6723629..7960deb22cc7 100644 --- a/src/Cli/dotnet/Telemetry/DevDeviceIDGetter.cs +++ b/src/Cli/dotnet/Telemetry/DevDeviceIDGetter.cs @@ -85,11 +85,11 @@ private static void CacheDeviceId(string deviceId) // Cache device Id in Windows registry matching the OS architecture using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)) { - using(var key = baseKey.CreateSubKey(@"SOFTWARE\Microsoft\DeveloperTools")) + using (var key = baseKey.CreateSubKey(@"SOFTWARE\Microsoft\DeveloperTools")) { if (key != null) { - key.SetValue("deviceid", deviceId); + key.SetValue("deviceid", deviceId); } } } diff --git a/src/Cli/dotnet/Telemetry/Telemetry.cs b/src/Cli/dotnet/Telemetry/Telemetry.cs index d9c3a59bd8a1..38f0d1c7ca19 100644 --- a/src/Cli/dotnet/Telemetry/Telemetry.cs +++ b/src/Cli/dotnet/Telemetry/Telemetry.cs @@ -258,6 +258,6 @@ static IDictionary Combine(IDictionary { eventMeasurements[measurement.Key] = measurement.Value; } - return eventMeasurements; - } + return eventMeasurements; + } } diff --git a/src/Cli/dotnet/ToolPackage/ToolConfiguration.cs b/src/Cli/dotnet/ToolPackage/ToolConfiguration.cs index 641c8c583a7c..9da8558f5384 100644 --- a/src/Cli/dotnet/ToolPackage/ToolConfiguration.cs +++ b/src/Cli/dotnet/ToolPackage/ToolConfiguration.cs @@ -62,7 +62,7 @@ private static void EnsureNoLeadingDot(string commandName) } } - + public string CommandName { get; } public string ToolAssemblyEntryPoint { get; } diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/dotnetcli.host.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/dotnetcli.host.json new file mode 100644 index 000000000000..6bc180f304f1 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/dotnetcli.host.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "ParentSolution": { + "longName": "parent-solution", + "shortName": "s" + } + } +} diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.cs.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.cs.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.de.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.de.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.en.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.en.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.en.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.es.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.es.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.fr.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.fr.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.it.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.it.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ja.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ja.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ko.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ko.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pl.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pl.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pt-BR.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.pt-BR.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ru.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.ru.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.tr.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.tr.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hans.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hans.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hant.json new file mode 100644 index 000000000000..535e0d7b8229 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/localize/templatestrings.zh-Hant.json @@ -0,0 +1,7 @@ +{ + "author": "Microsoft", + "name": "Solution Filter File", + "description": "Create a solution filter file that references a parent solution", + "symbols/ParentSolution/displayName": "Parent solution file", + "symbols/ParentSolution/description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension)." +} \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/template.json new file mode 100644 index 000000000000..7f33956aab45 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/.template.config/template.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ + "Solution" + ], + "name": "Solution Filter File", + "generatorVersions": "[1.0.0.0-*)", + "description": "Create a solution filter file that references a parent solution", + "groupIdentity": "ItemSolutionFilter", + "precedence": "100", + "identity": "Microsoft.Standard.QuickStarts.SolutionFilter", + "shortName": [ + "slnf", + "solutionfilter" + ], + "sourceName": "SolutionFilter1", + "symbols": { + "ParentSolution": { + "type": "parameter", + "displayName": "Parent solution file", + "description": "The parent solution file (sln or slnx) that this filter references (default: same name with .slnx extension).", + "datatype": "string", + "defaultValue": "SolutionFilter1.slnx", + "replaces": "SolutionFilter1.slnx" + } + }, + "defaultName": "SolutionFilter1" +} diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/SolutionFilter1.slnf b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/SolutionFilter1.slnf new file mode 100644 index 000000000000..d633922fb216 --- /dev/null +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/SolutionFilter/SolutionFilter1.slnf @@ -0,0 +1,6 @@ +{ + "solution": { + "path": "SolutionFilter1.slnx", + "projects": [] + } +} diff --git a/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.Create_GetAllSuggestions.verified.txt b/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.Create_GetAllSuggestions.verified.txt index a6396361b7ce..2d74588d6f19 100644 --- a/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.Create_GetAllSuggestions.verified.txt +++ b/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.Create_GetAllSuggestions.verified.txt @@ -118,6 +118,13 @@ InsertText: sln, Documentation: Create an empty solution containing no projects }, + { + Label: slnf, + Kind: Value, + SortText: slnf, + InsertText: slnf, + Documentation: Create a solution filter file that references a parent solution + }, { Label: tool-manifest, Kind: Value, diff --git a/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.RootCommand_GetAllSuggestions.verified.txt b/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.RootCommand_GetAllSuggestions.verified.txt index 1afda941c784..a18e0e26302c 100644 --- a/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.RootCommand_GetAllSuggestions.verified.txt +++ b/test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/Approvals/TabCompletionTests.RootCommand_GetAllSuggestions.verified.txt @@ -118,6 +118,13 @@ InsertText: sln, Documentation: Create an empty solution containing no projects }, + { + Label: slnf, + Kind: Value, + SortText: slnf, + InsertText: slnf, + Documentation: Create a solution filter file that references a parent solution + }, { Label: tool-manifest, Kind: Value, diff --git a/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnf b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnf new file mode 100644 index 000000000000..34cef9585f66 --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnf @@ -0,0 +1,8 @@ +{ + "solution": { + "path": "App.slnx", + "projects": [ + "src\\App\\App.csproj" + ] + } +} diff --git a/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnx b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnx new file mode 100644 index 000000000000..54df61baa606 --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/App.slnx @@ -0,0 +1,5 @@ + + + + + diff --git a/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/App/App.csproj b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/App/App.csproj new file mode 100644 index 000000000000..0361aa8cd36d --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/App/App.csproj @@ -0,0 +1,6 @@ + + + Exe + net9.0 + + diff --git a/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/Lib/Lib.csproj b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/Lib/Lib.csproj new file mode 100644 index 000000000000..3043227ce00b --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/src/Lib/Lib.csproj @@ -0,0 +1,5 @@ + + + net9.0 + + diff --git a/test/TestAssets/TestProjects/TestAppWithSlnfFiles/test/AppTests/AppTests.csproj b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/test/AppTests/AppTests.csproj new file mode 100644 index 000000000000..eb91b2d48523 --- /dev/null +++ b/test/TestAssets/TestProjects/TestAppWithSlnfFiles/test/AppTests/AppTests.csproj @@ -0,0 +1,9 @@ + + + net9.0 + + + + + + diff --git a/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/Solution-Filter-File/item.slnf b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/Solution-Filter-File/item.slnf new file mode 100644 index 000000000000..e66e7dc8b150 --- /dev/null +++ b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/Solution-Filter-File/item.slnf @@ -0,0 +1,6 @@ +{ + "solution": { + "path": "Parent.slnx", + "projects": [] + } +} diff --git a/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/std-streams/stdout.txt b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/std-streams/stdout.txt new file mode 100644 index 000000000000..70cab17a4b13 --- /dev/null +++ b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#--parent-solution#Parent.slnx.verified/std-streams/stdout.txt @@ -0,0 +1 @@ +The template "%TEMPLATE_NAME%" was created successfully. \ No newline at end of file diff --git a/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/Solution-Filter-File/item.slnf b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/Solution-Filter-File/item.slnf new file mode 100644 index 000000000000..e66e7dc8b150 --- /dev/null +++ b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/Solution-Filter-File/item.slnf @@ -0,0 +1,6 @@ +{ + "solution": { + "path": "Parent.slnx", + "projects": [] + } +} diff --git a/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/std-streams/stdout.txt b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/std-streams/stdout.txt new file mode 100644 index 000000000000..70cab17a4b13 --- /dev/null +++ b/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#Solution-Filter-File#-n#item#-s#Parent.slnx.verified/std-streams/stdout.txt @@ -0,0 +1 @@ +The template "%TEMPLATE_NAME%" was created successfully. \ No newline at end of file diff --git a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Linux.verified.txt b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Linux.verified.txt index 58f8f94f7231..e8f07f91d9b2 100644 --- a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Linux.verified.txt +++ b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Linux.verified.txt @@ -25,6 +25,7 @@ proto razorclasslib razorcomponent sln +slnf tool-manifest view viewimports diff --git a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.OSX.verified.txt b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.OSX.verified.txt index 58f8f94f7231..e8f07f91d9b2 100644 --- a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.OSX.verified.txt +++ b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.OSX.verified.txt @@ -25,6 +25,7 @@ proto razorclasslib razorcomponent sln +slnf tool-manifest view viewimports diff --git a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Windows.verified.txt b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Windows.verified.txt index 6f90bdf0dd15..fdb015c97bef 100644 --- a/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Windows.verified.txt +++ b/test/dotnet-new.IntegrationTests/Approvals/DotnetNewCompleteTests.CanDoTabCompletion.Windows.verified.txt @@ -25,6 +25,7 @@ proto razorclasslib razorcomponent sln +slnf tool-manifest view viewimports diff --git a/test/dotnet-new.IntegrationTests/CommonTemplatesTests.cs b/test/dotnet-new.IntegrationTests/CommonTemplatesTests.cs index ad2d5e54b48c..af33ea66d105 100644 --- a/test/dotnet-new.IntegrationTests/CommonTemplatesTests.cs +++ b/test/dotnet-new.IntegrationTests/CommonTemplatesTests.cs @@ -46,6 +46,9 @@ public CommonTemplatesTests(SharedHomeDirectory fixture, ITestOutputHelper log) [InlineData("Solution File", "sln", new[] { "--format", "sln" })] [InlineData("Solution File", "sln", new[] { "--format", "slnx" })] [InlineData("Solution File", "solution", null)] + [InlineData("Solution Filter File", "slnf", new[] { "--parent-solution", "Parent.slnx" })] + [InlineData("Solution Filter File", "slnf", new[] { "-s", "Parent.slnx" })] + [InlineData("Solution Filter File", "solutionfilter", new[] { "--parent-solution", "Parent.slnx" })] [InlineData("Dotnet local tool manifest file", "tool-manifest", null)] [InlineData("Web Config", "webconfig", null)] [InlineData("EditorConfig file", "editorconfig", null)] diff --git a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs index 8694f54a8533..891270085e31 100644 --- a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs +++ b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs @@ -1353,5 +1353,71 @@ private string GetSolutionFileTemplateContents(string templateFileName) .Path; return File.ReadAllText(Path.Join(templateContentDirectory, templateFileName)); } + + // SLNF TESTS + [Theory] + [InlineData("sln")] + [InlineData("solution")] + public void WhenAddingProjectToSlnfItAddsOnlyIfInParentSolution(string solutionCommand) + { + var projectDirectory = _testAssetsManager + .CopyTestAsset("TestAppWithSlnfFiles", identifier: $"GivenDotnetSlnAdd-Slnf-{solutionCommand}") + .WithSource() + .Path; + + var slnfFullPath = Path.Combine(projectDirectory, "App.slnf"); + + // Try to add Lib project which is in parent solution + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(solutionCommand, "App.slnf", "add", Path.Combine("src", "Lib", "Lib.csproj")); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain(string.Format(CliStrings.ProjectAddedToTheSolution, Path.Combine("src", "Lib", "Lib.csproj"))); + + // Verify the project was added to the slnf file + var slnfContent = File.ReadAllText(slnfFullPath); + slnfContent.Should().Contain("src\\\\Lib\\\\Lib.csproj"); + } + + [Theory] + [InlineData("sln")] + [InlineData("solution")] + public void WhenRemovingProjectFromSlnfItRemovesSuccessfully(string solutionCommand) + { + var projectDirectory = _testAssetsManager + .CopyTestAsset("TestAppWithSlnfFiles", identifier: $"GivenDotnetSlnAdd-SlnfRemove-{solutionCommand}") + .WithSource() + .Path; + + var slnfFullPath = Path.Combine(projectDirectory, "App.slnf"); + + // Remove the App project from the filter + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(solutionCommand, "App.slnf", "remove", Path.Combine("src", "App", "App.csproj")); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain(string.Format(CliStrings.ProjectRemovedFromTheSolution, Path.Combine("src", "App", "App.csproj"))); + + // Verify the project was removed from the slnf file + var slnfContent = File.ReadAllText(slnfFullPath); + slnfContent.Should().NotContain("src\\\\App\\\\App.csproj"); + } + + [Theory] + [InlineData("sln")] + [InlineData("solution")] + public void WhenAddingProjectToSlnfWithInRootOptionItErrors(string solutionCommand) + { + var projectDirectory = _testAssetsManager + .CopyTestAsset("TestAppWithSlnfFiles", identifier: $"GivenDotnetSlnAdd-SlnfInRoot-{solutionCommand}") + .WithSource() + .Path; + + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(solutionCommand, "App.slnf", "add", "--in-root", Path.Combine("src", "Lib", "Lib.csproj")); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain(CliCommandStrings.SolutionFilterDoesNotSupportFolderOptions); + } } }