Skip to content

Commit

Permalink
feat: Add the package configuration provider for DOS
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Schuberth <[email protected]>
  • Loading branch information
Etsija authored and sschuberth committed Jun 24, 2024
1 parent 1d8a4ab commit 0a708d2
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
22 changes: 22 additions & 0 deletions plugins/package-configuration-providers/dos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Double Open Server (DOS) Package Configuration Provider

The DOS package configuration provider provides license finding curations and path excludes for files of packages identified by purls.
As the provider works on a per-file basis, it should be used with either ORT's default ScanCode scanner wrapper with the `preferFileLicense` option enabled, or with the DOS scanner wrapper.

## Usage

The DOS package configuration provider requires [DOS] to be running on a host and corresponding `DosPackageConfigurationProviderConfig` to be present in ORT's `config.yml`, e.g.

```yaml
ort:
packageConfigurationProviders:
- type: DOS
options:
url: '${DOS_SERVER_URL}'
secrets:
token: '${DOS_SERVER_TOKEN}'
```
where `DOS_SERVER_URL` and `DOS_SERVER_TOKEN` are environment variables that are set to the respective values.

[DOS]: https://github.com/doubleopen-project/dos
30 changes: 30 additions & 0 deletions plugins/package-configuration-providers/dos/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2024 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

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

dependencies {
api(projects.plugins.packageConfigurationProviders.packageConfigurationProviderApi)

implementation(projects.clients.dosClient)
implementation(libs.kotlinx.coroutines)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (C) 2024 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

package org.ossreviewtoolkit.plugins.packageconfigurationproviders.dos

import java.time.Duration

import kotlinx.coroutines.runBlocking

import org.apache.logging.log4j.kotlin.logger

import org.ossreviewtoolkit.clients.dos.DosClient
import org.ossreviewtoolkit.clients.dos.DosService
import org.ossreviewtoolkit.clients.dos.PackageConfigurationResponseBody
import org.ossreviewtoolkit.model.ArtifactProvenance
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Provenance
import org.ossreviewtoolkit.model.RepositoryProvenance
import org.ossreviewtoolkit.model.config.LicenseFindingCuration
import org.ossreviewtoolkit.model.config.LicenseFindingCurationReason
import org.ossreviewtoolkit.model.config.PackageConfiguration
import org.ossreviewtoolkit.model.config.PathExclude
import org.ossreviewtoolkit.model.config.PathExcludeReason

Check warning on line 39 in plugins/package-configuration-providers/dos/src/main/kotlin/DosPackageConfigurationProvider.kt

View workflow job for this annotation

GitHub Actions / qodana-scan

Unused import directive

Unused import directive

Check warning

Code scanning / detekt

Unused Imports are dead code and should be removed. Warning

The import 'org.ossreviewtoolkit.model.config.PathExcludeReason' is unused.
import org.ossreviewtoolkit.model.config.VcsMatcher
import org.ossreviewtoolkit.model.utils.PackageConfigurationProvider
import org.ossreviewtoolkit.model.utils.associateLicensesWithExceptions
import org.ossreviewtoolkit.model.utils.toPurl
import org.ossreviewtoolkit.model.utils.toPurlExtras
import org.ossreviewtoolkit.plugins.packageconfigurationproviders.api.PackageConfigurationProviderFactory
import org.ossreviewtoolkit.utils.common.Options
import org.ossreviewtoolkit.utils.spdx.SpdxExpression

data class DosPackageConfigurationProviderConfig(
/** The URL where the DOS backend is running. */
val url: String,

/** The secret token to use with the DOS backend. */
val token: String,

/** The timeout for communicating with the DOS backend, in seconds. */
val timeout: Long?
)

class DosPackageConfigurationProviderFactory :
PackageConfigurationProviderFactory<DosPackageConfigurationProviderConfig> {
override val type = "DOS"

override fun create(config: DosPackageConfigurationProviderConfig) = DosPackageConfigurationProvider(config)

override fun parseConfig(options: Options, secrets: Options) =
DosPackageConfigurationProviderConfig(
url = options.getValue("url"),
timeout = options["timeout"]?.toLongOrNull(),
token = secrets.getValue("token")
)
}

/**
* A [PackageConfigurationProvider] that loads [PackageConfiguration]s from a Double Open Server instance.
*/
class DosPackageConfigurationProvider(config: DosPackageConfigurationProviderConfig) : PackageConfigurationProvider {
private val service = DosService.create(
config.url,
config.token,
config.timeout?.let { Duration.ofSeconds(it) }
)

private val client = DosClient(service)

override fun getPackageConfigurations(packageId: Identifier, provenance: Provenance): List<PackageConfiguration> {
val purl = packageId.toPurl(provenance.toPurlExtras())

val packageResults = runBlocking { client.getPackageConfiguration(purl) }
?.takeUnless { it.licenseConclusions.isEmpty() && it.pathExclusions.isEmpty() } ?: return emptyList()

val packageConfiguration = packageResults.toPackageConfiguration(packageId, provenance)

logger.info { "Found license conclusions for $purl: ${packageResults.licenseConclusions}" }
logger.info { "Found path exclusions for $purl: ${packageResults.pathExclusions}" }

return listOf(packageConfiguration)
}
}

private fun PackageConfigurationResponseBody.toPackageConfiguration(
id: Identifier,
provenance: Provenance
): PackageConfiguration {
val sourceArtifactUrl = if (provenance is ArtifactProvenance) {
provenance.sourceArtifact.url
} else {
null
}

val vcs = if (provenance is RepositoryProvenance) {
VcsMatcher(
type = provenance.vcsInfo.type,
url = provenance.vcsInfo.url,
revision = provenance.resolvedRevision
)
} else {
null
}

val licenseFindingCurations = licenseConclusions.map { licenseConclusion ->
// The "detectedLicenseExpressionSPDX" comes straight from ScanCode's raw results. As the DOS scanner is
// currently written in TypeScript and thus cannot make use of ORT's post-processing of ScanCode results,
// the call of "associateLicensesWithExceptions()" is postponed to here.
val detected = licenseConclusion.detectedLicenseExpressionSPDX?.takeUnless { it.isEmpty() }
?.let { SpdxExpression.parse(it) }
?.let { associateLicensesWithExceptions(it) }

check(licenseConclusion.concludedLicenseExpressionSPDX.isNotEmpty())
val concluded = SpdxExpression.parse(licenseConclusion.concludedLicenseExpressionSPDX)

LicenseFindingCuration(
path = licenseConclusion.path,
detectedLicense = detected,
concludedLicense = concluded,
reason = LicenseFindingCurationReason.INCORRECT,
comment = licenseConclusion.comment.orEmpty()
)
}

val pathExcludes = pathExclusions.map {
PathExclude(
pattern = it.pattern,
reason = it.reason,
comment = it.comment.orEmpty()
)
}

return PackageConfiguration(
id = id,
sourceArtifactUrl = sourceArtifactUrl,
vcs = vcs,
pathExcludes = pathExcludes,
licenseFindingCurations = licenseFindingCurations
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.ossreviewtoolkit.plugins.packageconfigurationproviders.dos.DosPackageConfigurationProviderFactory

0 comments on commit 0a708d2

Please sign in to comment.