Skip to content

Add option to skip invalid projects#427

Closed
theolivenbaum wants to merge 2 commits into
sensslen:mainfrom
theolivenbaum:main
Closed

Add option to skip invalid projects#427
theolivenbaum wants to merge 2 commits into
sensslen:mainfrom
theolivenbaum:main

Conversation

@theolivenbaum
Copy link
Copy Markdown

@theolivenbaum theolivenbaum commented Feb 8, 2026

Summary by CodeRabbit

  • New Features
    • Added a command-line option (-skip / --skip-invalid-projects) to skip invalid or unsupported project files during package license validation so the run can continue for compatible projects.
  • Tests
    • Updated tests to cover the new skip-invalid-projects option.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 8, 2026

Walkthrough

A new boolean option SkipInvalidProjects was added to CommandLineOptions, exposed as a CLI flag in Program.cs, and propagated into LicenseValidationOrchestrator. GetPackagesPerProject gained a skipInvalidProjects parameter and now conditionally suppresses InvalidProjectFileException and certain ReferencedPackageReaderException occurrences when skipping is requested; non-skippable errors continue to be aggregated and returned via the out exceptions collection.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add option to skip invalid projects' directly and accurately summarizes the main change across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/NuGetLicense/Program.cs`:
- Around line 36-39: The option skipInvalidProjectsOption incorrectly reuses the
aliases "-t" and "--include-transitive" already used by includeTransitiveOption,
causing System.CommandLine to throw on duplicate aliases; change
skipInvalidProjectsOption to unique flags (e.g., "-s" and
"--skip-invalid-projects" or another distinct pair) in its Option<bool>
constructor and update any code that parses or documents that option to use the
new alias names so there are no conflicting symbols between
skipInvalidProjectsOption and includeTransitiveOption.
🧹 Nitpick comments (2)
tests/NuGetLicense.Test/LicenseValidationOrchestratorTest.cs (1)

219-219: Missing test coverage for SkipInvalidProjects = true behavior.

This only sets the flag to false and doesn't assert anything specific to the new option. There's no test that verifies the core feature: when SkipInvalidProjects = true and a project throws InvalidProjectFileException, the exception is silently skipped and processing continues with a zero exit code.

Consider adding a test where _msBuild (or the reader) throws InvalidProjectFileException for a project, and asserting that ValidateAsync returns 0 when SkipInvalidProjects = true (and -1 when false).

src/NuGetLicense/LicenseValidationOrchestrator.cs (1)

157-163: Silently swallowing InvalidProjectFileException with no diagnostic output.

When skipInvalidProjects is true, the invalid project is skipped without any trace — no warning, no log. Users won't know which projects were skipped or why, making debugging harder. Consider writing a warning to the error stream (or at minimum including the project path in a diagnostic message) so users can see what was skipped.

♻️ Sketch — emit a warning when skipping

Since GetPackagesPerProject is static, one approach is to collect skipped project info alongside exceptions:

                 catch (InvalidProjectFileException ipfe)
                 {
                     if (!skipInvalidProjects)
                     {
                         encounteredExceptions.Add(ipfe);
                     }
+                    else
+                    {
+                        // Consider logging: Console.Error.WriteLine($"Warning: Skipping invalid project '{project}': {ipfe.Message}");
+                        // Or collect skipped projects and surface them to the caller.
+                    }
                 }

