Skip to content

Conversation

Prakash-Mishra-9ghz
Copy link

This pull request introduces a new external scanner plugin for integrating FOSSology's Nomos license scanner into the OSS Review Toolkit (ORT).

🔹 What's implemented

  • Nomos is implemented as an ExternalScanner.
  • Supports command-line execution of Nomos and parses its output.
  • Converts results into ORT's ScanResult model.

🔹 File breakdown

  • Nomossa.kt: Main scanner class implementing the Scanner interface.
  • NomossaConfig.kt: Loads configuration for invoking Nomos CLI.
  • NomossaResultParser.kt: Parses Nomos output to extract license info.
  • NomossaResultExtension.kt: Converts parsed data into ScanResult.
  • build.gradle.kts: Sets up plugin dependencies and build configuration.

This plugin allows users to plug FOSSology’s Nomos agent into ORT workflows to enhance license detection coverage and flexibility.

@Prakash-Mishra-9ghz Prakash-Mishra-9ghz requested a review from a team as a code owner July 20, 2025 17:14
@sschuberth
Copy link
Member

Commit message title: According to conventional commits, the title should start with feat(scanner):.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

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

detekt found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Copy link

codecov bot commented Jul 20, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.34%. Comparing base (78fb251) to head (a8ccf27).
⚠️ Report is 29 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##               main   #10631   +/-   ##
=========================================
  Coverage     57.34%   57.34%           
+ Complexity     1670     1668    -2     
=========================================
  Files           344      344           
  Lines         12747    12747           
  Branches       1209     1209           
=========================================
  Hits           7310     7310           
  Misses         4972     4972           
  Partials        465      465           
Flag Coverage Δ
funTest-non-docker 32.86% <ø> (-0.15%) ⬇️
test-ubuntu-24.04 41.90% <ø> (ø)
test-windows-2025 41.88% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sschuberth
Copy link
Member

detekt found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@Prakash-Mishra-9ghz, please have a look at these issues and make all automated checks pass before we start with the actual code review.

@Prakash-Mishra-9ghz
Copy link
Author

detekt found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@Prakash-Mishra-9ghz, please have a look at these issues and make all automated checks pass before we start with the actual code review.

Resolved all Detekt issues

@fviernau fviernau dismissed their stale review July 21, 2025 11:15

Addressed.

@sschuberth sschuberth requested a review from a team July 21, 2025 11:18
@sschuberth
Copy link
Member

@Prakash-Mishra-9ghz we have a policy to fix any issues identified during review / by automated checks in the actual commit that introduces the issue as long as the PR is still under review.

As your PR originally only had a single commit, this means to just amend that original commit with any updates. So please squash the two commits that you have now into just one again.

@Prakash-Mishra-9ghz
Copy link
Author

Prakash-Mishra-9ghz commented Jul 22, 2025

@sschuberth I added the following copyright header: Copyright (C) 2025 Prakash Mishra. The CI failed due to this not being in the NOTICE file. Should I add myself there, or just remove the header?

@sschuberth
Copy link
Member

The CI failed due to this not being in the NOTICE file. Should I add myself there, or just remove the header?

You should add yourself to the NOTICE file (grouped by year, then sorted alphabetically) and keep the generic Copyright header in the source file.

@Prakash-Mishra-9ghz
Copy link
Author

hi @sschuberth could you help me to figure out the funtest-docker, test(unbuntu and windows) fail.
I think it might be due to missing tests or some configuration issue, but I’m not exactly sure.
Could you help me understand what’s going wrong?

@sschuberth
Copy link
Member

hi @sschuberth could you help me to figure out the funtest-docker, test(unbuntu and windows) fail.

Those are temporary failures due to foojayio/discoapi#124, which had been fixed, but now is back again... we simply need to wait for that external service to recover. Just rebase your PR onto latest main in a few hours to check.

@sschuberth
Copy link
Member

Just rebase your PR onto latest main in a few hours to check.

Will you do that rebase, @Prakash-Mishra-9ghz?

@Prakash-Mishra-9ghz
Copy link
Author

@sschuberth Currently working on the Docker implementation for the plugin.

@Prakash-Mishra-9ghz Prakash-Mishra-9ghz force-pushed the nomos branch 3 times, most recently from 41e4847 to 2a793fa Compare July 30, 2025 18:13
@Prakash-Mishra-9ghz
Copy link
Author

