Skip to content

Commit

Permalink
Implement option to get the context in which a dependency was validated
Browse files Browse the repository at this point in the history
  • Loading branch information
sensslen committed Dec 28, 2023
1 parent b63fca7 commit 7a04a12
Show file tree
Hide file tree
Showing 898 changed files with 109,488 additions and 34,141 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Usage: nuget-license [options]
| `-d, --license-information-download-location` | When used, this option downloads the html content of the license URL to the specified folder. This is done for all NuGet packages that specify a license URL instead of providing the license expression. |
| `-o, --output` | This Parameter accepts the value `table`, `json` or `jsonPretty`. It allows to select the type of output that should be given. If omitted, the output is given in tabular form. |
| `-err, --error-only` | This flag allows to print only packages that contain validation errors (if there are any). This allows the user to focus on errors instead of having to deal with many properly validated |
| `-include-context, --always-include-validation-context` | Setting this flag will print all contexts (projects) that caused the nuget package to be validated. Note that this will only print the top level projects that are actually checked. Also note that a project with a dependency on another project may cause multiple entries if the `-t` flag is used. |

## Example tool commands

Expand Down
6 changes: 3 additions & 3 deletions src/NuGetUtility/LicenseValidator/LicenseValidationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ public record LicenseValidationResult(string PackageId,
string? PackageProjectUrl,
string? License,
LicenseInformationOrigin LicenseInformationOrigin,
List<ValidationError>? ValidationErrors = null)
List<ValidationCheck> ValidationChecks)
{
public List<ValidationError> ValidationErrors { get; } = ValidationErrors ?? new List<ValidationError>();

public string? License { get; set; } = License;
public LicenseInformationOrigin LicenseInformationOrigin { get; set; } = LicenseInformationOrigin;

public override string ToString() => $"LicenseValidationResult {{ PackageId = {PackageId}, PackageVersion = {PackageVersion}, PackageProjectUrl = {PackageProjectUrl}, ValidationChecks = {{{string.Join(',', ValidationChecks)}}}, License = {License}, LicenseInformationOrigin = {LicenseInformationOrigin} }}";
}
}
47 changes: 17 additions & 30 deletions src/NuGetUtility/LicenseValidator/LicenseValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ public async Task<IEnumerable<LicenseValidationResult>> Validate(
{
AddOrUpdateLicense(result,
info.PackageInfo,
LicenseInformationOrigin.Ignored);
LicenseInformationOrigin.Ignored,
new ValidationCheck(info.Context));
}
else if (info.PackageInfo.LicenseMetadata != null)
else if (info.PackageInfo.LicenseMetadata is not null)
{
ValidateLicenseByMetadata(info.PackageInfo, info.Context, result);
}
else if (info.PackageInfo.LicenseUrl != null)
else if (info.PackageInfo.LicenseUrl is not null)
{
await ValidateLicenseByUrl(info.PackageInfo, info.Context, result);
}
Expand All @@ -52,7 +53,7 @@ public async Task<IEnumerable<LicenseValidationResult>> Validate(
AddOrUpdateLicense(result,
info.PackageInfo,
LicenseInformationOrigin.Unknown,
new ValidationError("No license information found", info.Context));
new ValidationCheck(info.Context, "No license information found"));
}
}
return result.Values;
Expand All @@ -67,7 +68,7 @@ private void AddOrUpdateLicense(
ConcurrentDictionary<LicenseNameAndVersion, LicenseValidationResult> result,
IPackageMetadata info,
LicenseInformationOrigin origin,
ValidationError error,
ValidationCheck check,
string? license = null)
{
var newValue = new LicenseValidationResult(
Expand All @@ -76,24 +77,7 @@ private void AddOrUpdateLicense(
info.ProjectUrl?.ToString(),
license,
origin,
new List<ValidationError> { error });
result.AddOrUpdate(new LicenseNameAndVersion(info.Identity.Id, info.Identity.Version),
newValue,
(key, oldValue) => UpdateResult(oldValue, newValue));
}

private void AddOrUpdateLicense(
ConcurrentDictionary<LicenseNameAndVersion, LicenseValidationResult> result,
IPackageMetadata info,
LicenseInformationOrigin origin,
string? license = null)
{
var newValue = new LicenseValidationResult(
info.Identity.Id,
info.Identity.Version,
info.ProjectUrl?.ToString(),
license,
origin);
new List<ValidationCheck> { check });
result.AddOrUpdate(new LicenseNameAndVersion(info.Identity.Id, info.Identity.Version),
newValue,
(key, oldValue) => UpdateResult(oldValue, newValue));
Expand All @@ -102,7 +86,7 @@ private void AddOrUpdateLicense(
private LicenseValidationResult UpdateResult(LicenseValidationResult oldValue,
LicenseValidationResult newValue)
{
oldValue.ValidationErrors.AddRange(newValue.ValidationErrors);
oldValue.ValidationChecks.AddRange(newValue.ValidationChecks);
if (oldValue.License is null && newValue.License is not null)
{
oldValue.License = newValue.License;
Expand All @@ -125,14 +109,15 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
AddOrUpdateLicense(result,
info,
ToLicenseOrigin(info.LicenseMetadata.Type),
new ValidationCheck(context),
info.LicenseMetadata.License);
}
else
{
AddOrUpdateLicense(result,
info,
ToLicenseOrigin(info.LicenseMetadata.Type),
new ValidationError(GetLicenseNotAllowedMessage(info.LicenseMetadata.License), context),
new ValidationCheck(context, GetLicenseNotAllowedMessage(info.LicenseMetadata.License)),
info.LicenseMetadata.License);
}

Expand All @@ -141,9 +126,9 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
AddOrUpdateLicense(result,
info,
LicenseInformationOrigin.Unknown,
new ValidationError(
$"Validation for licenses of type {info.LicenseMetadata!.Type} not yet supported",
context));
new ValidationCheck(
context,
$"Validation for licenses of type {info.LicenseMetadata!.Type} not yet supported"));
break;
}
}
Expand Down Expand Up @@ -172,14 +157,15 @@ await _fileDownloader.DownloadFile(info.LicenseUrl,
AddOrUpdateLicense(result,
info,
LicenseInformationOrigin.Url,
new ValidationCheck(context),
licenseId);
}
else
{
AddOrUpdateLicense(result,
info,
LicenseInformationOrigin.Url,
new ValidationError(GetLicenseNotAllowedMessage(licenseId), context),
new ValidationCheck(context, GetLicenseNotAllowedMessage(licenseId)),
licenseId);
}
}
Expand All @@ -188,14 +174,15 @@ await _fileDownloader.DownloadFile(info.LicenseUrl,
AddOrUpdateLicense(result,
info,
LicenseInformationOrigin.Url,
new ValidationCheck(context),
info.LicenseUrl.ToString());
}
else
{
AddOrUpdateLicense(result,
info,
LicenseInformationOrigin.Url,
new ValidationError($"Cannot determine License type for url {info.LicenseUrl}", context),
new ValidationCheck(context, $"Cannot determine License type for url {info.LicenseUrl}"),
info.LicenseUrl.ToString());
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetUtility/LicenseValidator/ValidationError.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NuGetUtility.LicenseValidator
{
public record ValidationError(string Error, string Context);
public record ValidationCheck(string Context, string? Error = null);
}
13 changes: 9 additions & 4 deletions src/NuGetUtility/Output/Json/JsonOutputFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ public class JsonOutputFormatter : IOutputFormatter
private readonly bool _printErrorsOnly;
private readonly bool _skipIgnoredPackages;
private readonly JsonSerializerOptions _options;
public JsonOutputFormatter(bool prettyPrint, bool printErrorsOnly, bool skipIgnoredPackages)
public JsonOutputFormatter(bool prettyPrint, bool printErrorsOnly, bool skipIgnoredPackages, bool alwaysIncludeValidationContext)
{
_printErrorsOnly = printErrorsOnly;
_skipIgnoredPackages = skipIgnoredPackages;
_options = new JsonSerializerOptions
{
Converters = { new NuGetVersionJsonConverter(), new ValidatedLicenseJsonConverterWithOmittingEmptyErrorList() },
Converters = { new NuGetVersionJsonConverter() },
WriteIndented = prettyPrint
};

if (!alwaysIncludeValidationContext)
{
_options.Converters.Add(new PackageValidationContextErrorOnlyConverter());
}
}

public async Task Write(Stream stream, IList<LicenseValidationResult> results)
{
if (_printErrorsOnly)
{
IEnumerable<LicenseValidationResult> resultsWithErrors = results.Where(r => r.ValidationErrors.Any());
if (resultsWithErrors.Any())
IEnumerable<LicenseValidationResult> resultsWithErrors = results.Where(r => r.ValidationChecks.Exists(c => c.Error is not null));
if (results.Any())
{
await JsonSerializer.SerializeAsync(stream, resultsWithErrors, _options);
return;
Expand Down
14 changes: 8 additions & 6 deletions src/NuGetUtility/Output/Table/TableOutputFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@ public class TableOutputFormatter : IOutputFormatter
{
private readonly bool _printErrorsOnly;
private readonly bool _skipIgnoredPackages;
private readonly bool _alwaysIncludeValidationContext;

public TableOutputFormatter(bool printErrorsOnly, bool skipIgnoredPackages)
public TableOutputFormatter(bool printErrorsOnly, bool skipIgnoredPackages, bool alwaysIncludeValidationContext)
{
_printErrorsOnly = printErrorsOnly;
_skipIgnoredPackages = skipIgnoredPackages;
_alwaysIncludeValidationContext = alwaysIncludeValidationContext;
}

public async Task Write(Stream stream, IList<LicenseValidationResult> results)
{
var errorColumnDefinition = new ColumnDefinition("Error", license => license.ValidationErrors.Select(e => e.Error), license => license.ValidationErrors.Any());
var errorColumnDefinition = new ColumnDefinition("Error", license => license.ValidationChecks.Select(e => e.Error), license => license.ValidationChecks.Exists(c => c.Error is not null));
ColumnDefinition[] columnDefinitions = new[]
{
new ColumnDefinition("Package", license => license.PackageId, license => true, true),
new ColumnDefinition("Version", license => license.PackageVersion, license => true, true),
new ColumnDefinition("License Information Origin", license => license.LicenseInformationOrigin, license => true, true),
new ColumnDefinition("License Expression", license => license.License, license => license.License != null),
new ColumnDefinition("Package Project Url",license => license.PackageProjectUrl, license => license.PackageProjectUrl != null),
new ColumnDefinition("License Expression", license => license.License, license => license.License is not null),
new ColumnDefinition("Package Project Url",license => license.PackageProjectUrl, license => license.PackageProjectUrl is not null),
errorColumnDefinition,
new ColumnDefinition("Error Context", license => license.ValidationErrors.Select(e => e.Context), license => license.ValidationErrors.Any()),
new ColumnDefinition("Context", license => license.ValidationChecks.Select(e => e.Context), license => _alwaysIncludeValidationContext || license.ValidationChecks.Exists(c => c.Error is not null)),
};

foreach (LicenseValidationResult license in results)
Expand All @@ -37,7 +39,7 @@ public async Task Write(Stream stream, IList<LicenseValidationResult> results)

if (_printErrorsOnly && errorColumnDefinition.Enabled)
{
results = results.Where(r => r.ValidationErrors.Any()).ToList();
results = results.Where(r => r.ValidationChecks.Exists(c => c.Error is not null)).ToList();
}

if (_skipIgnoredPackages)
Expand Down
4 changes: 2 additions & 2 deletions src/NuGetUtility/Output/Table/TablePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public void AddRow(object?[] row)

private string[] GetLines(object? lines)
{
if (lines is IEnumerable<object> enumerable)
if (lines is IEnumerable<object?> enumerable)
{
return enumerable.Select(o => o.ToString() ?? string.Empty).ToArray();
return enumerable.Select(o => o?.ToString() ?? string.Empty).ToArray();
}
return new[] { lines?.ToString() ?? string.Empty };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async IAsyncEnumerable<ReferencedPackageWithContext> GetPackageInfo(
private PackageSearchResult TryGetPackageInformationFromGlobalPackageFolder(PackageIdentity package)
{
IPackageMetadata? metadata = _globalPackagesFolderUtility.GetPackage(package);
if (metadata != null)
if (metadata is not null)
{
return new PackageSearchResult(metadata);
}
Expand All @@ -74,7 +74,7 @@ private async Task<PackageSearchResult> TryGetPackageInformationFromRepositories

IPackageMetadata? updatedPackageMetadata = await resource.TryGetMetadataAsync(package, cancellation);

if (updatedPackageMetadata != null)
if (updatedPackageMetadata is not null)
{
return new PackageSearchResult(updatedPackageMetadata);
}
Expand Down
22 changes: 12 additions & 10 deletions src/NuGetUtility/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public class Program
Description = "If this option is set, the packages matching the ignore regexes are also printed to the output by specifying that they were explicitly ignored.")]
public bool IncludeIgnoredPackages { get; } = false;

[Option(LongName = "always-include-validation-context",
ShortName = "include-context",
Description = "If this option is set, the output will include the license context even if no error occurred.")]
public bool AlwaysIncludeValidationContext { get; } = false;

public static async Task Main(string[] args)
{
var lifetime = new AppLifetime();
Expand Down Expand Up @@ -135,7 +140,7 @@ private async Task<int> OnExecuteAsync(CancellationToken cancellationToken)

await using Stream outputStream = Console.OpenStandardOutput();
await output.Write(outputStream, results.OrderBy(r => r.PackageId).ThenBy(r => r.PackageVersion).ToList());
return results.Count(r => r.ValidationErrors.Any());
return results.Count(r => r.ValidationChecks.Exists(c => c.Error is not null));
}
private static IAsyncEnumerable<ReferencedPackageWithContext> GetPackageInfos(
ProjectWithReferencedPackages projectWithReferences,
Expand All @@ -156,9 +161,9 @@ private IOutputFormatter GetOutputFormatter()
{
return OutputType switch
{
OutputType.Json => new JsonOutputFormatter(false, ReturnErrorsOnly, !IncludeIgnoredPackages),
OutputType.JsonPretty => new JsonOutputFormatter(true, ReturnErrorsOnly, !IncludeIgnoredPackages),
OutputType.Table => new TableOutputFormatter(ReturnErrorsOnly, !IncludeIgnoredPackages),
OutputType.Json => new JsonOutputFormatter(false, ReturnErrorsOnly, !IncludeIgnoredPackages, AlwaysIncludeValidationContext),
OutputType.JsonPretty => new JsonOutputFormatter(true, ReturnErrorsOnly, !IncludeIgnoredPackages, AlwaysIncludeValidationContext),
OutputType.Table => new TableOutputFormatter(ReturnErrorsOnly, !IncludeIgnoredPackages, AlwaysIncludeValidationContext),
_ => throw new ArgumentOutOfRangeException($"{OutputType} not supported")
};
}
Expand Down Expand Up @@ -215,10 +220,7 @@ private IImmutableDictionary<Uri, string> GetLicenseMappings()
return UrlToLicenseMapping.Default;
}

var serializerOptions = new JsonSerializerOptions();
serializerOptions.Converters.Add(new UriDictionaryJsonConverter<string>());
Dictionary<Uri, string> userDictionary = JsonSerializer.Deserialize<Dictionary<Uri, string>>(File.ReadAllText(LicenseMapping),
serializerOptions)!;
Dictionary<Uri, string> userDictionary = JsonSerializer.Deserialize<Dictionary<Uri, string>>(File.ReadAllText(LicenseMapping))!;

return UrlToLicenseMapping.Default.SetItems(userDictionary);
}
Expand All @@ -235,12 +237,12 @@ private string[] GetIgnoredPackages()

private string[] GetInputFiles()
{
if (InputFile != null)
if (InputFile is not null)
{
return new[] { InputFile };
}

if (InputJsonFile != null)
if (InputJsonFile is not null)
{
return JsonSerializer.Deserialize<string[]>(File.ReadAllText(InputJsonFile))!;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using NuGetUtility.LicenseValidator;
using NuGetUtility.Wrapper.NuGetWrapper.Versioning;

namespace NuGetUtility.Serialization
Expand Down
Loading

0 comments on commit 7a04a12

Please sign in to comment.