Skip to content
New issue

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

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

Already on GitHub? # to your account

add slnx support #130

Merged
merged 4 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
30 changes: 4 additions & 26 deletions .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest]

framework: [net6.0, net8.0, net9.0]
framework: [net8.0, net9.0]

include:
- framework: net6.0
dotnetVersion: "6.0.x"

- framework: net8.0
dotnetVersion: "8.0.x"

Expand All @@ -30,11 +27,6 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup dotnet 6.0.x
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x

- name: Setup dotnet 8.0.x
uses: actions/setup-dotnet@v4
with:
Expand All @@ -59,12 +51,9 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
framework: [net6.0, net8.0, net472, net9.0]
framework: [net8.0, net472, net9.0]

include:
- framework: net6.0
dotnetVersion: "6.0.x"

- framework: net8.0
dotnetVersion: "8.0.x"

Expand All @@ -73,11 +62,6 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup dotnet 6.0.x
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x

- name: Setup dotnet 8.0.x
uses: actions/setup-dotnet@v4
with:
Expand Down Expand Up @@ -127,13 +111,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
framework: [net6.0, net8.0, net9.0]
framework: [net8.0, net9.0]
project: [App, Tests, ProjectWithReferenceContainingLicenseExpression]

include:
- framework: net6.0
dotnetVersion: "6.0.x"

- framework: net8.0
dotnetVersion: "8.0.x"

Expand Down Expand Up @@ -218,12 +199,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
framework: [net6.0, net8.0, net9.0]
framework: [net8.0, net9.0]

include:
- framework: net6.0
dotnetVersion: "6.0.x"

- framework: net8.0
dotnetVersion: "8.0.x"

Expand Down
6 changes: 0 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup dotnet 6.0.x
uses: actions/setup-dotnet@v4
with:
dotnet-version: "6.0.x"
- name: Setup dotnet 8.0.x
uses: actions/setup-dotnet@v4
with:
Expand Down Expand Up @@ -54,8 +50,6 @@ jobs:
echo "publish to path: $path"
echo "path=$path" >> $env:GITHUB_OUTPUT

- name: Publish the application binaries (.net6)
run: dotnet publish ./src/NuGetLicenseCore/NuGetLicenseCore.csproj -c Release --no-restore -o ${{ steps.artifacts_path.outputs.path }}/net6 -f net6.0 -p:Version=${{ steps.version.outputs.full_without_prefix }}
- name: Publish the application binaries (.net8)
run: dotnet publish ./src/NuGetLicenseCore/NuGetLicenseCore.csproj -c Release --no-restore -o ${{ steps.artifacts_path.outputs.path }}/net8 -f net8.0 -p:Version=${{ steps.version.outputs.full_without_prefix }}
- name: Publish the application binaries (.net9)
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetLicenseCore/NuGetLicenseCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<IsPackable>true</IsPackable>
<PackAsTool>true</PackAsTool>
<PackageType>DotnetTool</PackageType>
Expand Down
13 changes: 13 additions & 0 deletions src/NuGetUtility/Extensions/TaskExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

namespace NuGetUtility.Extensions
{
public static class TaskExtensions
{
public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(this IEnumerable<T> enumeration, Func<T, Task<IEnumerable<T1>>> func)
{
return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s);
}
}
}
3 changes: 2 additions & 1 deletion src/NuGetUtility/NuGetUtility.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net472;net8.0;net9.0</TargetFrameworks>
<RepositoryType>git</RepositoryType>
<Version>100.100.100</Version>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
Expand All @@ -27,6 +27,7 @@
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.7.8" />
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" Version="1.0.28" />
<PackageReference Include="NuGet.Commands" Version="6.12.1" />
<PackageReference Include="NuGet.Packaging" Version="6.12.1" />
<PackageReference Include="Tethys.SPDX.ExpressionParser" Version="2.1.2" />
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetUtility/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
ignoredPackages);

string[] excludedProjects = GetExcludedProjects();
IEnumerable<string> projects = inputFiles.SelectMany(projectCollector.GetProjects).Where(p => !Array.Exists(excludedProjects, ignored => p.Like(ignored)));
IEnumerable<string> projects = (await inputFiles.SelectManyAsync(projectCollector.GetProjectsAsync)).Where(p => !Array.Exists(excludedProjects, ignored => p.Like(ignored)));
IEnumerable<ProjectWithReferencedPackages> packagesForProject = GetPackagesPerProject(projects, projectReader, out IReadOnlyCollection<Exception>? projectReaderExceptions);
IAsyncEnumerable<ReferencedPackageWithContext> downloadedLicenseInformation =
packagesForProject.SelectMany(p => GetPackageInformations(p, overridePackageInformation, cancellationToken));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public ProjectsCollector(IMsBuildAbstraction msBuild)
_msBuild = msBuild;
}

