This repository has been archived by the owner on Jul 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Failure to update .NET AspNet WebApi projects (#242)
* Patch WebApiProjects with a Condition on Import This allows the dotnet remove/add commands to work without trying to import a non-existent project * Follow project references when updating import conditions * Undo unnecessary change * Specify solution file explicitly in build This might help the random build errors, according to dotnet/sdk#2076
- Loading branch information
Showing
11 changed files
with
223 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,5 +41,4 @@ _ReSharper*/ | |
|
||
# Custom | ||
NuKeeper/Properties/launchSettings.json | ||
|
||
|
||
*.orig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
NuKeeper.Integration.Tests/NuGet/Process/UpdateProjectImportsCommandTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using NuKeeper.Inspection.RepositoryInspection; | ||
using NuKeeper.NuGet.Process; | ||
using NUnit.Framework; | ||
|
||
namespace NuKeeper.Integration.Tests.NuGet.Process | ||
{ | ||
[TestFixture] | ||
public class UpdateProjectImportsCommandTests | ||
{ | ||
private readonly string _testWebApiProject = | ||
@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""> | ||
<Import Project=""$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"" Condition=""Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"" /> | ||
<Import Project=""$(MSBuildBinPath)\Microsoft.CSharp.targets"" /> | ||
<Import Project=""$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets"" Condition=""'$(VSToolsPath)' != ''"" /> | ||
<Import Project=""$(VSToolsPath)\DummyImportWithoutCondition\Microsoft.WebApplication.targets"" /> | ||
</Project>"; | ||
|
||
private readonly string _projectWithReference = | ||
@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""><ItemGroup><ProjectReference Include=""{importPath}"" /></ItemGroup></Project>"; | ||
|
||
private readonly string _unpatchedImport = | ||
@"<Import Project=""$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets"" Condition=""'$(VSToolsPath)' != ''"" />"; | ||
|
||
private readonly string _patchedImport = | ||
@"<Import Project=""$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets"" Condition=""Exists('$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets')"" />"; | ||
|
||
[Test] | ||
public async Task ShouldUpdateConditionOnTaskImport() | ||
{ | ||
var workDirectory = Path.Combine(TestContext.CurrentContext.WorkDirectory, | ||
nameof(ShouldUpdateConditionOnTaskImport)); | ||
|
||
Directory.CreateDirectory(workDirectory); | ||
var projectName = nameof(ShouldUpdateConditionOnTaskImport) + ".csproj"; | ||
var projectPath = Path.Combine(workDirectory, projectName); | ||
await File.WriteAllTextAsync(projectPath, _testWebApiProject); | ||
|
||
var subject = new UpdateProjectImportsCommand(); | ||
|
||
await subject.Invoke(null, null, | ||
new PackageInProject("acme", "1", | ||
new PackagePath(workDirectory, projectName, PackageReferenceType.ProjectFileOldStyle))); | ||
|
||
var updatedContents = await File.ReadAllTextAsync(projectPath); | ||
|
||
Assert.That(updatedContents, Does.Not.Contain(_unpatchedImport)); | ||
Assert.That(updatedContents, Does.Contain(_patchedImport)); | ||
} | ||
|
||
[Test] | ||
public async Task ShouldFollowResolvableImports() | ||
{ | ||
var workDirectory = Path.Combine(TestContext.CurrentContext.WorkDirectory, | ||
nameof(ShouldFollowResolvableImports)); | ||
|
||
Directory.CreateDirectory(workDirectory); | ||
|
||
var projectName = nameof(ShouldFollowResolvableImports) + ".csproj"; | ||
var projectPath = Path.Combine(workDirectory, projectName); | ||
await File.WriteAllTextAsync(projectPath, _testWebApiProject); | ||
|
||
var intermediateProject = Path.Combine(workDirectory, "Intermediate.csproj"); | ||
var intermediateContents = _projectWithReference.Replace("{importPath}", projectPath); | ||
await File.WriteAllTextAsync(intermediateProject, intermediateContents); | ||
|
||
var rootProject = Path.Combine(workDirectory, "RootProject.csproj"); | ||
var rootContets = _projectWithReference.Replace("{importPath}", | ||
Path.Combine("..", nameof(ShouldFollowResolvableImports), "Intermediate.csproj")); | ||
await File.WriteAllTextAsync(rootProject, rootContets); | ||
|
||
var subject = new UpdateProjectImportsCommand(); | ||
|
||
await subject.Invoke(null, null, | ||
new PackageInProject("acme", "1", | ||
new PackagePath(workDirectory, "RootProject.csproj", PackageReferenceType.ProjectFileOldStyle))); | ||
|
||
var updatedContents = await File.ReadAllTextAsync(projectPath); | ||
|
||
Assert.That(updatedContents, Does.Not.Contain(_unpatchedImport)); | ||
Assert.That(updatedContents, Does.Contain(_patchedImport)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Xml.Linq; | ||
using NuGet.Versioning; | ||
using NuKeeper.Inspection.RepositoryInspection; | ||
|
||
namespace NuKeeper.NuGet.Process | ||
{ | ||
public class UpdateProjectImportsCommand : IPackageCommand | ||
{ | ||
public async Task Invoke(NuGetVersion newVersion, string packageSource, PackageInProject currentPackage) | ||
{ | ||
var projectsToUpdate = new Stack<string>(); | ||
projectsToUpdate.Push(currentPackage.Path.FullName); | ||
|
||
while (projectsToUpdate.TryPop(out var currentProject)) | ||
{ | ||
using (var projectContents = File.Open(currentProject, FileMode.Open, FileAccess.ReadWrite)) | ||
{ | ||
var projectsToCheck = await UpdateConditionsOnProjects(projectContents); | ||
foreach (var potentialProject in projectsToCheck) | ||
{ | ||
var fullPath = | ||
Path.GetFullPath(Path.Combine(Path.GetDirectoryName(currentProject), potentialProject)); | ||
if (File.Exists(fullPath)) | ||
{ | ||
projectsToUpdate.Push(fullPath); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private async Task<IEnumerable<string>> UpdateConditionsOnProjects(Stream fileContents) | ||
{ | ||
var xml = XDocument.Load(fileContents); | ||
var ns = xml.Root.GetDefaultNamespace(); | ||
|
||
var project = xml.Element(ns + "Project"); | ||
|
||
if (project == null) | ||
{ | ||
return Enumerable.Empty<string>(); | ||
} | ||
|
||
var imports = project.Elements(ns + "Import"); | ||
var importsWithToolsPath = imports | ||
.Where(i => i.Attributes("Project").Any(a => a.Value.Contains("$(VSToolsPath)"))).ToList(); | ||
var importsWithoutCondition = importsWithToolsPath.Where(i => !i.Attributes("Condition").Any()); | ||
var importsWithBrokenVsToolsCondition = importsWithToolsPath.Where(i => | ||
i.Attributes("Condition").Any(a => a.Value == "\'$(VSToolsPath)\' != \'\'")); | ||
|
||
var saveRequired = false; | ||
foreach (var importToFix in importsWithBrokenVsToolsCondition.Concat(importsWithoutCondition)) | ||
{ | ||
saveRequired = true; | ||
UpdateImportNode(importToFix); | ||
} | ||
|
||
if (saveRequired) | ||
{ | ||
fileContents.Seek(0, SeekOrigin.Begin); | ||
await xml.SaveAsync(fileContents, SaveOptions.None, CancellationToken.None); | ||
} | ||
|
||
return FindProjectReferences(project, ns); | ||
} | ||
|
||
private static IEnumerable<string> FindProjectReferences(XElement project, XNamespace ns) | ||
{ | ||
var itemGroups = project.Elements(ns + "ItemGroup"); | ||
var projectReferences = itemGroups.SelectMany(ig => ig.Elements(ns + "ProjectReference")); | ||
var includes = projectReferences.Attributes("Include").Select(a => a.Value); | ||
return includes; | ||
} | ||
|
||
private static void UpdateImportNode(XElement importToFix) | ||
{ | ||
var importPath = importToFix.Attribute("Project").Value; | ||
var condition = $"Exists('{importPath}')"; | ||
if (!importToFix.Attributes("Condition").Any()) | ||
{ | ||
importToFix.Add(new XAttribute("Condition", condition)); | ||
} | ||
else | ||
{ | ||
importToFix.Attribute("Condition").Value = condition; | ||
} | ||
} | ||
} | ||
} |