Comment thread src/NuGetLicense/Program.cs Outdated
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Feb 8, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/NuGetLicense/LicenseValidationOrchestrator.cs`:
- Around line 157-170: The catch blocks for InvalidProjectFileException and
ReferencedPackageReaderException currently drop exceptions when
skipInvalidProjects is true, leaving users unaware of skipped projects; modify
the static method that contains these catches (referencing skipInvalidProjects
and encounteredExceptions) to surface skipped exceptions by either adding an out
IReadOnlyCollection<Exception> skippedExceptions or an Action<Exception>
logSkipped callback parameter, and when skipInvalidProjects is true call the
logger/callback (or add the exception to the out collection) instead of silently
discarding; update all callers to pass the new out parameter or callback and
document that callers should write to _errorStream if they want console
warnings.
- Line 164: The current catch in LicenseValidationOrchestrator.cs using "catch
(ReferencedPackageReaderException rpre) when (rpre.Message.Contains(\"not
compatible with\"))" is dead and misses the "Target framework not found" case
from ReferencedPackageReader.cs; replace the string-filter approach by adding a
discriminant to ReferencedPackageReaderException (e.g., a FailureKind or
ErrorCode property or an IsFrameworkRelated boolean) at the throw sites in
ReferencedPackageReader (including the throw at ReferencedPackageReader.cs:63),
set the appropriate value for framework-related failures, and change the catch
to "catch (ReferencedPackageReaderException rpre) when (rpre.FailureKind ==
FailureKind.FrameworkNotFound)" (or similar), or alternatively, if you must keep
string checks, expand them to cover all framework-related messages and use
rpre.Message.Contains(..., StringComparison.OrdinalIgnoreCase); update all throw
sites accordingly so framework errors are consistently classified and skipped
when skipInvalidProjects is set.

Comment thread src/NuGetLicense/LicenseValidationOrchestrator.cs
Comment thread src/NuGetLicense/LicenseValidationOrchestrator.cs
@theolivenbaum
Copy link
Copy Markdown
Author

@sensslen tiny PR to add a new flag to allow ignoring errors when loading not compatible projects.

@sensslen
Copy link
Copy Markdown
Owner

sensslen commented Feb 8, 2026

@theolivenbaum thank you very much for your contribution. I would like to understand more about the issue you are facing. I'm using nuget-license on a multi language project successfully. Though I have to use the .net framework version due to incompatibilities of msbuild for .net core with vcxproj files. This is the main reason the project still builds against .net framework. Have you tried that version?

My main concern here is that nuget-license has always attempted to prioritize correctness over all other aspects. With the newly proposed flag, it's fairly easy to miss nuget packages that may use incompatible licenses...

By the way: one benefit of using the netframework version is that native nuget packages used in native code projects are validated alongside all the managed packages...

@theolivenbaum
Copy link
Copy Markdown
Author

Hi @sensslen , thanks for the quick response! The issue we faced is related to a .pyproj project on our solution, that is not supported by the dotnet tooling and gives an exception when trying to use nuget-license:

Microsoft.Build.Exceptions.InvalidProjectFileException: The imported file "$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" does not exist and appears to be part of a Visual Studio component. This file may require MSBuild.exe in order to be imported successfully, and so may fail to build in the dotnet CLI.   D:\work\mosaik\Library\Curiosity.Library.Python\Curiosity.Library.Python.pyproj
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args)
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject[T1](IElementLocation elementLocation, String resourceName, T1 arg0)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpression(String directoryOfImportingFile, ProjectImportElement importElement, String unescapedExpression, Boolean throwOnFileNotExistsError, List`1& imports)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, SdkResult& sdkResult)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement, SdkResult& sdkResult)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate()
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(IEvaluatorData`4 data, Project project, ProjectRootElement root, ProjectLoadSettings loadSettings, Int32 maxNodeCount, PropertyDictionary`1 environmentProperties, ICollection`1 propertiesFromCommandLine, ILoggingService loggingService, IItemFactory`2 itemFactory, IToolsetProvider toolsetProvider, IDirectoryCacheFactory directoryCacheFactory, ProjectRootElementCacheBase projectRootElementCache, BuildEventContext buildEventContext, ISdkResolverService sdkResolverService, Int32 submissionId, EvaluationContext evaluationContext, Boolean interactive)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext, Boolean interactive)
   at Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, Boolean interactive)
   at Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion)
   at NuGetUtility.Wrapper.MsBuildWrapper.MsBuildAbstraction.GetProject(String projectPath) in /_/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs:line 27
   at NuGetUtility.ReferencedPackagesReader.ReferencedPackageReader.GetInstalledPackages(String projectPath, Boolean includeTransitive, String targetFramework) in /_/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs:line 30
   at NuGetLicense.Program.GetPackagesPerProject(IEnumerable`1 projects, ReferencedPackageReader reader, IReadOnlyCollection`1& exceptions) in /_/src/NuGetLicense/Program.cs:line 330

I tried to use the exclude projects option, but it seems that it is only applied after this step. As a quick fix, I thought a new flag to skip these not-supported projects.

@sensslen
Copy link
Copy Markdown
Owner

sensslen commented Feb 9, 2026

Hi @sensslen , thanks for the quick response! The issue we faced is related to a .pyproj project on our solution, that is not supported by the dotnet tooling and gives an exception when trying to use nuget-license:

Microsoft.Build.Exceptions.InvalidProjectFileException: The imported file "$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" does not exist and appears to be part of a Visual Studio component. This file may require MSBuild.exe in order to be imported successfully, and so may fail to build in the dotnet CLI.   D:\work\mosaik\Library\Curiosity.Library.Python\Curiosity.Library.Python.pyproj
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args)
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject[T1](IElementLocation elementLocation, String resourceName, T1 arg0)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpression(String directoryOfImportingFile, ProjectImportElement importElement, String unescapedExpression, Boolean throwOnFileNotExistsError, List`1& imports)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, SdkResult& sdkResult)
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement, SdkResult& sdkResult)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate()
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(IEvaluatorData`4 data, Project project, ProjectRootElement root, ProjectLoadSettings loadSettings, Int32 maxNodeCount, PropertyDictionary`1 environmentProperties, ICollection`1 propertiesFromCommandLine, ILoggingService loggingService, IItemFactory`2 itemFactory, IToolsetProvider toolsetProvider, IDirectoryCacheFactory directoryCacheFactory, ProjectRootElementCacheBase projectRootElementCache, BuildEventContext buildEventContext, ISdkResolverService sdkResolverService, Int32 submissionId, EvaluationContext evaluationContext, Boolean interactive)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project.ProjectImpl.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext, Boolean interactive)
   at Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, Boolean interactive)
   at Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion)
   at NuGetUtility.Wrapper.MsBuildWrapper.MsBuildAbstraction.GetProject(String projectPath) in /_/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs:line 27
   at NuGetUtility.ReferencedPackagesReader.ReferencedPackageReader.GetInstalledPackages(String projectPath, Boolean includeTransitive, String targetFramework) in /_/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs:line 30
   at NuGetLicense.Program.GetPackagesPerProject(IEnumerable`1 projects, ReferencedPackageReader reader, IReadOnlyCollection`1& exceptions) in /_/src/NuGetLicense/Program.cs:line 330

I tried to use the exclude projects option, but it seems that it is only applied after this step. As a quick fix, I thought a new flag to skip these not-supported projects.

Thanks for sharing more details. Would you be able to share a reproduction example? Also please try wehther the .net framework version of nuget-license actually works with these project types. I'm facing similar issues when I try to use the .net core version (dotnet tool install will always install the core version).

@theolivenbaum
Copy link
Copy Markdown
Author

Indeed the framework version seems to be able to open it. I'll investigate here and let you know in a bit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants