Skip to content

fix invalid license paths#353

Merged
sensslen merged 10 commits into
mainfrom
simon/fix-invalid-license-paths
Dec 2, 2025
Merged

fix invalid license paths#353
sensslen merged 10 commits into
mainfrom
simon/fix-invalid-license-paths

Conversation

@sensslen
Copy link
Copy Markdown
Owner

@sensslen sensslen commented Dec 2, 2025

Fixes #290

Summary by CodeRabbit

  • New Features

    • File-based license mappings and a composed license matcher; new CLI option to supply license-file-to-license mappings.
  • Bug Fixes

    • Improved path normalization for license files and package downloads.
    • CI license steps now consistently enumerate and display downloaded license files.
  • Chores

    • Updated solution and CI assets to cover Windows-specific scenarios and add integration/license assets.
  • Tests

    • Added unit tests for license matching and file-system interactions.
  • Documentation

    • New docs and README examples for license file mappings.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 2, 2025

Walkthrough

Replaces SPDXLicenseMatcher with a new FileLicenseMatcher implementation (code, tests, and renamed namespaces); adds license-file mapping support and CLI option; updates Program and LicenseValidator to use IFileSystem and IFileLicenseMatcher; normalizes package/license file paths via NuGet.Common.PathUtility.GetPathWithDirectorySeparator in WrappedPackageDownloader and GlobalPackagesFolderUtility; adds an integration project ProjectWithReferenceContainingWindowsSpecificPath and related workflow asset files (licenses, mappings, configs); updates solution/project references and CI workflow matrix/steps to pass the new -file-mapping option.

Possibly related PRs

  • attempt to fix release pipeline #327 — Modifies CI workflow test project references and solution entries similarly, overlapping on workflow and solution/project configuration changes.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes substantial out-of-scope changes beyond fixing invalid license paths, including new FileLicenseMatcher component, file-based license mapping feature, and Windows-specific path integration tests. Consider splitting this PR into focused commits: (1) core path normalization fix for issue #290, and (2) new FileLicenseMatcher and file-mapping features in separate PRs for better review and maintainability.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix invalid license paths' clearly and concisely summarizes the main change in the PR, which is fixing license path handling for cross-platform compatibility.
Linked Issues check ✅ Passed The PR successfully implements the core objective from issue #290 by normalizing license paths using NuGet.Common.PathUtility.GetPathWithDirectorySeparator() before retrieving streams in GlobalPackagesFolderUtility and WrappedPackageDownloader.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch simon/fix-invalid-license-paths

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c60ab83 and afbfc6c.

