-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add initial commit for package validation #16992
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
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1b64bd5
Add intitial version of package Validation
Anipik 25ce7fb
Add some basic tests
Anipik d1096a1
addressing feedback
Anipik 15d8c44
fix and some more tests
Anipik ae4d1fc
logging the errors on the fly , add GetLastStableVersion, removing du…
Anipik 751984c
fix test builds
Anipik 152f716
add some feedback
Anipik ab4ff42
adding a new test logger to test the output for tests
Anipik 716c213
addressing some mroe feedback around running targets
Anipik e39bfd1
Restore fixes, baseline conditional validation
ViktorHofer ec98cc6
Use full namespace in task invocations
ViktorHofer 5946d6d
React to ericstj's feedback
ViktorHofer 2c40aa5
address feedback
Anipik 0582d7f
merge checker with diagnostic bag
Anipik 7bbf7a8
use hashset for diaglist and add comments in the package
Anipik 2e2d7a9
fix failing test on ci and resx files
Anipik 898b8ba
add left and right to resx
Anipik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
91 changes: 91 additions & 0 deletions
91
src/Compatibility/Microsoft.DotNet.PackageValidation/ApiCompatRunner.cs
This file contains hidden or 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,91 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.IO.Compression; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.DotNet.ApiCompatibility; | ||
using Microsoft.DotNet.ApiCompatibility.Abstractions; | ||
using NuGet.Common; | ||
|
||
namespace Microsoft.DotNet.PackageValidation | ||
{ | ||
/// <summary> | ||
/// Runs ApiCompat over different assembly tuples. | ||
/// </summary> | ||
public class ApiCompatRunner | ||
{ | ||
private List<(string leftAssemblyPackagePath, string leftAssemblyRelativePath, string rightAssemblyPackagePath, string rightAssemblyRelativePath, string assemblyName, string compatibilityReason, string header)> _queue = new(); | ||
private readonly ILogger _log; | ||
private readonly ApiComparer _differ = new(); | ||
|
||
public ApiCompatRunner(string noWarn, (string, string)[] ignoredDifferences, ILogger log) | ||
{ | ||
_log = log; | ||
_differ.NoWarn = noWarn; | ||
_differ.IgnoredDifferences = ignoredDifferences; | ||
} | ||
|
||
/// <summary> | ||
/// Runs the api compat for the tuples in the queue. | ||
/// </summary> | ||
public void RunApiCompat() | ||
{ | ||
foreach (var apicompatTuples in _queue.Distinct()) | ||
{ | ||
// TODO: Add Assembly version check. | ||
// TODO: Add optimisations tuples. | ||
// TODO: Run it Asynchronously. | ||
using (Stream leftAssemblyStream = GetFileStreamFromPackage(apicompatTuples.leftAssemblyPackagePath, apicompatTuples.leftAssemblyRelativePath)) | ||
using (Stream rightAssemblyStream = GetFileStreamFromPackage(apicompatTuples.rightAssemblyPackagePath, apicompatTuples.rightAssemblyRelativePath)) | ||
{ | ||
IAssemblySymbol leftSymbols = new AssemblySymbolLoader().LoadAssembly(apicompatTuples.assemblyName, leftAssemblyStream); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this logic will need to be changed with: #17295 |
||
IAssemblySymbol rightSymbols = new AssemblySymbolLoader().LoadAssembly(apicompatTuples.assemblyName, rightAssemblyStream); | ||
|
||
IEnumerable<CompatDifference> differences = _differ.GetDifferences(leftSymbols, rightSymbols); | ||
|
||
if (differences.Any()) | ||
{ | ||
_log.LogError(apicompatTuples.compatibilityReason); | ||
_log.LogError(apicompatTuples.header); | ||
} | ||
|
||
foreach (CompatDifference difference in differences) | ||
{ | ||
_log.LogError(difference.ToString()); | ||
} | ||
} | ||
} | ||
_queue.Clear(); | ||
} | ||
|
||
/// <summary> | ||
/// Queues the api compat for 2 assemblies. | ||
/// </summary> | ||
/// <param name="leftPackagePath">Path to package containing left assembly.</param> | ||
/// <param name="leftRelativePath">Relative left assembly path in package.</param> | ||
/// <param name="rightPackagePath">Path to package containing right assembly.</param> | ||
/// <param name="rightRelativePath">Relative right assembly path in package.</param> | ||
/// <param name="assemblyName">The name of the assembly.</param> | ||
/// <param name="compatibiltyReason">The reason for assembly compatibilty.</param> | ||
/// <param name="header">The header for the api compat diagnostics.</param> | ||
public void QueueApiCompat(string leftPackagePath, string leftRelativePath, string rightPackagePath, string rightRelativePath, string assemblyName, string compatibiltyReason, string header) | ||
{ | ||
_queue.Add((leftPackagePath, leftRelativePath, rightPackagePath, rightRelativePath, assemblyName, compatibiltyReason, header)); | ||
} | ||
|
||
private static Stream GetFileStreamFromPackage(string packagePath, string entry) | ||
{ | ||
MemoryStream ms = new MemoryStream(); | ||
Anipik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using (FileStream stream = File.OpenRead(packagePath)) | ||
{ | ||
var zipFile = new ZipArchive(stream); | ||
zipFile.GetEntry(entry).Open().CopyTo(ms); | ||
ms.Seek(0, SeekOrigin.Begin); | ||
} | ||
return ms; | ||
} | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
src/Compatibility/Microsoft.DotNet.PackageValidation/BaselinePackageValidator.cs
This file contains hidden or 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,126 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using Microsoft.DotNet.ApiCompatibility; | ||
using Microsoft.DotNet.ApiCompatibility.Abstractions; | ||
using NuGet.Common; | ||
using NuGet.ContentModel; | ||
using NuGet.Frameworks; | ||
|
||
namespace Microsoft.DotNet.PackageValidation | ||
{ | ||
/// <summary> | ||
/// Validates that no target framework / rid support is dropped in the latest package. | ||
/// Reports all the breaking changes in the latest package. | ||
/// </summary> | ||
public class BaselinePackageValidator | ||
{ | ||
private static HashSet<string> s_diagList = new HashSet<string>{ DiagnosticIds.TargetFrameworkDropped, DiagnosticIds.TargetFrameworkAndRidPairDropped }; | ||
|
||
private readonly Package _baselinePackage; | ||
private readonly bool _runApiCompat; | ||
private readonly ApiCompatRunner _apiCompatRunner; | ||
private readonly ILogger _log; | ||
private readonly DiagnosticBag<IDiagnostic> _diagnosticBag; | ||
|
||
public BaselinePackageValidator(Package baselinePackage, string noWarn, (string, string)[] ignoredDifferences, bool runApiCompat, ILogger log) | ||
{ | ||
_baselinePackage = baselinePackage; | ||
_runApiCompat = runApiCompat; | ||
_log = log; | ||
_apiCompatRunner = new(noWarn, ignoredDifferences, _log); | ||
_diagnosticBag = new(noWarn?.Split(';')?.Where(t => s_diagList.Contains(t)), ignoredDifferences); | ||
Anipik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/// <summary> | ||
/// Validates the latest nuget package doesnot drop any target framework/rid and does not introduce any breaking changes. | ||
/// </summary> | ||
/// <param name="package">Nuget Package that needs to be validated.</param> | ||
public void Validate(Package package) | ||
{ | ||
foreach (ContentItem baselineCompileTimeAsset in _baselinePackage.CompileAssets) | ||
{ | ||
NuGetFramework baselineTargetFramework = (NuGetFramework)baselineCompileTimeAsset.Properties["tfm"]; | ||
ContentItem latestCompileTimeAsset = package.FindBestCompileAssetForFramework(baselineTargetFramework); | ||
if (latestCompileTimeAsset == null) | ||
{ | ||
if (!_diagnosticBag.Filter(DiagnosticIds.TargetFrameworkDropped, baselineTargetFramework.ToString())) | ||
{ | ||
string message = string.Format(Resources.MissingTargetFramework, baselineTargetFramework.ToString()); | ||
_log.LogError(DiagnosticIds.TargetFrameworkDropped + " " + message); | ||
} | ||
} | ||
else if (_runApiCompat) | ||
{ | ||
_apiCompatRunner.QueueApiCompat(_baselinePackage.PackagePath, | ||
baselineCompileTimeAsset.Path, | ||
package.PackagePath, | ||
latestCompileTimeAsset.Path, | ||
Path.GetFileName(package.PackagePath), | ||
Resources.BaselineVersionValidatorHeader, | ||
string.Format(Resources.ApiCompatibilityHeader, baselineCompileTimeAsset.Path, latestCompileTimeAsset.Path)); | ||
} | ||
} | ||
|
||
foreach (ContentItem baselineRuntimeAsset in _baselinePackage.RuntimeAssets) | ||
{ | ||
NuGetFramework baselineTargetFramework = (NuGetFramework)baselineRuntimeAsset.Properties["tfm"]; | ||
ContentItem latestRuntimeAsset = package.FindBestRuntimeAssetForFramework(baselineTargetFramework); | ||
if (latestRuntimeAsset == null) | ||
{ | ||
if (!_diagnosticBag.Filter(DiagnosticIds.TargetFrameworkDropped, baselineTargetFramework.ToString())) | ||
{ | ||
string message = string.Format(Resources.MissingTargetFramework, baselineTargetFramework.ToString()); | ||
_log.LogError(DiagnosticIds.TargetFrameworkDropped + " " + message); | ||
} | ||
} | ||
else | ||
{ | ||
if (_runApiCompat) | ||
{ | ||
Anipik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_apiCompatRunner.QueueApiCompat(_baselinePackage.PackagePath, | ||
baselineRuntimeAsset.Path, | ||
package.PackagePath, | ||
latestRuntimeAsset.Path, | ||
Path.GetFileName(package.PackagePath), | ||
Resources.BaselineVersionValidatorHeader, | ||
string.Format(Resources.ApiCompatibilityHeader, baselineRuntimeAsset.Path, latestRuntimeAsset.Path)); | ||
} | ||
} | ||
} | ||
|
||
foreach (ContentItem baselineRuntimeSpecificAsset in _baselinePackage.RuntimeSpecificAssets) | ||
{ | ||
NuGetFramework baselineTargetFramework = (NuGetFramework)baselineRuntimeSpecificAsset.Properties["tfm"]; | ||
string baselineRid = (string)baselineRuntimeSpecificAsset.Properties["rid"]; | ||
ContentItem latestRuntimeSpecificAsset = package.FindBestRuntimeAssetForFrameworkAndRuntime(baselineTargetFramework, baselineRid); | ||
if (latestRuntimeSpecificAsset == null) | ||
{ | ||
if (!_diagnosticBag.Filter(DiagnosticIds.TargetFrameworkDropped, baselineTargetFramework.ToString() + "-" + baselineRid)) | ||
{ | ||
string message = string.Format(Resources.MissingTargetFrameworkAndRid, baselineTargetFramework.ToString(), baselineRid); | ||
_log.LogError(DiagnosticIds.TargetFrameworkAndRidPairDropped + " " + message); | ||
} | ||
} | ||
else | ||
{ | ||
if (_runApiCompat) | ||
Anipik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
_apiCompatRunner.QueueApiCompat(_baselinePackage.PackagePath, | ||
baselineRuntimeSpecificAsset.Path, | ||
package.PackagePath, | ||
latestRuntimeSpecificAsset.Path, | ||
Path.GetFileName(package.PackagePath), | ||
Resources.BaselineVersionValidatorHeader, | ||
string.Format(Resources.ApiCompatibilityHeader, baselineRuntimeSpecificAsset.Path, latestRuntimeSpecificAsset.Path)); | ||
} | ||
} | ||
} | ||
|
||
_apiCompatRunner.RunApiCompat(); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.