From 03e36f8882640a9e8d5d34f82752291b998deca3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:19:41 +0000 Subject: [PATCH 1/2] Initial plan From 6bd5123ad0a164310777abd09b4b7934871d23b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:40:57 +0000 Subject: [PATCH 2/2] Add warning functionality for newer tool versions during restore Co-authored-by: marcpopMSFT <12663534+marcpopMSFT@users.noreply.github.com> --- .../dotnet/Commands/CliCommandStrings.resx | 3 + .../Tool/Restore/ToolPackageRestorer.cs | 44 ++++++++++++++- .../Tool/Restore/ToolRestoreCommand.cs | 31 +++++++++-- .../Commands/xlf/CliCommandStrings.cs.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.de.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.es.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.fr.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.it.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.ja.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.ko.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.pl.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.pt-BR.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.ru.xlf | 5 ++ .../Commands/xlf/CliCommandStrings.tr.xlf | 5 ++ .../xlf/CliCommandStrings.zh-Hans.xlf | 5 ++ .../xlf/CliCommandStrings.zh-Hant.xlf | 5 ++ .../Tool/Restore/ToolRestoreCommandTests.cs | 55 +++++++++++++++++++ 17 files changed, 191 insertions(+), 7 deletions(-) diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index 16a3a06f0d6d..8703f8d1a234 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -1733,6 +1733,9 @@ The default is to publish a framework-dependent application. Tool '{0}' (version '{1}') was restored. Available commands: {2} + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + Invalid rollback definition. The manifest IDs in rollback definition {0} do not match installed manifest IDs {1}. diff --git a/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs b/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs index b1c3b3f4ed52..8ac5c500c371 100644 --- a/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs +++ b/src/Cli/dotnet/Commands/Tool/Restore/ToolPackageRestorer.cs @@ -83,6 +83,9 @@ public ToolRestoreResult InstallPackage( toolPackage.Command.Name)); } + // Check for newer versions and prepare warning message + string warning = CheckForNewerVersion(package, configFile); + return ToolRestoreResult.Success( saveToCache: (new RestoredCommandIdentifier( @@ -96,7 +99,8 @@ public ToolRestoreResult InstallPackage( CliCommandStrings.RestoreSuccessful, package.PackageId, package.Version.ToNormalizedString(), - string.Join(" ", package.CommandNames))); + string.Join(" ", package.CommandNames)), + warning: warning); } catch (ToolPackageException e) { @@ -129,6 +133,44 @@ public bool PackageHasBeenRestored( && _fileSystem.File.Exists(toolCommand.Executable.Value); } + private string CheckForNewerVersion(ToolManifestPackage package, FilePath? configFile) + { + try + { + // Use wildcard version range to get the latest version + var latestVersionRange = VersionRange.Parse("*"); + + var (latestVersion, _) = _toolPackageDownloader.GetNuGetVersion( + new PackageLocation( + nugetConfig: configFile, + additionalFeeds: _additionalSources, + sourceFeedOverrides: _overrideSources, + rootConfigDirectory: package.FirstEffectDirectory), + package.PackageId, + _verbosity, + latestVersionRange, + _restoreActionConfig); + + // Compare versions - only warn if there's a newer stable version or if the manifest uses prerelease + if (latestVersion != null && latestVersion > package.Version) + { + // If the current version is prerelease, show warning for any newer version + // If the current version is stable, only show warning for newer stable versions + if (package.Version.IsPrerelease || !latestVersion.IsPrerelease) + { + return string.Format(CliCommandStrings.RestoreNewVersionAvailable, package.PackageId, latestVersion.ToNormalizedString()); + } + } + } + catch + { + // If we can't check for newer versions, don't show a warning + // This could happen due to network issues, package source problems, etc. + } + + return string.Empty; + } + private static string JoinBySpaceWithQuote(IEnumerable objects) { return string.Join(" ", objects.Select(o => $"\"{o.ToString()}\"")); diff --git a/src/Cli/dotnet/Commands/Tool/Restore/ToolRestoreCommand.cs b/src/Cli/dotnet/Commands/Tool/Restore/ToolRestoreCommand.cs index 978bc9c1bdd7..e284100501e1 100644 --- a/src/Cli/dotnet/Commands/Tool/Restore/ToolRestoreCommand.cs +++ b/src/Cli/dotnet/Commands/Tool/Restore/ToolRestoreCommand.cs @@ -142,7 +142,14 @@ private int PrintConclusionAndReturn(ToolRestoreResult[] toolRestoreResults) { _reporter.WriteLine(); _reporter.WriteLine(string.Join(Environment.NewLine, successMessage)); - + + // Display warnings for successful restorations even in partial failure case + var warnings = toolRestoreResults.Where(r => r.IsSuccess && !string.IsNullOrEmpty(r.Warning)).Select(r => r.Warning); + if (warnings.Any()) + { + _reporter.WriteLine(); + _reporter.WriteLine(string.Join(Environment.NewLine, warnings).Yellow()); + } } _errorReporter.WriteLine(Environment.NewLine + @@ -156,6 +163,15 @@ private int PrintConclusionAndReturn(ToolRestoreResult[] toolRestoreResults) { _reporter.WriteLine(string.Join(Environment.NewLine, toolRestoreResults.Where(r => r.IsSuccess).Select(r => r.Message))); + + // Display warnings for newer versions available + var warnings = toolRestoreResults.Where(r => r.IsSuccess && !string.IsNullOrEmpty(r.Warning)).Select(r => r.Warning); + if (warnings.Any()) + { + _reporter.WriteLine(); + _reporter.WriteLine(string.Join(Environment.NewLine, warnings).Yellow()); + } + _reporter.WriteLine(); _reporter.WriteLine(CliCommandStrings.LocalToolsRestoreWasSuccessful.Green()); @@ -205,10 +221,11 @@ public struct ToolRestoreResult public (RestoredCommandIdentifier restoredCommandIdentifier, ToolCommand toolCommand)? SaveToCache { get; } public bool IsSuccess { get; } public string Message { get; } + public string Warning { get; } private ToolRestoreResult( (RestoredCommandIdentifier, ToolCommand)? saveToCache, - bool isSuccess, string message) + bool isSuccess, string message, string warning = null) { if (string.IsNullOrWhiteSpace(message)) { @@ -218,18 +235,20 @@ private ToolRestoreResult( SaveToCache = saveToCache; IsSuccess = isSuccess; Message = message; + Warning = warning; } public static ToolRestoreResult Success( (RestoredCommandIdentifier, ToolCommand)? saveToCache, - string message) + string message, + string warning = null) { - return new ToolRestoreResult(saveToCache, true, message); + return new ToolRestoreResult(saveToCache, true, message, warning); } public static ToolRestoreResult Failure(string message) { - return new ToolRestoreResult(null, false, message); + return new ToolRestoreResult(null, false, message, null); } public static ToolRestoreResult Failure( @@ -238,7 +257,7 @@ public static ToolRestoreResult Failure( { return new ToolRestoreResult(null, false, string.Format(CliCommandStrings.PackageFailedToRestore, - packageId.ToString(), toolPackageException.ToString())); + packageId.ToString(), toolPackageException.ToString()), null); } } } diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index dee9644dc2f7..c372f8124a32 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -2662,6 +2662,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Obnovení selhalo. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Obnovení bylo částečně neúspěšné. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 46892379eed8..cf26edf1285d 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -2662,6 +2662,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht.Fehler beim Wiederherstellen. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Die Wiederherstellung war nur teilweise möglich. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index bdf71f5f305c..c9ce17d2cc96 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -2662,6 +2662,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.Error en la restauración. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Error parcial de restauración. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 6c6506704c03..9aa1ea96e3be 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -2662,6 +2662,11 @@ La valeur par défaut est de publier une application dépendante du framework.Échec de la restauration. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Échec partiel de la restauration. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 2509e7812de9..03bdba18f72a 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -2662,6 +2662,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Il ripristino non è riuscito. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Il ripristino non è riuscito parzialmente. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 79883aae158c..f446eab19602 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -2662,6 +2662,11 @@ The default is to publish a framework-dependent application. 復元に失敗しました。 + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. 復元が部分的に失敗しました。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index b55b0b2942d9..58876ccd32c9 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -2662,6 +2662,11 @@ The default is to publish a framework-dependent application. 복원하지 못했습니다. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. 부분적으로 복원하지 못했습니다. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 9bf56dfbef0b..1736f470dace 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -2662,6 +2662,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Przywracanie nie powiodło się. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Przywracanie częściowo nie powiodło się. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index 867911fa2922..d402166aac33 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -2662,6 +2662,11 @@ O padrão é publicar uma aplicação dependente de framework. Falha na restauração. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Falha parcial na restauração. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 8f17ac19b058..a2f773d90aea 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -2662,6 +2662,11 @@ The default is to publish a framework-dependent application. Сбой восстановления. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Восстановление частично не выполнено. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 785c96067f6b..5ebe4bfeef8c 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -2662,6 +2662,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. Geri yükleme başarısız oldu. + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. Geri yükleme kısmen başarısız oldu. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index f6d66652c01c..6723a3deb235 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -2662,6 +2662,11 @@ The default is to publish a framework-dependent application. 还原失败。 + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. 还原部分失败。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index ee8da47791fb..4da473edaec4 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -2662,6 +2662,11 @@ The default is to publish a framework-dependent application. 還原失敗。 + + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + A newer version of tool '{0}' is available (version '{1}'). Consider updating your manifest file. + + Restore partially failed. 還原部分失敗。 diff --git a/test/dotnet.Tests/CommandTests/Tool/Restore/ToolRestoreCommandTests.cs b/test/dotnet.Tests/CommandTests/Tool/Restore/ToolRestoreCommandTests.cs index c84b9c8ee372..1029f0d10fc8 100644 --- a/test/dotnet.Tests/CommandTests/Tool/Restore/ToolRestoreCommandTests.cs +++ b/test/dotnet.Tests/CommandTests/Tool/Restore/ToolRestoreCommandTests.cs @@ -532,6 +532,61 @@ public void WhenRunWithoutManifestFileItShouldPrintSpecificRestoreErrorMessage() l.Contains(AnsiExtensions.Yellow(CliCommandStrings.NoToolsWereRestored))); } + [Fact] + public void WhenNewerVersionIsAvailableItShowsWarning() + { + var newerPackageVersion = NuGetVersion.Parse("2.1.4"); + var manifestPackage = new ToolManifestPackage( + _packageIdA, + _packageVersionA, + new[] { _toolCommandNameA }, + new DirectoryPath(_temporaryDirectory), + rollForward: false); + + // Create a mock feed with both the current version and a newer version + var feeds = new List + { + new MockFeed + { + Type = MockFeedType.FeedFromGlobalNugetConfig, + Packages = new List + { + new MockFeedPackage + { + PackageId = _packageIdA.ToString(), + Version = _packageVersionA.ToNormalizedString(), + ToolCommandName = _toolCommandNameA.Value, + }, + new MockFeedPackage + { + PackageId = _packageIdA.ToString(), + Version = newerPackageVersion.ToNormalizedString(), + ToolCommandName = _toolCommandNameA.Value, + } + } + } + }; + + var toolPackageDownloaderMockWithFeeds = new ToolPackageDownloaderMock( + _toolPackageStore, _fileSystem, feeds: feeds); + + IToolManifestFinder fakeManifestFinder = + new MockManifestFinder(new[] { manifestPackage }); + + ToolRestoreCommand toolRestoreCommand = new(_parseResult, + toolPackageDownloaderMockWithFeeds, + fakeManifestFinder, + _localToolsResolverCache, + _fileSystem, + _reporter + ); + + toolRestoreCommand.Execute().Should().Be(0); + + _reporter.Lines.Should().Contain(l => + l.Contains(string.Format(CliCommandStrings.RestoreNewVersionAvailable, _packageIdA, newerPackageVersion.ToNormalizedString()))); + } + private class MockManifestFinder : IToolManifestFinder { private readonly IReadOnlyCollection _toReturn;