Skip to content

Commit

Permalink
Merge pull request #13 from danielklecha/master
Browse files Browse the repository at this point in the history
update structure, use dependency injection, enable nullable annotations
  • Loading branch information
danielklecha authored Mar 10, 2024
2 parents 5123297 + 4e454ea commit 924e58f
Show file tree
Hide file tree
Showing 38 changed files with 827 additions and 860 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,26 @@ dotnet tool install -g DotnetThirdPartyNotices

## Get started

Go inside the project directory and run:
Go inside the project or solution directory and run:

```
dotnet-thirdpartynotices
```

To change the name of the file that will be generated:

```
dotnet-thirdpartynotices --output-filename "third party notices.txt"
```

If your project is in a different directory:

```
dotnet-thirdpartynotices <project directory path>
dotnet-thirdpartynotices --output-filename "third party notices.txt" <project directory path>
```

Options:

- `--output-filename` allow to change output filename
- `--copy-to-outdir` allow to copy output file to output directory in Release configuration
- `--filter` allow to use regex to filter project files

Note that if you use the solution folder and don't use `--copy-to-outdir` then licenses from all projects will be merged to single file.

## How it works

### 1. Resolve assemblies
Expand Down
141 changes: 141 additions & 0 deletions src/DotnetThirdPartyNotices/Commands/ScanCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using DotnetThirdPartyNotices.Models;
using DotnetThirdPartyNotices.Services;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace DotnetThirdPartyNotices.Commands;

