Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a version check and warn if not using latest #632

Merged
merged 9 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/DacpacTool/DacpacTool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="162.4.92" />
<PackageReference Include="NuGet.Versioning" Version="6.11.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions src/DacpacTool/IVersionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using NuGet.Versioning;

namespace MSBuild.Sdk.SqlProj.DacpacTool
{
public interface IVersionProvider
{
NuGetVersion CurrentPackageVersion();
}
}
21 changes: 15 additions & 6 deletions src/DacpacTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ static async Task<int> Main(string[] args)
new Option<bool>(new string[] { "--debug" }, "Waits for a debugger to attach")
#endif
};
buildCommand.Handler = CommandHandler.Create<BuildOptions>(BuildDacpac);
buildCommand.Handler = CommandHandler.Create<BuildOptions>(async (buildOptions) =>
{
await BuildDacpac(buildOptions).ConfigureAwait(false);
});

var collectIncludesCommand = new Command("collect-includes")
{
Expand Down Expand Up @@ -79,18 +82,20 @@ static async Task<int> Main(string[] args)
rootCommand.Description = "Command line tool for generating a SQL Server Data-Tier Application Framework package (dacpac)";

var processed = rootCommand.Parse(args);
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
return await processed.InvokeAsync();
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
return await processed.InvokeAsync().ConfigureAwait(false);
}

