diff --git a/Src/CSharpier.Cli/CommandLineFormatter.cs b/Src/CSharpier.Cli/CommandLineFormatter.cs index d2b22e8ad..0fca3208e 100644 --- a/Src/CSharpier.Cli/CommandLineFormatter.cs +++ b/Src/CSharpier.Cli/CommandLineFormatter.cs @@ -1,7 +1,9 @@ using System; using System.Diagnostics; using System.IO.Abstractions; -using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using System.Xml.XPath; using CSharpier.Utilities; using Microsoft.Extensions.Logging; @@ -94,6 +96,17 @@ await FormatPhysicalFile( } else if (fileSystem.Directory.Exists(directoryOrFile)) { + if ( + HasMismatchedCliAndMsBuildVersions.Check( + directoryOrFile, + fileSystem, + logger + ) + ) + { + return 1; + } + var tasks = fileSystem.Directory .EnumerateFiles(directoryOrFile, "*.cs", SearchOption.AllDirectories) .Select(FormatFile) diff --git a/Src/CSharpier.Cli/HasMismatchedCliAndMsBuildVersions.cs b/Src/CSharpier.Cli/HasMismatchedCliAndMsBuildVersions.cs new file mode 100644 index 000000000..f0bd277e2 --- /dev/null +++ b/Src/CSharpier.Cli/HasMismatchedCliAndMsBuildVersions.cs @@ -0,0 +1,55 @@ +using System.IO.Abstractions; +using System.Xml.Linq; +using System.Xml.XPath; +using Microsoft.Extensions.Logging; + +namespace CSharpier.Cli; + +public static class HasMismatchedCliAndMsBuildVersions +{ + public static bool Check(string directory, IFileSystem fileSystem, ILogger logger) + { + var csProjPaths = fileSystem.Directory + .EnumerateFiles(directory, "*.csproj", SearchOption.AllDirectories) + .ToArray(); + + var versionOfDotnetTool = typeof(CommandLineFormatter).Assembly.GetName().Version!.ToString( + 3 + ); + + foreach (var pathToCsProj in csProjPaths) + { + // this could potentially use the Microsoft.CodeAnalysis.Project class, but that was + // proving difficult to use + var csProjXElement = XElement.Load(fileSystem.File.OpenRead(pathToCsProj)); + var csharpierMsBuildElement = csProjXElement + .XPathSelectElements("//PackageReference[@Include='CSharpier.MsBuild']") + .FirstOrDefault(); + if (csharpierMsBuildElement == null) + { + continue; + } + + var versionOfMsBuildPackage = csharpierMsBuildElement.Attribute("Version")?.Value; + if (versionOfMsBuildPackage == null) + { + logger.LogError( + $"The csproj at {pathToCsProj} uses an unknown version of CSharpier.MsBuild" + + $" which is a mismatch with version {versionOfDotnetTool}" + ); + return true; + } + + if (versionOfDotnetTool != versionOfMsBuildPackage) + { + logger.LogError( + $"The csproj at {pathToCsProj} uses version {versionOfMsBuildPackage} of CSharpier.MsBuild" + + $" which is a mismatch with version {versionOfDotnetTool}" + ); + return true; + } + } + + return false; + } +} diff --git a/Src/CSharpier.Tests/CommandLineFormatterTests.cs b/Src/CSharpier.Tests/CommandLineFormatterTests.cs index 5fdfa3833..937066116 100644 --- a/Src/CSharpier.Tests/CommandLineFormatterTests.cs +++ b/Src/CSharpier.Tests/CommandLineFormatterTests.cs @@ -75,6 +75,91 @@ public void Format_Writes_File_With_Directory_Path() this.GetFileContent(unformattedFilePath).Should().Be(FormattedClassContent); } + [TestCase("0.9.0", false)] + [TestCase("9999.0.0", false)] + [TestCase("current", true)] + public void Works_With_MSBuild_Version_Checking(string version, bool shouldPass) + { + var currentVersion = typeof(CommandLineFormatter).Assembly.GetName().Version!.ToString(3); + + var versionToTest = version == "current" ? currentVersion : version; + + WhenAFileExists( + "Test.csproj", + $@" + + + + +" + ); + + var result = this.Format(); + + if (shouldPass) + { + result.ExitCode.Should().Be(0); + result.ErrorLines.Should().BeEmpty(); + } + else + { + result.ExitCode.Should().Be(1); + result.ErrorLines + .First() + .Should() + .EndWith( + $@"Test.csproj uses version {version} of CSharpier.MsBuild which is a mismatch with version {currentVersion}" + ); + } + } + + [Test] + public void Works_With_MSBuild_Version_Checking_When_No_Version_Specified() + { + var currentVersion = typeof(CommandLineFormatter).Assembly.GetName().Version!.ToString(3); + + WhenAFileExists( + "Test.csproj", + $@" + + + + +" + ); + + var result = this.Format(); + + result.ExitCode.Should().Be(1); + result.ErrorLines + .First() + .Should() + .EndWith( + $"Test.csproj uses an unknown version of CSharpier.MsBuild which is a mismatch with version {currentVersion}" + ); + } + + [Test] + public void Works_With_MSBuild_Version_Checking_When_No_Version_Included() + { + var currentVersion = typeof(CommandLineFormatter).Assembly.GetName().Version!.ToString(3); + + WhenAFileExists( + "Test.csproj", + $@" + + + + +" + ); + + var result = this.Format(); + + result.ExitCode.Should().Be(0); + result.ErrorLines.Should().BeEmpty(); + } + [Test] public void Format_Writes_File_With_File_Path() { @@ -357,6 +442,7 @@ public void Empty_Config_Files_Should_Log_Warning(string configFileName) result.Lines .First() + .Replace("\\", "/") .Should() .Be($"Warning The configuration file at {configPath} was empty."); }