📒 Files selected for processing (3)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/allowedLicenses.json (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json (1 hunks)
  • src/NuGetLicense/LicenseValidator/LicenseValidator.cs (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.
📚 Learning: 2025-11-07T21:33:27.230Z
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.

Applied to files:

  • src/NuGetLicense/LicenseValidator/LicenseValidator.cs
🧬 Code graph analysis (1)
src/NuGetLicense/LicenseValidator/LicenseValidator.cs (1)
src/NuGetLicense/Program.cs (3)
  • IFileLicenseMatcher (343-359)
  • IImmutableDictionary (268-278)
  • IFileDownloader (223-236)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test_file_license_matching (macos-latest)
🔇 Additional comments (6)
.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json (1)

1-3: Test asset correctly supports file-mapping feature.

The mapping file is valid JSON and uses forward slashes, which aligns with the PR's cross-platform path normalization objective. The mapping correctly corresponds to the allowed license in the companion asset file.

To ensure comprehensive test coverage, please verify:

  1. The SPDX identifier "SSA-1.0" is valid and corresponds to Sonar Source Available License 1.0.
  2. This test asset is used by a corresponding integration test that validates license file path normalization on Windows-style paths (e.g., normalizing Licenses\Sonar-Source-Available.txt to Licenses/Sonar-Source-Available.txt).
.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/allowedLicenses.json (1)

1-3: Consistent with companion mapping file.

The allowed license "SSA-1.0" correctly corresponds to the license defined in the accompanying fileToLicenseMapping.json. The JSON array format is valid and properly structured for use as a license whitelist in the integration test.

src/NuGetLicense/LicenseValidator/LicenseValidator.cs (4)

6-6: LGTM on interface migration.

The switch from ILicenseMatcher to IFileLicenseMatcher is consistent across the import, field declaration, and constructor parameter. The wiring from Program.GetLicenseMatcher() confirms the new interface is correctly integrated.

Also applies to: 21-21, 28-28


162-171: Good defensive handling for unmatched licenses.

The early exit when matchedLicense is null or empty prevents downstream parsing errors and provides a clear error message. Storing the original license path (info.LicenseMetadata.License) in the result preserves diagnostic context.


173-180: Correctly parses the matched license identifier.

The logic properly:

  1. Uses the matched SPDX identifier for expression parsing (not the raw file path)
  2. Stores the resolved matchedLicense on success, giving downstream consumers the actual license identifier rather than the file path

182-189: Improved error messaging consistency.

Using GetLicenseNotAllowedMessage(matchedLicense) aligns the error format with the Expression/Overwrite branches (line 154), providing users with the specific license identifier that was rejected.


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.

@sensslen sensslen force-pushed the simon/fix-invalid-license-paths branch from ce64726 to 50a948d Compare December 2, 2025 20:25
@sensslen sensslen force-pushed the simon/fix-invalid-license-paths branch from efb1581 to 3916f5b Compare December 2, 2025 22:08
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: 0

🧹 Nitpick comments (4)
src/FileLicenseMatcher/IFileLicenseMatcher.cs (1)

4-9: Clarify the Match contract (empty string vs no match)

The interface is fine as-is, but the contract that implementations should return an empty string when there is no match is implicit rather than explicit. Consider adding brief XML docs (and/or a named constant) to spell this out so future implementations behave consistently with the existing callers and tests.

src/FileLicenseMatcher/Compare/LicenseMatcher.cs (1)

16-37: Avoid re-splitting licenseText on every iteration

Behavior looks correct (whitespace-based token comparison, skipping missing files, empty string when nothing matches), but licenseText is split inside the loop each time.

You can reduce allocations and CPU a bit by splitting once before the loop:

-        public string Match(string licenseText)
-        {
-            foreach (KeyValuePair<string, string> kvp in _fileLicenseMap)
-            {
+        public string Match(string licenseText)
+        {
+            IEnumerable<string> licenseContent = licenseText.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
+            foreach (KeyValuePair<string, string> kvp in _fileLicenseMap)
+            {
                 if (!_fileSystem.File.Exists(kvp.Key))
                 {
                     continue;
                 }
-                IEnumerable<string> fileContent = _fileSystem.File.ReadAllText(kvp.Key).Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
-                IEnumerable<string> licenseContent = licenseText.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
+                IEnumerable<string> fileContent = _fileSystem.File.ReadAllText(kvp.Key).Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
                 if (licenseContent.SequenceEqual(fileContent))
                 {
                     return kvp.Value;
                 }
-            }
+            }
             return string.Empty;
         }
tests/FileLicenseMatcher.Test/SPDX/LicenseMatcherTest.cs (2)

28-36: Avoid capturing StreamReader in Func<> closures to prevent lifetime issues

In all three data sources you do:

using var reader = new StreamReader(executingAssembly.GetManifestResourceStream(name)!);
yield return () => new Case(expectedIdentifier, reader.ReadToEnd());   // or reader.ReadToEnd() for string

Given TUnit’s MethodDataSource semantics for IEnumerable<Func<T>>, the Func<> can be invoked after the iterator has finished and using var reader has disposed the underlying stream. That risks ObjectDisposedException or framework‑dependent flakiness, especially in AOT mode. (tunit.dev)

Safer pattern is to read the content inside the iterator and capture the string instead of the reader:

-using var reader = new StreamReader(executingAssembly.GetManifestResourceStream(name)!);
-yield return () => new Case(expectedIdentifier, reader.ReadToEnd());
+using var reader = new StreamReader(executingAssembly.GetManifestResourceStream(name)!);
+var content = reader.ReadToEnd();
+yield return () => new Case(expectedIdentifier, content);

and similarly:

-using var reader = new StreamReader(executingAssembly.GetManifestResourceStream(name)!);
-yield return () => reader.ReadToEnd();
+using var reader = new StreamReader(executingAssembly.GetManifestResourceStream(name)!);
+var content = reader.ReadToEnd();
+yield return () => content;

This keeps each Func<> self‑contained and independent of the disposed StreamReader while still giving each test its own Case instance/string.

Also applies to: 45-52, 60-69


73-92: Async assertions with discarded results are fine; consider minor naming cleanups

Using _ = await Assert.That(...).Contains(...) / .IsEmpty() is functionally equivalent to a bare await and is compatible with TUnit’s async assertion model, so there’s no behavioral concern here. (tunit.dev)

Two small nits you might optionally address while touching this area:

  • Line 89: method name Fast_License_Matcher_Should_Mattch_Real_World_Licenses has a typo in “Mattch”.
  • The property FastlicenseMatcher (Line 19) could be renamed to FastLicenseMatcher for consistent PascalCase and readability.

Both are non‑blocking but will improve clarity in test output and code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50a948d and 3916f5b.

📒 Files selected for processing (34)
  • .github/workflows/action.yml (3 hunks)
  • .github/workflows/assets/App/fileToLicenseMapping.json (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingFileLicense/fileToLicenseMapping.json (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingLicenseExpression/fileToLicenseMapping.json (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/Licenses/Sonar-Source-Available.txt (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json (1 hunks)
  • .github/workflows/assets/Tests/fileToLicenseMapping.json (1 hunks)
  • NuGetUtility.sln (4 hunks)
  • src/FileLicenseMatcher/Combine/LicenseMatcher.cs (1 hunks)
  • src/FileLicenseMatcher/Compare/LicenseMatcher.cs (1 hunks)
  • src/FileLicenseMatcher/FileLicenseMatcher.csproj (1 hunks)
  • src/FileLicenseMatcher/IFileLicenseMatcher.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/FastLicenseMatcher.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/ILicenseTemplateOutputHandler.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/InvalidSPDXAnalysisException.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/LicenseParserException.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/LicenseTemplateRule.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/LicenseTemplateRuleException.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/LicenseTextHelper.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/LineColumn.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaCore/SpdxLicenseTemplateHelper.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaLibrary/CompareTemplateOutputHandler.cs (2 hunks)
  • src/FileLicenseMatcher/SPDX/JavaLibrary/DifferenceDescription.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaLibrary/LicenseCompareHelper.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaLibrary/ParseInstruction.cs (1 hunks)
  • src/FileLicenseMatcher/SPDX/JavaLibrary/SpdxCompareException.cs (1 hunks)
  • src/NuGetLicense/LicenseValidator/LicenseValidator.cs (2 hunks)
  • src/NuGetLicense/NuGetLicense.csproj (2 hunks)
  • src/NuGetLicense/Program.cs (11 hunks)
  • src/SPDXLicenseMatcher/LicenseMatcher.cs (0 hunks)
  • tests/FileLicenseMatcher.Test/Combine/LicenseMatcherTest.cs (1 hunks)
  • tests/FileLicenseMatcher.Test/Compare/LicenseMatcherTest.cs (1 hunks)
  • tests/FileLicenseMatcher.Test/FileLicenseMatcher.Test.csproj (1 hunks)
  • tests/FileLicenseMatcher.Test/SPDX/LicenseMatcherTest.cs (5 hunks)
💤 Files with no reviewable changes (1)
  • src/SPDXLicenseMatcher/LicenseMatcher.cs
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/assets/ProjectWithReferenceContainingLicenseExpression/fileToLicenseMapping.json
🚧 Files skipped from review as they are similar to previous changes (15)
  • .github/workflows/assets/ProjectWithReferenceContainingFileLicense/fileToLicenseMapping.json
  • src/FileLicenseMatcher/SPDX/JavaLibrary/CompareTemplateOutputHandler.cs
  • src/FileLicenseMatcher/Combine/LicenseMatcher.cs
  • src/NuGetLicense/NuGetLicense.csproj
  • src/FileLicenseMatcher/FileLicenseMatcher.csproj
  • tests/FileLicenseMatcher.Test/Compare/LicenseMatcherTest.cs
  • src/NuGetLicense/Program.cs
  • src/FileLicenseMatcher/SPDX/JavaLibrary/ParseInstruction.cs
  • src/NuGetLicense/LicenseValidator/LicenseValidator.cs
  • src/FileLicenseMatcher/SPDX/JavaCore/LineColumn.cs
  • src/FileLicenseMatcher/SPDX/JavaLibrary/SpdxCompareException.cs
  • src/FileLicenseMatcher/SPDX/FastLicenseMatcher.cs
  • .github/workflows/action.yml
  • .github/workflows/assets/App/fileToLicenseMapping.json
  • .github/workflows/assets/Tests/fileToLicenseMapping.json
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.
📚 Learning: 2025-11-07T21:33:27.230Z
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.

Applied to files:

  • tests/FileLicenseMatcher.Test/Combine/LicenseMatcherTest.cs
  • NuGetUtility.sln
  • tests/FileLicenseMatcher.Test/FileLicenseMatcher.Test.csproj
  • tests/FileLicenseMatcher.Test/SPDX/LicenseMatcherTest.cs
🧬 Code graph analysis (7)
src/FileLicenseMatcher/SPDX/JavaCore/LicenseTextHelper.cs (2)
tests/SPDXLicenseMatcher.Test/LicenseMatcherTest.cs (6)
  • SPDXLicenseMatcher (4-106)
  • LicenseMatcherTest (6-105)
  • n (29-29)
  • Case (8-8)
  • AllSpdxLicensesFastLicenseMatcher (10-14)
  • Match (13-13)
src/SPDXLicenseMatcher/FastLicenseMatcher.cs (1)
  • SPDXLicenseMatcher (14-181)
tests/FileLicenseMatcher.Test/Combine/LicenseMatcherTest.cs (1)
tests/SPDXLicenseMatcher.Test/LicenseMatcherTest.cs (9)
  • SPDXLicenseMatcher (4-106)
  • LicenseMatcherTest (6-105)
  • n (29-29)
  • NonSPDXLicensesTestSource (39-52)
  • SPDXLicensesTestSource (20-36)
  • Test (85-90)
  • Test (99-104)
  • Match (13-13)
  • Test (92-97)
src/FileLicenseMatcher/SPDX/JavaCore/SpdxLicenseTemplateHelper.cs (2)
tests/SPDXLicenseMatcher.Test/LicenseMatcherTest.cs (8)
  • SPDXLicenseMatcher (4-106)
  • LicenseMatcherTest (6-105)
  • n (29-29)
  • NonSPDXLicensesTestSource (39-52)
  • SPDXLicensesTestSource (20-36)
  • Case (8-8)
  • Match (13-13)
  • Test (85-90)
src/SPDXLicenseMatcher/FastLicenseMatcher.cs (2)
  • SPDXLicenseMatcher (14-181)
  • FastLicenseMatcher (20-180)
src/FileLicenseMatcher/SPDX/JavaLibrary/LicenseCompareHelper.cs (2)
src/SPDXLicenseMatcher/FastLicenseMatcher.cs (1)
  • SPDXLicenseMatcher (14-181)
src/SPDXLicenseMatcher/JavaLibrary/ParseInstruction.cs (1)
  • ParseInstruction (11-708)
src/FileLicenseMatcher/Compare/LicenseMatcher.cs (1)
src/SPDXLicenseMatcher/FastLicenseMatcher.cs (2)
  • SPDXLicenseMatcher (14-181)
  • FastLicenseMatcher (20-180)
tests/FileLicenseMatcher.Test/FileLicenseMatcher.Test.csproj (1)
tests/SPDXLicenseMatcher.Test/LicenseMatcherTest.cs (3)
  • SPDXLicenseMatcher (4-106)
  • NonSPDXLicensesTestSource (39-52)
  • LicenseMatcherTest (6-105)
src/FileLicenseMatcher/SPDX/JavaLibrary/DifferenceDescription.cs (1)
src/SPDXLicenseMatcher/JavaLibrary/ParseInstruction.cs (2)
  • DifferenceDescription (510-524)
  • setLastOptionalDifference (533-541)
🪛 LanguageTool
.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/Licenses/Sonar-Source-Available.txt

[style] ~49-~49: Consider a more concise word here.
Context: ...iles of the Program solely in each case in order to link to, bind by name, or subclass the ...

(IN_ORDER_TO_PREMIUM)


[style] ~118-~118: Consider replacing this phrase with the adverb “reasonably” to avoid wordiness.
Context: ... and inform Recipients how to obtain it in a reasonable manner on or through a medium customarily used...

(IN_A_X_MANNER)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: check_licenses_net472 (Tests)
  • GitHub Check: check_licenses_net472 (App)
  • GitHub Check: check_version_command_net472
  • GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingLicenseExpression)
  • GitHub Check: test_windows (net472)
  • GitHub Check: test_windows (net9.0)
  • GitHub Check: test_windows (net10.0)
  • GitHub Check: test_windows (net8.0)
🔇 Additional comments (22)
src/FileLicenseMatcher/SPDX/JavaCore/LicenseTemplateRuleException.cs (1)

11-11: Namespace rename is consistent with FileLicenseMatcher refactor

The updated namespace aligns with the new FileLicenseMatcher.SPDX.* structure and doesn’t introduce any behavioral changes. Looks good as part of the consolidation away from SPDXLicenseMatcher.*.

.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json (1)

1-3: Well-formed JSON with correct path normalization.

The mapping correctly uses forward slashes for cross-platform compatibility, and the file path aligns with the actual license file location. This approach properly demonstrates Windows-specific path handling for the asset.

.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/Licenses/Sonar-Source-Available.txt (1)

1-184: Official license text appropriately included.

The complete SONAR Source-Available License v1.0 is properly added as a test asset. The file content should remain unmodified since it represents the official license terms.

Note: Static analysis flagged style suggestions within this license text, but these are false positives—official license text should not be altered for style preferences to maintain legal accuracy and integrity.

src/FileLicenseMatcher/SPDX/JavaCore/LicenseTextHelper.cs (1)

15-15: Namespace move is fine; just confirm external consumer impact

Moving LicenseTextHelper to FileLicenseMatcher.SPDX.JavaCore aligns with the new FileLicenseMatcher layout and doesn’t change behavior. Because the class is public, this is a breaking namespace change for any external callers referencing SPDXLicenseMatcher.JavaCore.LicenseTextHelper; please confirm all in‑repo and downstream consumers are updated or intentionally dropped.

src/FileLicenseMatcher/SPDX/JavaCore/InvalidSPDXAnalysisException.cs (1)

11-11: Namespace rename is straightforward; verify external impact of this public type

Changing the namespace to FileLicenseMatcher.SPDX.JavaCore is consistent with the broader refactor and is technically fine, but InvalidSPDXAnalysisException is public, so any external code using the old SPDXLicenseMatcher.JavaCore namespace will break. Please verify that:

  • All references inside this repo are updated to the new namespace, and
  • If this assembly is consumed externally (e.g., via NuGet), you’re comfortable with the breaking change or have a migration strategy (type forwarders, major version bump, or a transitional package).
NuGetUtility.sln (4)

3-4: Verify Visual Studio version bump is intentional.

The Visual Studio version has been bumped from 17 to 18 (VS 2022 17.11+). Ensure all team members have compatible IDE versions to avoid solution loading issues.


58-65: LGTM! New projects properly structured.

The new projects (FileLicenseMatcher, FileLicenseMatcher.Test, and integration test projects) are correctly added with unique GUIDs and follow the established folder structure.


415-468: LGTM! Build configurations properly set.

All new projects have complete configuration mappings across Debug, Release, and TestWindows configurations for all platforms (Any CPU, x64, x86).


494-496: LGTM! Project nesting is correct.

The new projects are properly nested within their respective solution folders (integration, src, tests).

Based on learnings, the previous SPDXLicenseMatcher.Test.dll was intentionally excluded from release workflow tests using the pattern NuGet*.Test.dll. Since FileLicenseMatcher.Test replaces SPDXLicenseMatcher.Test, verify whether FileLicenseMatcher.Test.dll should also be excluded from release workflow tests or if the pattern needs updating.

src/FileLicenseMatcher/SPDX/JavaCore/SpdxLicenseTemplateHelper.cs (1)

13-13: No action needed. SpdxLicenseTemplateHelper is part of an internal project structure (FileLicenseMatcher is a project dependency referenced in NuGetLicense, not a published library). The namespace change is a clean internal refactor with no external API compatibility concerns.

Likely an incorrect or invalid review comment.

src/FileLicenseMatcher/SPDX/JavaCore/LicenseParserException.cs (1)

11-11: Namespace move looks correct

Namespace FileLicenseMatcher.SPDX.JavaCore matches the new layout; no behavioral impact.

src/FileLicenseMatcher/SPDX/JavaCore/ILicenseTemplateOutputHandler.cs (1)

9-9: Namespace rename is consistent

Interface is unchanged apart from moving to FileLicenseMatcher.SPDX.JavaCore; this aligns with the new SPDX organization.

src/FileLicenseMatcher/SPDX/JavaLibrary/DifferenceDescription.cs (1)

5-7: Using and namespace updates are aligned

Switch to FileLicenseMatcher.SPDX.JavaCore and FileLicenseMatcher.SPDX.JavaLibrary matches the new SPDX layout; DifferenceDescription behavior remains unchanged.

src/FileLicenseMatcher/SPDX/JavaCore/LicenseTemplateRule.cs (1)

13-13: Namespace relocation only

LicenseTemplateRule is just moved to FileLicenseMatcher.SPDX.JavaCore; parsing and validation logic are untouched.

src/FileLicenseMatcher/SPDX/JavaLibrary/LicenseCompareHelper.cs (1)

10-13: SPDX helper now correctly references FileLicenseMatcher namespaces

The added using FileLicenseMatcher.SPDX.JavaCore; and namespace FileLicenseMatcher.SPDX.JavaLibrary keep LicenseCompareHelper wired up to the moved SPDX JavaCore/JavaLibrary types without changing behavior.

tests/FileLicenseMatcher.Test/Combine/LicenseMatcherTest.cs (1)

1-60: Combine matcher tests cover key behaviors

The tests clearly assert the intended semantics for the combinator (empty result when no matchers, first non-empty match wins, and short-circuiting). They also encode the empty-string-as-"no match" convention used by IFileLicenseMatcher. Looks good.

tests/FileLicenseMatcher.Test/FileLicenseMatcher.Test.csproj (4)

18-18: NSubstitute dependency addition is appropriate.

The addition of NSubstitute 5.3.0 is a common mocking library for unit tests and aligns with the test infrastructure for the new FileLicenseMatcher implementation.


28-28: ProjectReference path is correct.

The FileLicenseMatcher.csproj file exists at src/FileLicenseMatcher/FileLicenseMatcher.csproj, and the relative path ..\..\src\FileLicenseMatcher\FileLicenseMatcher.csproj from the test project directory resolves correctly.


32-34: The embedded resource paths and test code PREFIX patterns are correctly aligned.

Verification confirms:

  1. All three resource directories exist under tests/FileLicenseMatcher.Test/SPDX/ (SPDXLicenses, NonSpdxTestLicenses, RealLicenses)
  2. Test code PREFIX constants in LicenseMatcherTest.cs are properly updated with the SPDX. segment: "FileLicenseMatcher.Test.SPDX.SPDXLicenses.", "FileLicenseMatcher.Test.SPDX.NonSpdxTestLicenses.", and "FileLicenseMatcher.Test.SPDX.RealLicenses."
  3. 665 resource files are present across the three directories

The reorganization has been properly implemented throughout the codebase.


24-24: Verify.TUnit 31.8.0 is compatible with TUnit 1.3.15.

Verify.TUnit requires TUnit.Core >= 0.87.8, and TUnit 1.3.15 ships with TUnit.Core in the 1.x series (e.g., 1.2.11), which satisfies this constraint. The patch update is safe.

tests/FileLicenseMatcher.Test/SPDX/LicenseMatcherTest.cs (2)

4-16: Namespace and matcher wiring look consistent with new FileLicenseMatcher.SPDX API

using FileLicenseMatcher.SPDX;, the FileLicenseMatcher.Test.SPDX namespace, and AllSpdxLicensesFastLicenseMatcher : IFileLicenseMatcher all line up with the new matcher abstraction and TUnit’s ClassDataSource pattern; no issues from a wiring or DI perspective.

One minor nit (pre‑existing): FastlicenseMatcher (Line 19) violates usual PascalCase (FastLicenseMatcher). Consider renaming when convenient to keep property names aligned with .NET conventions.

Also, given the earlier workflow pattern NuGet*.Test.dll intentionally excluded the old SPDX tests, please double‑check that the new test assembly name still matches your intended CI coverage (i.e., either remains excluded or is now intentionally included). Based on learnings.


25-60: Updated embedded‑resource prefixes must match assembly default namespace

The new PREFIX values:

  • Line 25: "FileLicenseMatcher.Test.SPDX.SPDXLicenses."
  • Line 44: "FileLicenseMatcher.Test.SPDX.NonSpdxTestLicenses."
  • Line 58: "FileLicenseMatcher.Test.SPDX.RealLicenses."

are correct assuming:

  • The test project’s default namespace is FileLicenseMatcher.Test.SPDX, and
  • The embedded resources live under SPDXLicenses, NonSpdxTestLicenses, and RealLicenses folders with Build Action = Embedded resource.

If either the default namespace or folder structure differs, GetManifestResourceNames() will return names that don’t start with these prefixes and the data sources will silently yield zero cases. Please run the tests once with --verbosity normal (or inspect the DLL with something like ildasm / dotnet tool of your choice) to confirm the resource names actually start with these prefixes.

@sensslen sensslen force-pushed the simon/fix-invalid-license-paths branch from 74266a4 to 120cb7f Compare December 2, 2025 22:19
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3916f5b and d9ba265.

📒 Files selected for processing (6)
  • .github/workflows/action.yml (5 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json (1 hunks)
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/ignorePackages.json (1 hunks)
  • integration/ProjectWithReferenceContainingWindowsSpecificPath/ProjectWithReferenceContainingWindowsSpecificPath.csproj (1 hunks)
  • src/FileLicenseMatcher/Compare/LicenseMatcher.cs (1 hunks)
  • src/NuGetLicense/Program.cs (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • integration/ProjectWithReferenceContainingWindowsSpecificPath/ProjectWithReferenceContainingWindowsSpecificPath.csproj
  • .github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/fileToLicenseMapping.json
  • src/FileLicenseMatcher/Compare/LicenseMatcher.cs
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.
📚 Learning: 2025-11-07T21:33:27.230Z
Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern `NuGet*.Test.dll` intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.

Applied to files:

  • .github/workflows/action.yml
🧬 Code graph analysis (1)
src/NuGetLicense/Program.cs (1)
src/NuGetUtility/Wrapper/HttpClientWrapper/NopFileDownloader.cs (1)
  • NopFileDownloader (6-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingWindowsSpecificPath)
  • GitHub Check: check_version_command (net10.0)
  • GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingLicenseExpression)
  • GitHub Check: check_version_command (net8.0)
  • GitHub Check: check_licenses_net472 (App)
  • GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingFileLicense)
  • GitHub Check: check_licenses_net472 (Tests)
  • GitHub Check: test_windows (net9.0)
  • GitHub Check: check_version_command_net472
  • GitHub Check: test_windows (net472)
  • GitHub Check: test_windows (net10.0)
  • GitHub Check: test_windows (net8.0)
  • GitHub Check: test (macos-latest, net9.0)
  • GitHub Check: test_file_license_matching (windows-latest)
  • GitHub Check: test (macos-latest, net8.0)
  • GitHub Check: test_file_license_matching (macos-latest)
  • GitHub Check: test_file_license_matching (ubuntu-latest)
🔇 Additional comments (10)
.github/workflows/assets/ProjectWithReferenceContainingWindowsSpecificPath/ignorePackages.json (1)

1-3: JSON structure is valid.

The ignorePackages.json file is syntactically valid and appropriately ignores NETStandard.Library for the Windows-specific path project.

.github/workflows/action.yml (3)

147-147: All required asset files are properly configured for the new project. The matrix expansion to include ProjectWithReferenceContainingWindowsSpecificPath at lines 147 and 184 is fully supported by complete asset directories with all required configuration files.


169-169: Ensure -file-mapping parameter files exist for all projects.

The -file-mapping parameter is added to both check_licenses and check_licenses_net472 jobs, referencing fileToLicenseMapping.json for each project. Verify that this file exists for all five projects in the matrix (App, Tests, ProjectWithReferenceContainingLicenseExpression, ProjectWithReferenceContainingFileLicense, ProjectWithReferenceContainingWindowsSpecificPath).

Also applies to: 215-215


72-72: No changes needed. The FileLicenseMatcher.Test project targets only net9.0, so the absence of a framework matrix is correct and intentional. Unlike the other test jobs that run against multiple framework versions (net8.0, net9.0, net10.0), this test project does not require framework matrix testing. The OS matrix (ubuntu-latest, macos-latest, windows-latest) appropriately handles platform-specific compatibility verification.

src/NuGetLicense/Program.cs (6)

5-5: LGTM! Good refactoring for testability.

The addition of System.IO.Abstractions enables testable file system operations, and FileLicenseMatcher provides the new license matching capabilities.

Also applies to: 8-8


119-122: LGTM! Clear and consistent option definition.

The new command-line option follows the existing naming conventions and provides a clear description of its purpose.


132-149: LGTM! Systematic refactoring to use IFileSystem abstraction.

The file system abstraction is properly initialized and consistently passed to all dependent methods. This improves testability while maintaining clean production code.


178-185: LGTM! Proper use of IFileSystem abstraction.

The method correctly delegates file operations to the provided file system abstraction, maintaining the same functionality while improving testability.


223-236: LGTM! Consistent refactoring for directory operations.

Directory existence checks and creation now properly use the file system abstraction.


246-318: LGTM! Consistent refactoring across all file-reading methods.

All configuration file reading operations now properly use the file system abstraction, maintaining consistency throughout the codebase.

Comment thread src/NuGetLicense/Program.cs
sensslen and others added 2 commits December 2, 2025 23:47
…ion behavior (#354)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sensslen <3428860+sensslen@users.noreply.github.com>
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

🧹 Nitpick comments (1)
docs/licensefile-mappings-json.md (1)

41-48: Add language identifier to fenced code block.

For consistency and to satisfy the linter, specify a language identifier for the fenced code block. Use text or plaintext for directory structures.

Apply this diff:

-```
+```text
 project/
 ├── config/
 │   └── license-mappings.json
 └── licenses/
     ├── MIT.txt
     └── Apache-2.0.txt

Based on static analysis hints.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9ba2654f29e16c7ec0a4ede7f8566df02ebf64c and c60ab839badfe3111b0d666a23d2b87df5802504.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `README.md` (2 hunks)
* `docs/licensefile-mappings-json.md` (1 hunks)
* `src/NuGetLicense/Program.cs` (11 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: sensslen
Repo: sensslen/nuget-license PR: 327
File: .github/workflows/release.yml:38-38
Timestamp: 2025-11-07T21:33:27.230Z
Learning: In the release workflow (.github/workflows/release.yml), the test assembly pattern NuGet*.Test.dll intentionally excludes SPDXLicenseMatcher.Test.dll from the release workflow tests.


</details>

</details><details>
<summary>🧬 Code graph analysis (1)</summary>

<details>
<summary>src/NuGetLicense/Program.cs (1)</summary><blockquote>

<details>
<summary>src/NuGetUtility/Wrapper/HttpClientWrapper/NopFileDownloader.cs (1)</summary>

* `NopFileDownloader` (6-11)

</details>

</blockquote></details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>docs/licensefile-mappings-json.md</summary>

41-41: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)</summary>

* GitHub Check: check_licenses (net9.0, App)
* GitHub Check: check_licenses (net8.0, Tests)
* GitHub Check: check_licenses (net9.0, ProjectWithReferenceContainingWindowsSpecificPath)
* GitHub Check: check_licenses (net9.0, Tests)
* GitHub Check: check_licenses (net8.0, ProjectWithReferenceContainingLicenseExpression)
* GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingWindowsSpecificPath)
* GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingFileLicense)
* GitHub Check: check_licenses_net472 (App)
* GitHub Check: check_code_format
* GitHub Check: check_licenses_net472 (ProjectWithReferenceContainingLicenseExpression)
* GitHub Check: check_licenses_net472 (Tests)
* GitHub Check: check_version_command_net472
* GitHub Check: test_windows (net472)
* GitHub Check: test_windows (net9.0)
* GitHub Check: test_windows (net10.0)
* GitHub Check: test_windows (net8.0)
* GitHub Check: test_file_license_matching (ubuntu-latest)
* GitHub Check: test_url_to_license_mapping
* GitHub Check: test_file_license_matching (macos-latest)
* GitHub Check: test_file_license_matching (windows-latest)

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>README.md (2)</summary><blockquote>

`64-64`: **LGTM! Clear documentation of the new option.**

The option is well-documented with clear guidance on path resolution and a reference to detailed documentation.

---

`114-121`: **LGTM! Well-structured example.**

The example clearly demonstrates the new feature with a helpful note about path resolution.

</blockquote></details>
<details>
<summary>src/NuGetLicense/Program.cs (6)</summary><blockquote>

`119-122`: **LGTM! Option definition follows established patterns.**

The new option is properly defined and documented, consistent with other command-line options.

---

`132-149`: **LGTM! Proper introduction of IFileSystem abstraction.**

The integration of `System.IO.Abstractions.FileSystem` improves testability and follows dependency injection principles. All dependent methods are consistently updated.

---

`178-185`: **LGTM! Proper use of IFileSystem abstraction.**

The method correctly uses the file system abstraction for file operations.

---

`223-236`: **LGTM! Directory operations properly abstracted.**

The method correctly uses the file system abstraction for directory checks and creation.

---

`246-318`: **LGTM! Consistent application of IFileSystem abstraction.**

All configuration reading methods are properly updated to use the file system abstraction, maintaining consistency and improving testability.

---

`343-362`: **Address concerns from previous review.**

A previous review identified several issues in this method that remain unaddressed:

1. **Line 351**: `Path.GetDirectoryName` can return null, but the code assumes a non-null result using the null-forgiving operator
2. **Lines 352, 353**: Null-forgiving operators on JSON deserialization without validation
3. **No error handling**: Missing validation for file existence, accessibility, and malformed JSON
4. **No test coverage**: The feature lacks unit or integration tests

The previous review recommended:
- Validate that `LicenseFileMappings` exists and is accessible before reading
- Check that `GetDirectoryName` result is not null and throw a clear exception if it is
- Perform null-checks on `JsonSerializer.Deserialize` with meaningful error messages
- Add tests covering relative/absolute path resolution, cross-platform paths, and error cases

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread src/NuGetLicense/Program.cs Outdated
@sensslen sensslen force-pushed the simon/fix-invalid-license-paths branch from c60ab83 to aeb326c Compare December 2, 2025 23:23
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Dec 2, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
31 Security Hotspots

See analysis details on SonarQube Cloud

@sensslen
Copy link
Copy Markdown
Owner Author

sensslen commented Dec 2, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 2, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

file licenses with backslashes throw exception on linux

2 participants