internal class ScanCommand : Command
{
public ScanCommand() : base("scan", "A tool to generate file with third party legal notices for .NET projects")
{
AddArgument(new Argument<string>("scan-dir", "Path of the directory to look for projects (optional)") { Arity = ArgumentArity.ZeroOrOne });
AddOption(new Option<string>("--output-filename", () => "third-party-notices.txt", "Output filename"));
AddOption(new Option<bool>("--copy-to-outdir", () => false, "Copy output file to output directory in Release configuration"));
AddOption(new Option<string>("--filter", () => string.Empty, "Filter project files"));
}

internal new class Handler(ILogger<Handler> logger, IProjectService projectService, ILicenseService licenseService) : ICommandHandler
{
public string? ScanDir { get; set; }
public string? OutputFilename { get; set; }
public bool CopyToOutDir { get; set; }
public string? Filter { get; set; }
public bool Merge { get; set; }
private readonly Dictionary<string, List<ResolvedFileInfo>> _licenseContents = [];
private readonly List<ResolvedFileInfo> _unresolvedFiles = [];

public int Invoke(InvocationContext context)
{
return 0;
}

public async Task<int> InvokeAsync(InvocationContext context)
{
MSBuildLocator.RegisterDefaults();
ScanDir ??= Directory.GetCurrentDirectory();
var projectFilePaths = projectService.GetProjectFilePaths(ScanDir);
projectFilePaths = GetFilteredProjectPathes(projectFilePaths);
if (projectFilePaths.Length == 0)
{
logger.LogError("No C# or F# project file found in the directory");
return 0;
}
foreach (var projectFilePath in projectFilePaths)
await ScanProjectAsync(projectFilePath);
if (!CopyToOutDir)
await GenerateOutputFileAsync(OutputFilename);
return 0;
}

private string[] GetFilteredProjectPathes(string[] projectPathes)
{
if (string.IsNullOrEmpty(Filter))
return projectPathes;
var filterRegex = new Regex(Filter, RegexOptions.None, TimeSpan.FromMilliseconds(300));
return projectPathes.Where(x => filterRegex.IsMatch(x)).ToArray();
}

private async Task ScanProjectAsync(string projectFilePath)
{
var stopWatch = new Stopwatch();
stopWatch.Start();
logger.LogInformation("Resolving files for {ProjectName}...", Path.GetFileName(projectFilePath));
var project = new Project(projectFilePath);
project.SetProperty("Configuration", "Release");
project.SetProperty("DesignTimeBuild", "true");
var resolvedFiles = projectService.ResolveFiles(project).ToList();
logger.LogInformation("Resolved files count: {ResolvedFilesCount}", resolvedFiles.Count);
foreach (var resolvedFileInfo in resolvedFiles)
{
logger.LogInformation("Resolving license for {RelativeOutputPath}", resolvedFileInfo.RelativeOutputPath);
if (resolvedFileInfo.NuSpec != null)
{
logger.LogInformation("Package: {NuSpecId}", resolvedFileInfo.NuSpec.Id);
}
else
{
logger.LogWarning("Package not found");
}
var licenseContent = await licenseService.ResolveFromResolvedFileInfo(resolvedFileInfo);
if (licenseContent == null)
{
_unresolvedFiles.Add(resolvedFileInfo);
logger.LogError("No license found for {RelativeOutputPath}. Source path: {SourcePath}. Verify this manually", resolvedFileInfo.RelativeOutputPath, resolvedFileInfo.SourcePath);
continue;
}
if (!_licenseContents.ContainsKey(licenseContent))
_licenseContents[licenseContent] = [];
_licenseContents[licenseContent].Add(resolvedFileInfo);
}
stopWatch.Stop();
logger.LogInformation("Project {ProjectName} resolved in {StopwatchElapsedMilliseconds}ms", Path.GetFileName(projectFilePath), stopWatch.ElapsedMilliseconds);
if (CopyToOutDir && !string.IsNullOrEmpty(ScanDir) && !string.IsNullOrEmpty(OutputFilename))
await GenerateOutputFileAsync(Path.Combine(ScanDir, project.GetPropertyValue("OutDir"), Path.GetFileName(OutputFilename)));
}

private async Task GenerateOutputFileAsync(string? outputFilePath)
{
if (outputFilePath == null)
return;
logger.LogInformation("Resolved {LicenseContentsCount} licenses for {Sum}/{ResolvedFilesCount} files", _licenseContents.Count, _licenseContents.Values.Sum(v => v.Count), _licenseContents.Values.Sum(v => v.Count) + _unresolvedFiles.Count);
logger.LogInformation("Unresolved files: {UnresolvedFilesCount}", _unresolvedFiles.Count);
var stopWatch = new Stopwatch();
stopWatch.Start();
var stringBuilder = new StringBuilder();
foreach (var (licenseContent, resolvedFileInfos) in _licenseContents)
{
var longestNameLen = 0;
foreach (var resolvedFileInfo in resolvedFileInfos)
{
var strLen = resolvedFileInfo.RelativeOutputPath?.Length ?? 0;
if (strLen > longestNameLen)
longestNameLen = strLen;
stringBuilder.AppendLine(resolvedFileInfo.RelativeOutputPath);
}
stringBuilder.AppendLine(new string('-', longestNameLen));
stringBuilder.AppendLine(licenseContent);
stringBuilder.AppendLine();
}
stopWatch.Stop();
logger.LogInformation("Generate licenses in {StopwatchElapsedMilliseconds}ms", stopWatch.ElapsedMilliseconds);
if (stringBuilder.Length == 0)
return;
logger.LogInformation("Writing to {OutputFilename}...", outputFilePath);
await System.IO.File.WriteAllTextAsync(outputFilePath, stringBuilder.ToString());
_licenseContents.Clear();
_unresolvedFiles.Clear();
}
}
}
8 changes: 7 additions & 1 deletion src/DotnetThirdPartyNotices/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
<PackageVersion Include="Microsoft.Build.Locator" Version="1.7.1" />
<PackageVersion Include="Microsoft.Build.Tasks.Core" Version="17.9.5" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.9.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="Microsoft.NET.StringTools" Version="17.9.5" />
<PackageVersion Include="System.CodeDom" Version="8.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
<PackageVersion Include="System.CommandLine.Hosting" Version="0.4.0-alpha.22272.1" />
<PackageVersion Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.0" />
<PackageVersion Include="System.Formats.Asn1" Version="8.0.0" />
Expand All @@ -27,4 +33,4 @@
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="8.0.0" />
</ItemGroup>
</Project>
</Project>
17 changes: 11 additions & 6 deletions src/DotnetThirdPartyNotices/DotnetThirdPartyNotices.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
<PackAsTool>true</PackAsTool>
<ToolCommandName>dotnet-thirdpartynotices</ToolCommandName>
<PackageId>DotnetThirdPartyNotices</PackageId>
<Description>A .NET tool to generate file with third party legal notices</Description>
<PackageVersion>0.2.8</PackageVersion>
<PackageVersion>0.2.9</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/bugproof/DotnetThirdPartyNotices</RepositoryUrl>
<PackageProjectUrl>https://github.com/bugproof/DotnetThirdPartyNotices</PackageProjectUrl>
<RootNamespace>DotnetThirdPartyNotices</RootNamespace>
<AssemblyName>DotnetThirdPartyNotices</AssemblyName>
<NoWarn>1591</NoWarn>
<Authors>bugproof</Authors>
<Copyright>bugproof</Copyright>
<NeutralLanguage>en</NeutralLanguage>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>

<ItemGroup>
Expand All @@ -24,12 +31,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.Build" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" />
<PackageReference Include="Microsoft.Build.Framework" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Tasks.Core" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" ExcludeAssets="runtime" />
<PackageReference Include="Serilog" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="System.CommandLine.Hosting" />
</ItemGroup>

</Project>
14 changes: 0 additions & 14 deletions src/DotnetThirdPartyNotices/Extensions/AssemblyExtensions.cs

This file was deleted.

110 changes: 0 additions & 110 deletions src/DotnetThirdPartyNotices/Extensions/ProjectExtensions.cs

This file was deleted.

Loading

0 comments on commit 924e58f

Please sign in to comment.