public IEnumerable<string> GetProjects(string inputPath)
public async Task<IEnumerable<string>> GetProjectsAsync(string inputPath)
{
return Path.GetExtension(inputPath).Equals(".sln")
? _msBuild.GetProjectsFromSolution(Path.GetFullPath(inputPath)).Where(File.Exists).Select(Path.GetFullPath)
return Path.GetExtension(inputPath).StartsWith(".sln")
? (await _msBuild.GetProjectsFromSolutionAsync(Path.GetFullPath(inputPath))).Where(File.Exists).Select(Path.GetFullPath)
: new[] { Path.GetFullPath(inputPath) };
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ namespace NuGetUtility.Wrapper.MsBuildWrapper
public interface IMsBuildAbstraction
{
IProject GetProject(string projectPath);
IEnumerable<string> GetProjectsFromSolution(string inputPath);
Task<IEnumerable<string>> GetProjectsFromSolutionAsync(string inputPath);
}
}
12 changes: 7 additions & 5 deletions src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;
using Microsoft.VisualStudio.SolutionPersistence;
using Microsoft.VisualStudio.SolutionPersistence.Serializer;

namespace NuGetUtility.Wrapper.MsBuildWrapper
{
Expand Down Expand Up @@ -32,11 +33,12 @@ public IProject GetProject(string projectPath)
return new ProjectWrapper(project);
}

public IEnumerable<string> GetProjectsFromSolution(string inputPath)
public async Task<IEnumerable<string>> GetProjectsFromSolutionAsync(string inputPath)
{
string absolutePath = Path.IsPathRooted(inputPath) ? inputPath : Path.Combine(Environment.CurrentDirectory, inputPath);
var sln = SolutionFile.Parse(absolutePath);
return sln.ProjectsInOrder.Select(p => p.AbsolutePath);
ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(inputPath) ?? throw new MsBuildAbstractionException("Failed to determine serializer for solution");

Microsoft.VisualStudio.SolutionPersistence.Model.SolutionModel model = await serializer.OpenAsync(inputPath, CancellationToken.None);
return model.SolutionProjects.Select(p => p.FilePath);
}