@sschuberth CI is failing due to an sbt-assembly version mismatch—could you confirm if it is related to PR?

@sschuberth
Copy link
Member

@sschuberth CI is failing due to an sbt-assembly version mismatch—could you confirm if it is related to PR?

No, it should not be related, I've retriggered the test.

*/

plugins {
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.22"
Copy link
Member

Choose a reason for hiding this comment

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

Please change the syntax to make use of Gradle's version catalog alias like here:

alias(libs.plugins.kotlinSerialization)

Copy link
Member

Choose a reason for hiding this comment

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

While at it, also add our conventional the grouping comments here, so it looks exactly like:

plugins {
    // Apply precompiled plugins.
    id("ort-plugin-conventions")

    // Apply third-party plugins.
    alias(libs.plugins.kotlinSerialization)
}

api(projects.scanner)

implementation(projects.utils.commonUtils)

Copy link
Member

Choose a reason for hiding this comment

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

No separation for grouping needed here, so just remove this blank line.


ksp(projects.scanner)

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
Copy link
Member

Choose a reason for hiding this comment

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

Again, align with code that makes use of the version catalog, so that we do not hard-code versions here:

implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)

Comment on lines 39 to 41
override fun command(workingDir: File?): String {
return listOfNotNull(workingDir, "nomossa").joinToString(File.separator)
}
Copy link
Member

Choose a reason for hiding this comment

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

This is basically a one-liner that should be a function expression with =, similar to

override fun command(workingDir: File?) =
listOfNotNull(workingDir, if (Os.isWindows) "askalono.exe" else "askalono").joinToString(File.separator)

}

override fun transformVersion(output: String): String {
// Example output: nomossasa build version: 4.5.1.1 r(ff4fa7)
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Maybe put the "nomossasa build version: 4.5.1.1 r(ff4fa7)" part to its own line, similar to

// The version string can be something like:
// askalono 0.2.0-beta.1

to avoid potential confusion where the example begins / ends due to the multiple colons in the line-

internal fun NomossaResult.toScanSummary(startTime: Instant, endTime: Instant): ScanSummary {
val licenseFindings = results.flatMap { fileResult ->
fileResult.licenses.map { rawLicense ->

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Omit this superfluous blank line.

Comment on lines 47 to 53
}.toSortedSet(
compareBy(
{ it.license.toString() },
{ it.location.path },
{ it.location.startLine }
)
)
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't do any sorting here for the in-memory representation. Sorting is only meaningful for deserialization, and there we do it centrally. So just drop this code.

}

LicenseFinding(
license = safeLicense, // use raw string
Copy link
Member

Choose a reason for hiding this comment

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

I think the comment is misleading, as the "raw string" has been converted to a "safe string". I'd just drop the comment.

Comment on lines 42 to 43
startLine = 1,
endLine = 1
Copy link
Member

Choose a reason for hiding this comment

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

So, does Nomossa not provide the line range where a license matches was found at all? I fear that makes it quite unattractive as a scanner. In any case, if the line is unknown, the TextLocation.UNKNOWN_LINE constant should be used.

Copy link
Author

Choose a reason for hiding this comment

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

Nomossa does provide line numbers. Some updates are needed in the implementation — will take care of that soon.

Comment on lines 32 to 42
val safeLicense = if (rawLicense.matches(Regex("^[A-Za-z0-9.\\-+]+$"))) {
rawLicense
} else {
"LicenseRef-Nomossa-${rawLicense.replace(Regex("[^A-Za-z0-9.+-]"), "-")}" // License not found
}
Copy link
Member

Choose a reason for hiding this comment

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

I guess instead of Regex-based checking we could just try to parse the license, similar to here:

val licenseExpression = runCatching { SpdxExpression.parse(license.name) }.getOrNull()
val validatedLicense = when {
licenseExpression == null -> SpdxConstants.NOASSERTION
licenseExpression.isValid() -> license.name
else -> "${SpdxConstants.LICENSE_REF_PREFIX}scanoss-${license.name}"
}

Signed-off-by: Prakash Mishra <prakashmishra9921@gmail.com>

chore(scanner): Update display name, fix detekt issue, and add copyright

Signed-off-by: Prakash Mishra <prakashmishra9921@gmail.com>
@sschuberth
Copy link
Member

Please see my comment over here: This implementation lacks functional tests that make use of the scanner to prove that the implementation is working.

@sschuberth sschuberth requested a review from a team August 28, 2025 16:15
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.

3 participants