Skip to content

Commit c2f0970

Browse files
committed
feat(ctrlx-reporter): Allow license filtering based on classifications
Signed-off-by: Nicolas Nobelis <[email protected]>
1 parent 5625dfb commit c2f0970

File tree

2 files changed

+72
-24
lines changed

2 files changed

+72
-24
lines changed

plugins/reporters/ctrlx/src/funTest/kotlin/CtrlXAutomationReporterFunTest.kt

+33
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,14 @@ import org.ossreviewtoolkit.model.RootDependencyIndex
4646
import org.ossreviewtoolkit.model.Scope
4747
import org.ossreviewtoolkit.model.VcsInfo
4848
import org.ossreviewtoolkit.model.VcsType
49+
import org.ossreviewtoolkit.model.licenses.LicenseCategorization
50+
import org.ossreviewtoolkit.model.licenses.LicenseCategory
51+
import org.ossreviewtoolkit.model.licenses.LicenseClassifications
4952
import org.ossreviewtoolkit.plugins.reporters.ctrlx.CtrlXAutomationReporter.Companion.REPORT_FILENAME
5053
import org.ossreviewtoolkit.reporter.ORT_RESULT
5154
import org.ossreviewtoolkit.reporter.ReporterInput
5255
import org.ossreviewtoolkit.utils.ort.createOrtTempDir
56+
import org.ossreviewtoolkit.utils.spdx.SpdxSingleLicenseExpression
5357
import org.ossreviewtoolkit.utils.spdx.toSpdx
5458
import org.ossreviewtoolkit.utils.test.getAssetFile
5559

@@ -87,6 +91,35 @@ class CtrlXAutomationReporterFunTest : StringSpec({
8791
}
8892
}
8993
}
94+
95+
"The reporter should only include licenses with the given category" {
96+
val category = "include-in-disclosure-document"
97+
val categorizations = listOf(
98+
LicenseCategorization(
99+
SpdxSingleLicenseExpression.parse("MIT"),
100+
setOf(category)
101+
)
102+
)
103+
val categories = listOf(LicenseCategory(category))
104+
val input = createReporterInput().copy(
105+
licenseClassifications = LicenseClassifications(
106+
categories = categories,
107+
categorizations = categorizations
108+
),
109+
licenseCategoriesToInclude = listOf(category)
110+
)
111+
val reporter = CtrlXAutomationReporter()
112+
val outputDir = createOrtTempDir("ctrlx-automation-reporter-test")
113+
114+
val reporterResult = reporter.generateReport(input, outputDir)
115+
116+
validateReport(reporterResult) {
117+
components.shouldNotBeNull {
118+
this shouldHaveSize 1
119+
first().name shouldBe "package2"
120+
}
121+
}
122+
}
90123
})
91124

92125
private fun validateReport(reporterResult: List<Result<File>>, validate: FossInfo.() -> Unit) {

plugins/reporters/ctrlx/src/main/kotlin/CtrlXAutomationReporter.kt

+39-24
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ class CtrlXAutomationReporter(override val descriptor: PluginDescriptor = CtrlXA
5454

5555
override fun generateReport(input: ReporterInput, outputDir: File): List<Result<File>> {
5656
val packages = input.ortResult.getPackages(omitExcluded = true)
57-
val components = packages.mapTo(mutableListOf()) { (pkg, _) ->
57+
58+
val components = packages.mapNotNullTo(mutableListOf()) { (pkg, _) ->
5859
val qualifiedName = when (pkg.id.type) {
5960
// At least for NPM packages, CtrlX requires the component name to be prefixed with the scope name,
6061
// separated with a slash. Other package managers might require similar handling, but there seems to be
@@ -67,31 +68,45 @@ class CtrlXAutomationReporter(override val descriptor: PluginDescriptor = CtrlXA
6768
}
6869

6970
val resolvedLicenseInfo = input.licenseInfoResolver.resolveLicenseInfo(pkg.id).filterExcluded()
70-
val copyrights = resolvedLicenseInfo.getCopyrights().joinToString("\n").takeUnless { it.isEmpty() }
71-
val effectiveLicense = resolvedLicenseInfo.effectiveLicense(
72-
LicenseView.CONCLUDED_OR_DECLARED_AND_DETECTED,
73-
input.ortResult.getPackageLicenseChoices(pkg.id),
74-
input.ortResult.getRepositoryLicenseChoices()
71+
val filteredResolvedLicenseInfo = resolvedLicenseInfo.filterNoCategorizedLicenses(
72+
input.licenseClassifications,
73+
input.licenseCategoriesToInclude
7574
)
76-
val licenses = effectiveLicense?.decompose()?.map {
77-
val name = it.toString()
78-
val spdxId = SpdxLicense.forId(name)?.id
79-
val text = input.licenseTextProvider.getLicenseText(name)
80-
License(name = name, spdx = spdxId, text = text.orEmpty())
81-
}
8275

83-
// The specification requires at least one license.
84-
val componentLicenses = licenses.orEmpty().ifEmpty { listOf(LICENSE_NOASSERTION) }
85-
86-
Component(
87-
name = qualifiedName,
88-
version = pkg.id.version,
89-
homepage = pkg.homepageUrl.takeUnless { it.isEmpty() },
90-
copyright = copyrights?.let { CopyrightInformation(it) },
91-
licenses = componentLicenses,
92-
usage = if (pkg.isModified) Usage.Modified else Usage.AsIs
93-
// TODO: Map the PackageLinkage to an IntegrationMechanism.
94-
)
76+
// If the license was removed by the classification filter, we don't output it in the report (otherwise it
77+
// would be included with NOASSERTION (see underneath).
78+
if (filteredResolvedLicenseInfo.licenses.isEmpty() && resolvedLicenseInfo.licenses.isNotEmpty()) {
79+
null
80+
} else {
81+
val copyrights = filteredResolvedLicenseInfo.getCopyrights().joinToString("\n").takeUnless {
82+
it.isEmpty()
83+
}
84+
85+
val effectiveLicense = filteredResolvedLicenseInfo.effectiveLicense(
86+
LicenseView.CONCLUDED_OR_DECLARED_AND_DETECTED,
87+
input.ortResult.getPackageLicenseChoices(pkg.id),
88+
input.ortResult.getRepositoryLicenseChoices()
89+
)
90+
val licenses = effectiveLicense?.decompose()?.map {
91+
val name = it.toString()
92+
val spdxId = SpdxLicense.forId(name)?.id
93+
val text = input.licenseTextProvider.getLicenseText(name)
94+
License(name = name, spdx = spdxId, text = text.orEmpty())
95+
}
96+
97+
// The specification requires at least one license.
98+
val componentLicenses = licenses.orEmpty().ifEmpty { listOf(LICENSE_NOASSERTION) }
99+
100+
Component(
101+
name = qualifiedName,
102+
version = pkg.id.version,
103+
homepage = pkg.homepageUrl.takeUnless { it.isEmpty() },
104+
copyright = copyrights?.let { CopyrightInformation(it) },
105+
licenses = componentLicenses,
106+
usage = if (pkg.isModified) Usage.Modified else Usage.AsIs
107+
// TODO: Map the PackageLinkage to an IntegrationMechanism.
108+
)
109+
}
95110
}
96111

97112
val reportFileResult = runCatching {

0 commit comments

Comments
 (0)