private static void RegisterMsBuildLocatorIfNeeded()
Expand Down
2 changes: 1 addition & 1 deletion tests/NuGetUtility.Test/NuGetUtility.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net472;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
{SolutionDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{SolutionDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{SolutionDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
PackageReferenceProject/PackageReferenceProject.csproj,PackagesConfigProject/PackagesConfigProject.csproj,ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{SolutionDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{SolutionDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{SolutionDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
PackageReferenceProject/PackageReferenceProject.csproj,PackagesConfigProject/PackagesConfigProject.csproj,ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{SolutionDirectory}tests\targets\PackageReferenceProject\PackageReferenceProject.csproj,{SolutionDirectory}tests\targets\PackagesConfigProject\PackagesConfigProject.csproj,{SolutionDirectory}tests\targets\ProjectWithoutNugetReferences\ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests\targets\ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests\targets\ProjectWithTransitiveReferences\ProjectWithTransitiveReferences.csproj
PackageReferenceProject\PackageReferenceProject.csproj,PackagesConfigProject\PackagesConfigProject.csproj,ProjectWithoutNugetReferences\ProjectWithoutNugetReferences.csproj,ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj,ProjectWithTransitiveReferences\ProjectWithTransitiveReferences.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
..\ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,97 +34,102 @@ public void SetUp()
[TestCase("B.fsproj")]
[TestCase("C.vbproj")]
[TestCase("D.dbproj")]
public void GetProjects_Should_ReturnProjectsAsListDirectly(string projectFile)
public async Task GetProjects_Should_ReturnProjectsAsListDirectly(string projectFile)
{
IEnumerable<string> result = _uut.GetProjects(projectFile);
IEnumerable<string> result = await _uut.GetProjectsAsync(projectFile);
Assert.That(result, Is.EqualTo(new[] { Path.GetFullPath(projectFile) }));
_msBuild.DidNotReceive().GetProjectsFromSolution(Arg.Any<string>());
await _msBuild.DidNotReceive().GetProjectsFromSolutionAsync(Arg.Any<string>());
}

[TestCase("A.sln")]
[TestCase("B.sln")]
[TestCase("C.sln")]
public void GetProjects_Should_QueryMsBuildToGetProjectsForSolutionFiles(string solutionFile)
[TestCase("A.slnx")]
public async Task GetProjects_Should_QueryMsBuildToGetProjectsForSolutionFiles(string solutionFile)
{
_ = _uut.GetProjects(solutionFile);
_ = await _uut.GetProjectsAsync(solutionFile);

_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
}

[TestCase("A.sln")]
[TestCase("B.sln")]
[TestCase("C.sln")]
public void GetProjects_Should_ReturnEmptyArray_If_SolutionContainsNoProjects(string solutionFile)
[TestCase("C.slnx")]
public async Task GetProjects_Should_ReturnEmptyArray_If_SolutionContainsNoProjects(string solutionFile)
{
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(Enumerable.Empty<string>());
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult(Enumerable.Empty<string>()));

IEnumerable<string> result = _uut.GetProjects(solutionFile);
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
Assert.That(result, Is.Empty);

_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
}

[TestCase("A.sln")]
[TestCase("B.sln")]
[TestCase("C.sln")]
public void GetProjects_Should_ReturnEmptyArray_If_SolutionContainsProjectsThatDontExist(string solutionFile)
[TestCase("B.slnx")]
public async Task GetProjects_Should_ReturnEmptyArray_If_SolutionContainsProjectsThatDontExist(string solutionFile)
{
IEnumerable<string> projects = _fixture.CreateMany<string>();
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(projects);
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult(projects));

IEnumerable<string> result = _uut.GetProjects(solutionFile);
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
Assert.That(result, Is.Empty);

_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
}

[TestCase("A.sln")]
[TestCase("B.sln")]
[TestCase("C.sln")]
public void GetProjects_Should_ReturnArrayOfProjects_If_SolutionContainsProjectsThatDoExist(string solutionFile)
[TestCase("C.slnx")]
public async Task GetProjects_Should_ReturnArrayOfProjects_If_SolutionContainsProjectsThatDoExist(string solutionFile)
{
string[] projects = _fixture.CreateMany<string>().ToArray();
CreateFiles(projects);
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(projects);
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult<IEnumerable<string>>(projects));

IEnumerable<string> result = _uut.GetProjects(solutionFile);
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
Assert.That(result, Is.EqualTo(projects.Select(Path.GetFullPath)));

_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
}

[TestCase("A.sln")]
[TestCase("B.sln")]
[TestCase("C.sln")]
public void GetProjects_Should_ReturnOnlyExistingProjectsInSolutionFile(string solutionFile)
[TestCase("A.slnx")]
public async Task GetProjects_Should_ReturnOnlyExistingProjectsInSolutionFile(string solutionFile)
{
string[] existingProjects = _fixture.CreateMany<string>().ToArray();
IEnumerable<string> missingProjects = _fixture.CreateMany<string>();

CreateFiles(existingProjects);

_msBuild.GetProjectsFromSolution(Arg.Any<string>())
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>())
.Returns(existingProjects.Concat(missingProjects).Shuffle(54321));

IEnumerable<string> result = _uut.GetProjects(solutionFile);
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
Assert.That(result, Is.EquivalentTo(existingProjects.Select(Path.GetFullPath)));

_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
}

[Test]
public async Task GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileRelativePath()
{
var msbuild = new MsBuildAbstraction();
IEnumerable<string> result = msbuild.GetProjectsFromSolution("../../../../targets/Projects.sln");
IEnumerable<string> result = await msbuild.GetProjectsFromSolutionAsync("../../../../targets/Projects.sln");
await Verify(string.Join(",", result), _osPlatformSpecificVerifySettings);
}

[Test]
public async Task GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileAbsolutePath()
public async Task GetProjectsFromXmlSolution_Should_ReturnProjectsInActualSolutionFileRelativePath()
{
var msbuild = new MsBuildAbstraction();
IEnumerable<string> result = msbuild.GetProjectsFromSolution(Path.GetFullPath("../../../../targets/Projects.sln"));
IEnumerable<string> result = await msbuild.GetProjectsFromSolutionAsync("../../../../targets/slnx/slnx.slnx");
await Verify(string.Join(",", result), _osPlatformSpecificVerifySettings);
}

Expand Down
Loading
Loading