diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs index 9c750eeb079..b50bcaf7138 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs @@ -903,6 +903,175 @@ await TestUpdateForProject("Newtonsoft.Json", "7.0.1", "13.0.1", """); } + [Fact] + public async Task PackagesConfigUpdateIsNotThwartedBy_VSToolsPath_PropertyBeingSetInUserCode() + { + await TestUpdateForProject("Newtonsoft.Json", "7.0.1", "13.0.1", + projectContents: """ + + + Debug + AnyCPU + + + 2.0 + 68ed3303-52a0-47b8-a687-3abbb07530da + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + TestProject + TestProject + v4.5 + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + C:\some\path\that\does\not\exist + + + + + + """, + packagesConfigContents: """ + + + + """, + expectedProjectContents: """ + + + Debug + AnyCPU + + + 2.0 + 68ed3303-52a0-47b8-a687-3abbb07530da + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + TestProject + TestProject + v4.5 + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + C:\some\path\that\does\not\exist + + + + + + """, + expectedPackagesConfigContents: """ + + + + + """); + } + protected static async Task TestUpdateForProject( string dependencyName, string oldVersion, diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs index 428cc3451d5..acdb37c3940 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs @@ -7,6 +7,7 @@ using System.Xml.Linq; using Microsoft.Language.Xml; +using NuGetUpdater.Core.Updater; namespace NuGetUpdater.Core; @@ -56,7 +57,10 @@ public static async Task UpdateDependencyAsync(string repoRootPath, string proje args.Add(msbuildDirectory); // e.g., /usr/share/dotnet/sdk/7.0.203 } - RunNuget(args, packagesDirectory, logger); + using (new WebApplicationTargetsConditionPatcher(projectPath)) + { + RunNuget(args, packagesDirectory, logger); + } projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath); projectBuildFile.NormalizeDirectorySeparatorsInProject(); diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs new file mode 100644 index 00000000000..a04746567ef --- /dev/null +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Linq; + +using Microsoft.Language.Xml; + +namespace NuGetUpdater.Core.Updater +{ + internal class WebApplicationTargetsConditionPatcher : IDisposable + { + private string? _capturedCondition; + private readonly XmlFilePreAndPostProcessor _processor; + + public WebApplicationTargetsConditionPatcher(string projectFilePath) + { + _processor = new XmlFilePreAndPostProcessor( + getContent: () => File.ReadAllText(projectFilePath), + setContent: s => File.WriteAllText(projectFilePath, s), + nodeFinder: doc => doc.Descendants() + .FirstOrDefault(e => e.Name == "Import" && e.GetAttributeValue("Project") == @"$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets") + as XmlNodeSyntax, + preProcessor: n => + { + var element = (IXmlElementSyntax)n; + _capturedCondition = element.GetAttributeValue("Condition"); + return (XmlNodeSyntax)element.RemoveAttributeByName("Condition").WithAttribute("Condition", "false"); + }, + postProcessor: n => + { + var element = (IXmlElementSyntax)n; + var newElement = element.RemoveAttributeByName("Condition"); + if (_capturedCondition is not null) + { + newElement = newElement.WithAttribute("Condition", _capturedCondition); + } + + return (XmlNodeSyntax)newElement; + } + ); + } + + public void Dispose() + { + _processor.Dispose(); + } + } +} diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs new file mode 100644 index 00000000000..a728a919a53 --- /dev/null +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs @@ -0,0 +1,55 @@ +using System; + +using Microsoft.Language.Xml; + +namespace NuGetUpdater.Core.Updater +{ + internal class XmlFilePreAndPostProcessor : IDisposable + { + public Func GetContent { get; } + public Action SetContent { get; } + public Func NodeFinder { get; } + public Func PreProcessor { get; } + public Func PostProcessor { get; } + + public XmlFilePreAndPostProcessor(Func getContent, Action setContent, Func nodeFinder, Func preProcessor, Func postProcessor) + { + GetContent = getContent; + SetContent = setContent; + NodeFinder = nodeFinder; + PreProcessor = preProcessor; + PostProcessor = postProcessor; + PreProcess(); + } + + public void Dispose() + { + PostProcess(); + } + + private void PreProcess() => RunProcessor(PreProcessor); + + private void PostProcess() => RunProcessor(PostProcessor); + + private void RunProcessor(Func processor) + { + var content = GetContent(); + var xml = Parser.ParseText(content); + if (xml is null) + { + return; + } + + var node = NodeFinder(xml); + if (node is null) + { + return; + } + + var replacementElement = processor(node); + var replacementXml = xml.ReplaceNode(node, replacementElement); + var replacementString = replacementXml.ToFullString(); + SetContent(replacementString); + } + } +} diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs index e31bb65b943..1a38affa7d7 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs @@ -30,6 +30,17 @@ public static IEnumerable GetElements(this IXmlElementSyntax return element.Attributes.FirstOrDefault(a => a.Name.Equals(name, comparisonType)); } + public static IXmlElementSyntax RemoveAttributeByName(this IXmlElementSyntax element, string attributeName, StringComparison comparisonType = StringComparison.Ordinal) + { + var attribute = element.GetAttribute(attributeName, comparisonType); + if (attribute is null) + { + return element; + } + + return element.RemoveAttribute(attribute); + } + public static string GetAttributeValue(this IXmlElementSyntax element, string name, StringComparison comparisonType) { return element.Attributes.First(a => a.Name.Equals(name, comparisonType)).Value;