Skip to content

Commit

Permalink
Add basic support for license expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
sensslen committed Mar 13, 2024
1 parent 2c618a3 commit 291f319
Show file tree
Hide file tree
Showing 30 changed files with 1,185 additions and 45 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
strategy:
matrix:
targetFramework: [net6.0, net7.0, net8.0]
project: [App, Tests]
project: [App, Tests, ProjectWithReferenceContainingLicenseExpression]

include:
- targetFramework: net6.0
Expand All @@ -110,7 +110,7 @@ jobs:
dotnet-version: ${{ matrix.dotnetVersion }}

- name: restore
run: dotnet restore -p:TargetFramework=${{ matrix.targetFramework }}
run: dotnet restore NuGetUtility.sln

- name: build
run: dotnet publish ./src/NuGetLicenseCore/NuGetLicenseCore.csproj --configuration Release -o ./release -f ${{ matrix.targetFramework }} --no-restore
Expand All @@ -131,7 +131,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
project: [App, Tests]
project: [App, Tests, ProjectWithReferenceContainingLicenseExpression]

steps:
- uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["MIT","Apache-2.0","MS-EULA"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["NETStandard.Library"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["./integration/ProjectWithReferenceContainingLicenseExpression/ProjectWithReferenceContainingLicenseExpression.csproj"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
30 changes: 28 additions & 2 deletions NuGetUtility.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleCppProject", "tests\targets\SimpleCppProject\SimpleCppProject.vcxproj", "{380FBD90-2CF0-4F83-A58E-EB98CE2EAE15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetLicenseCore", "src\NuGetLicenseCore\NuGetLicenseCore.csproj", "{FBA6622A-C9E3-4250-AB79-35F02CAD2419}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetLicenseCore", "src\NuGetLicenseCore\NuGetLicenseCore.csproj", "{FBA6622A-C9E3-4250-AB79-35F02CAD2419}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetLicenseFramework", "src\NuGetLicenseFramework\NuGetLicenseFramework.csproj", "{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetLicenseFramework", "src\NuGetLicenseFramework\NuGetLicenseFramework.csproj", "{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tethys.SPDX.ExpressionParser", "src\Tethys.SPDX.ExpressionParser\Tethys.SPDX.ExpressionParser.csproj", "{408005E7-D628-477C-A816-59AA4AD3E40C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration", "integration", "{FFB2826C-17A3-4C18-8FFE-A34AB51592F7}"
ProjectSection(SolutionItems) = preProject
integration\ProjectWithReferenceContainingLicenseExpression\ProjectWithReferenceContainingLicenseExpression.csproj = integration\ProjectWithReferenceContainingLicenseExpression\ProjectWithReferenceContainingLicenseExpression.csproj
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -224,6 +231,24 @@ Global
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x64.Build.0 = Debug|Any CPU
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F}.TestWindows|x86.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x64.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x64.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x86.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Debug|x86.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|Any CPU.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x64.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x64.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x86.ActiveCfg = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.Release|x86.Build.0 = Release|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|Any CPU.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|Any CPU.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x64.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x64.Build.0 = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{408005E7-D628-477C-A816-59AA4AD3E40C}.TestWindows|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -240,6 +265,7 @@ Global
{380FBD90-2CF0-4F83-A58E-EB98CE2EAE15} = {FA92392F-D895-4D1E-A5ED-E6DC3C08223E}
{FBA6622A-C9E3-4250-AB79-35F02CAD2419} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
{DE079B9C-B6BA-4D53-8B83-03D3CBD4027F} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
{408005E7-D628-477C-A816-59AA4AD3E40C} = {D2AB2D00-1F48-487D-BFE0-99FDB4E071CC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {70887D40-0182-4C32-BFA1-B5A02E405F11}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Configurations>Debug;Release;TestWindows</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="31.0.2" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/NuGetLicenseCore/NuGetLicenseCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<Platforms>AnyCPU</Platforms>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>A .net tool to print and validate the licenses of .net code. This tool supports .NET (Core), .NET Standard and .NET Framework projects.</Description>
<PackageTags>NuGet;License</PackageTags>
</PropertyGroup>

<ItemGroup>
Expand Down
12 changes: 11 additions & 1 deletion src/NuGetUtility/LicenseValidator/LicenseValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NuGetUtility.Wrapper.NuGetWrapper.Packaging;
using NuGetUtility.Wrapper.NuGetWrapper.Packaging.Core;
using NuGetUtility.Wrapper.NuGetWrapper.Versioning;
using Tethys.SPDX.ExpressionParser;

namespace NuGetUtility.LicenseValidator
{
Expand Down Expand Up @@ -126,7 +127,8 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
case LicenseType.Expression:
case LicenseType.Overwrite:
string licenseId = info.LicenseMetadata!.License;
if (IsLicenseValid(licenseId))
SpdxExpression? licenseExpression = SpdxExpressionParser.Parse(licenseId, _ => true, _ => true);
if (IsValidLicenseExpression(licenseExpression))
{
AddOrUpdateLicense(result,
info,
Expand Down Expand Up @@ -154,6 +156,14 @@ private void ValidateLicenseByMetadata(IPackageMetadata info,
}
}

private bool IsValidLicenseExpression(SpdxExpression? expression) => expression switch
{
SpdxAndExpression and => IsValidLicenseExpression(and.Left) && IsValidLicenseExpression(and.Right),
SpdxOrExpression or => IsValidLicenseExpression(or.Left) || IsValidLicenseExpression(or.Right),
SpdxWithExpression or SpdxLicenseExpression or SpdxLicenseReference => IsLicenseValid(expression.ToString()),
_ => false,
};

private async Task ValidateLicenseByUrl(IPackageMetadata info,
string context,
ConcurrentDictionary<LicenseNameAndVersion, LicenseValidationResult> result,
Expand Down
6 changes: 5 additions & 1 deletion src/NuGetUtility/NuGetUtility.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
Expand Down Expand Up @@ -60,4 +60,8 @@
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Tethys.SPDX.ExpressionParser\Tethys.SPDX.ExpressionParser.csproj" />
</ItemGroup>

</Project>
53 changes: 53 additions & 0 deletions src/Tethys.SPDX.ExpressionParser/SpdxAndExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using System;

namespace Tethys.SPDX.ExpressionParser
{
/// <summary>
/// Represents an SPDX expression.
/// </summary>
public class SpdxAndExpression : SpdxExpression
{
//// Annex D SPDX license expressions
//// compound-expression "AND" compound-expression

#region PUBLIC PROPERTIES
/// <summary>
/// Gets the left side of the expression.
/// </summary>
public SpdxExpression Left { get; }

/// <summary>
/// Gets the right side of the expression.
/// </summary>
public SpdxExpression Right { get; }
#endregion // PUBLIC PROPERTIES

//// ---------------------------------------------------------------------

#region CONSTRUCTION
/// <summary>
/// Initializes a new instance of the <see cref="SpdxAndExpression"/> class.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
public SpdxAndExpression(SpdxExpression? left, SpdxExpression? right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
} // SpdxAndExpression()
#endregion // CONSTRUCTION

//// ---------------------------------------------------------------------

#region PUBLIC METHODS
/// <inheritdoc />
public override string ToString()
{
return $"{Left.ToString()} AND {Right.ToString()}";
} // ToString()
#endregion // PUBLIC METHODS
} // SpdxAndExpression
}
42 changes: 42 additions & 0 deletions src/Tethys.SPDX.ExpressionParser/SpdxExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

namespace Tethys.SPDX.ExpressionParser
{
/*************************************************************************
* SPDX Expressions
* ----------------
* idstring = 1*(ALPHA / DIGIT / "-" / "." )
*
* license-id = <short form license identifier in Annex A.1>
*
* license-exception-id = <short form license exception identifier in Annex A.2>
*
* license-ref = ["DocumentRef-"(idstring)":"]"LicenseRef-"(idstring)
*
* simple-expression = license-id / license-id"+" / license-ref
*
* compound-expression = (simple-expression /
* simple-expression "WITH" license-exception-id /
* compound-expression "AND" compound-expression /
* compound-expression "OR" compound-expression /
* "(" compound-expression ")" )
*
* license-expression = (simple-expression / compound-expression)
*
************************************************************************/

/// <summary>
/// Represents an SPDX expression.
/// </summary>
public abstract class SpdxExpression
{
/// <summary>
/// Converts an <see cref="SpdxExpression"/> to a string.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public new abstract string ToString();
} // SpdxExpression
}
22 changes: 22 additions & 0 deletions src/Tethys.SPDX.ExpressionParser/SpdxExpressionException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using System;

namespace Tethys.SPDX.ExpressionParser
{
/// <summary>
/// Represents an SPDX expression.
/// </summary>
public class SpdxExpressionException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="SpdxExpressionException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public SpdxExpressionException(string message)
: base(message)
{
} // SpdxExpressionException()
} // SpdxExpressionException
}
Loading

0 comments on commit 291f319

Please sign in to comment.