private static int BuildDacpac(BuildOptions options)
private static async Task<int> BuildDacpac(BuildOptions options)
{
// Wait for a debugger to attach
WaitForDebuggerToAttach(options);

// Set metadata for the package
using var packageBuilder = new PackageBuilder(new ActualConsole());
var versionChecker = new VersionChecker(new ActualConsole(), new VersionProvider());

await versionChecker.CheckForPackageUpdateAsync().ConfigureAwait(false);

// Set metadata for the package
packageBuilder.SetMetadata(options.Name, options.Version);

// Set properties on the model (if defined)
Expand Down Expand Up @@ -145,6 +150,7 @@ private static int BuildDacpac(BuildOptions options)
{
if (options.InputFile.Exists)
{
#pragma warning disable CA1849 // Call async methods when in an async method - must wait for .NET 6 to be removed
foreach (var line in File.ReadLines(options.InputFile.FullName))
{
FileInfo inputFile = new FileInfo(line); // Validation occurs in AddInputFile
Expand All @@ -153,6 +159,7 @@ private static int BuildDacpac(BuildOptions options)
modelExceptions = true;
}
}
#pragma warning restore CA1849 // Call async methods when in an async method
}
else
{
Expand All @@ -172,6 +179,7 @@ private static int BuildDacpac(BuildOptions options)
{
if (options.SuppressWarningsListFile.Exists)
{
#pragma warning disable CA1849 // Call async methods when in an async method
foreach (var line in File.ReadLines(options.SuppressWarningsListFile.FullName))
{
//Checks if there are suppression warnings list
Expand All @@ -181,6 +189,7 @@ private static int BuildDacpac(BuildOptions options)
FileInfo inputFile = new FileInfo(parts[0]); // Validation occurs in AddInputFile
packageBuilder.AddFileWarningsToSuppress(inputFile, warningList);
}
#pragma warning restore CA1849 // Call async methods when in an async method
}
}

Expand Down
84 changes: 84 additions & 0 deletions src/DacpacTool/VersionChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Http.Json;
using NuGet.Versioning;
using System.IO;
using System.Globalization;
using System.Net.Http;

namespace MSBuild.Sdk.SqlProj.DacpacTool
{

public class VersionChecker
{
private readonly IConsole _console;
private readonly IVersionProvider _versionProvider;

public VersionChecker(IConsole console, IVersionProvider versionProvider)
{
_versionProvider = versionProvider;
_console = console;
}

public async Task CheckForPackageUpdateAsync()
{
try
{
var timeout = TimeSpan.FromSeconds(2);

using var cts = new CancellationTokenSource(timeout);

var cacheFile = Path.Join(Path.GetTempPath(), "MSBuild.Sdk.SqlProj.tag-" + DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
ErikEJ marked this conversation as resolved.
Show resolved Hide resolved

NuGetVersion latestVersion = null;

if (File.Exists(cacheFile))
{
var cache = await File.ReadAllTextAsync(cacheFile, cts.Token).ConfigureAwait(false);
latestVersion = NuGetVersion.Parse(cache);
}
else
{
using var httpClient = new HttpClient
{
DefaultRequestHeaders = { { "User-Agent", "MSBuild.Sdk.SqlProj" } },
Timeout = timeout,
};

var response = await httpClient.GetFromJsonAsync<Release>("https://api.github.com/repos/rr-wfm/MSBuild.Sdk.SqlProj/releases/latest").ConfigureAwait(false);
if (response is null || response.tag_name is null)
{
return;
}

latestVersion = NuGetVersion.Parse(response.tag_name.TrimStart('v'));

await File.WriteAllTextAsync(cacheFile, latestVersion.ToNormalizedString(), cts.Token).ConfigureAwait(false);
}

if (latestVersion is null)
{
return;
}

if (latestVersion > _versionProvider.CurrentPackageVersion())
{
_console.WriteLine($"DacpacTool warning SQLPROJ0002: You are not using the latest version of this SDK, please update to get the latest bug fixes, features and support. Modify your project file: '<Project Sdk=\"MSBuild.Sdk.SqlProj/{latestVersion}\">')");
}
}
#pragma warning disable CA1031
catch (Exception)
{
// Ignore
}
#pragma warning restore CA1031
}
}


internal class Release

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

'Release' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

Type 'Release' can be sealed because it has no subtypes in its containing assembly and is not externally visible (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1852)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

'Release' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

Type 'Release' can be sealed because it has no subtypes in its containing assembly and is not externally visible (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1852)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

'Release' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

Type 'Release' can be sealed because it has no subtypes in its containing assembly and is not externally visible (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1852)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

'Release' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check warning on line 80 in src/DacpacTool/VersionChecker.cs

View workflow job for this annotation

GitHub Actions / build

Type 'Release' can be sealed because it has no subtypes in its containing assembly and is not externally visible (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1852)
ErikEJ marked this conversation as resolved.
Show resolved Hide resolved
{
public string tag_name { get; set; }
}
}
17 changes: 17 additions & 0 deletions src/DacpacTool/VersionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Reflection;
using NuGet.Versioning;

namespace MSBuild.Sdk.SqlProj.DacpacTool
{
public class VersionProvider : IVersionProvider
{
public NuGetVersion CurrentPackageVersion()
{
return new NuGetVersion(
typeof(VersionProvider)
.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
.InformationalVersion);
}
}
}
2 changes: 0 additions & 2 deletions test/DacpacTool.Tests/PackageAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public void RunsAnalyzer()
testConsole.Lines.ShouldContain($"Successfully analyzed package '{result.fileInfo.FullName}'");
}



[TestMethod]
public void RunsAnalyzerWithSupressions()
{
Expand Down
19 changes: 19 additions & 0 deletions test/DacpacTool.Tests/TestVersionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using NuGet.Versioning;

namespace MSBuild.Sdk.SqlProj.DacpacTool.Tests
{
public class VersionProvider : IVersionProvider
{
private readonly string _version;

public VersionProvider(string version)
{
_version = version;
}

public NuGetVersion CurrentPackageVersion()
{
return new NuGetVersion(_version);
}
}
}
69 changes: 69 additions & 0 deletions test/DacpacTool.Tests/VersionCheckerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Globalization;
using System.IO;
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System.Diagnostics;

namespace MSBuild.Sdk.SqlProj.DacpacTool.Tests
{
[TestClass]
public class VersionCheckerTests
{
private readonly IConsole _console = new TestConsole();

[TestMethod]
public async Task RunsVersionCheck()
{
// Arrange
var testConsole = (TestConsole)_console;
testConsole.Lines.Clear();
var versionChecker = new VersionChecker(_console, new VersionProvider("1.17.0+4c0175a82e"));

var cacheFile = Path.Join(Path.GetTempPath(), "MSBuild.Sdk.SqlProj.tag-" + DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");

if (File.Exists(cacheFile))
{
File.Delete(cacheFile);
}

// Act
await versionChecker.CheckForPackageUpdateAsync();

// Assert
testConsole.Lines.Count.ShouldBe(1);
testConsole.Lines[0].ShouldStartWith($"DacpacTool warning SQLPROJ0002: You are not using the latest version of this SDK, please update to get the latest bug fixes, features and support. Modify your project file: ");

// Arrange
testConsole.Lines.Clear();

var stopWatch = Stopwatch.StartNew();

// Act
await versionChecker.CheckForPackageUpdateAsync();

stopWatch.Stop();

testConsole.Lines.Count.ShouldBe(1);
testConsole.Lines[0].ShouldStartWith($"DacpacTool warning SQLPROJ0002: You are not using the latest version of this SDK, please update to get the latest bug fixes, features and support. Modify your project file: ");
File.Exists(cacheFile).ShouldBeTrue();
stopWatch.ElapsedMilliseconds.ShouldBeLessThan(100);
}

[TestMethod]
public async Task RunsVersionCheckAndNoLog()
{
// Arrange
var testConsole = (TestConsole)_console;
testConsole.Lines.Clear();
var versionChecker = new VersionChecker(_console, new VersionProvider("9999999.9999999.0+4c0175a82e"));

// Act
await versionChecker.CheckForPackageUpdateAsync();

// Assert
testConsole.Lines.Count.ShouldBe(0);
}
}
}