Skip to content

Commit 4d39003

Browse files
committed
feat(Provenance): Add DirectoryProvenance as a LocalProvenance
In contrast to the previously added `RemoteProvenance` stands the `LocalProvenance`, which has no remote source, but instead references a local input of some kind. The `DirectoryProvenance` references a local project directory, which is lacking supported (remote) version control. It is defined by its canonical path only. Since ORT needs further refactoring until `DirectoryProvenance` can be fully utilized, the new class can not be used right now. However the presence of a new `KnownProvenance` class results in `when` conditional cases not being exhaustive anymore. To circumvent this issue, the following changes were made: 1. Wherever possible `RemoteProvenance` is used as parameter type instead of `KnownProvenance`. 2. When necessary `KnownProvenance`s are cast to `RemoteProvenance`. 3. If `Provenance` is expected, `DirectoryProvenance` is handled like `UnknownProvenance`. The exception being `hash` and `storageKey`, which both required a default value, which was set to the `canonicalPath` and its hash. However, since the rest of the code does not handle `DirectoryProvenance`, this should remain unused for now. See [1] for more context on the new provenance hierarchy. [1]: oss-review-toolkit#8803 (comment) Signed-off-by: Jens Keim <[email protected]>
1 parent 19da2db commit 4d39003

15 files changed

+62
-23
lines changed

model/src/main/kotlin/Provenance.kt

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize
2626
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
2727
import com.fasterxml.jackson.module.kotlin.treeToValue
2828

29+
import java.io.File
30+
2931
/**
3032
* Provenance information about the origin of source code.
3133
*/
@@ -45,6 +47,8 @@ sealed interface KnownProvenance : Provenance
4547

4648
sealed interface RemoteProvenance : KnownProvenance
4749

50+
sealed interface LocalProvenance : KnownProvenance
51+
4852
/**
4953
* Provenance information for a source artifact.
5054
*/
@@ -83,6 +87,23 @@ data class RepositoryProvenance(
8387
override fun matches(pkg: Package): Boolean = vcsInfo == pkg.vcsProcessed
8488
}
8589

90+
/**
91+
* Provenance information for a local directory path.
92+
*/
93+
data class DirectoryProvenance(
94+
val canonicalPath: File
95+
) : LocalProvenance {
96+
init {
97+
require(canonicalPath.isDirectory) { "The directory path must exist." }
98+
}
99+
100+
/**
101+
* Return true if this provenance's directoryPath matches the package URL of the [package][pkg],
102+
* as it contains the local file path for non-remote Provenances.
103+
*/
104+
override fun matches(pkg: Package): Boolean = false
105+
}
106+
86107
/**
87108
* A custom deserializer for polymorphic deserialization of [Provenance] without requiring type information.
88109
*/

model/src/main/kotlin/ProvenanceResolutionResult.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ data class ProvenanceResolutionResult(
3737
* The resolved provenance of the package. Can only be null if a [packageProvenanceResolutionIssue] occurred.
3838
*/
3939
@JsonInclude(JsonInclude.Include.NON_NULL)
40-
val packageProvenance: KnownProvenance? = null,
40+
val packageProvenance: RemoteProvenance? = null,
4141

4242
/**
4343
* The (recursive) sub-repositories of [packageProvenance]. The map can only be empty if a

model/src/main/kotlin/config/PackageConfiguration.kt

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package org.ossreviewtoolkit.model.config
2222
import com.fasterxml.jackson.annotation.JsonInclude
2323

2424
import org.ossreviewtoolkit.model.ArtifactProvenance
25+
import org.ossreviewtoolkit.model.DirectoryProvenance
2526
import org.ossreviewtoolkit.model.Identifier
2627
import org.ossreviewtoolkit.model.Provenance
2728
import org.ossreviewtoolkit.model.RepositoryProvenance
@@ -84,6 +85,7 @@ data class PackageConfiguration(
8485
is UnknownProvenance -> false
8586
is ArtifactProvenance -> sourceArtifactUrl != null && sourceArtifactUrl == provenance.sourceArtifact.url
8687
is RepositoryProvenance -> vcs != null && vcs.matches(provenance)
88+
is DirectoryProvenance -> false
8789
}
8890
}
8991
}

model/src/main/kotlin/utils/FileProvenanceFileStorage.kt

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import java.io.InputStream
2424
import org.apache.logging.log4j.kotlin.logger
2525

2626
import org.ossreviewtoolkit.model.ArtifactProvenance
27+
import org.ossreviewtoolkit.model.DirectoryProvenance
2728
import org.ossreviewtoolkit.model.HashAlgorithm
2829
import org.ossreviewtoolkit.model.KnownProvenance
2930
import org.ossreviewtoolkit.model.RepositoryProvenance
@@ -81,6 +82,7 @@ private fun KnownProvenance.hash(): String {
8182
val key = when (this) {
8283
is ArtifactProvenance -> "${sourceArtifact.url}${sourceArtifact.hash.value}"
8384
is RepositoryProvenance -> "${vcsInfo.type}${vcsInfo.url}$resolvedRevision"
85+
is DirectoryProvenance -> "${canonicalPath.toPath()}${canonicalPath.hashCode()}"
8486
}
8587

8688
return HashAlgorithm.SHA1.calculate(key.toByteArray())

model/src/main/kotlin/utils/PostgresProvenanceFileStorage.kt

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import org.jetbrains.exposed.sql.insert
3636
import org.jetbrains.exposed.sql.selectAll
3737

3838
import org.ossreviewtoolkit.model.ArtifactProvenance
39+
import org.ossreviewtoolkit.model.DirectoryProvenance
3940
import org.ossreviewtoolkit.model.KnownProvenance
4041
import org.ossreviewtoolkit.model.RepositoryProvenance
4142
import org.ossreviewtoolkit.model.utils.DatabaseUtils.checkDatabaseEncoding
@@ -121,4 +122,5 @@ private fun KnownProvenance.storageKey(): String =
121122
is ArtifactProvenance -> "source-artifact|${sourceArtifact.url}|${sourceArtifact.hash}"
122123
// The trailing "|" is kept for backward compatibility because there used to be an additional parameter.
123124
is RepositoryProvenance -> "vcs|${vcsInfo.type}|${vcsInfo.url}|$resolvedRevision|"
125+
is DirectoryProvenance -> "directory|${canonicalPath.toPath()}|${canonicalPath.hashCode()}"
124126
}

model/src/main/kotlin/utils/PurlExtensions.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.ossreviewtoolkit.model.Identifier
2727
import org.ossreviewtoolkit.model.Package
2828
import org.ossreviewtoolkit.model.Provenance
2929
import org.ossreviewtoolkit.model.RemoteArtifact
30+
import org.ossreviewtoolkit.model.RemoteProvenance
3031
import org.ossreviewtoolkit.model.RepositoryProvenance
3132
import org.ossreviewtoolkit.model.UnknownProvenance
3233
import org.ossreviewtoolkit.model.VcsInfo
@@ -101,7 +102,7 @@ fun Provenance.toPurlExtras(): PurlExtras =
101102
)
102103
}
103104

104-
is UnknownProvenance -> PurlExtras()
105+
!is RemoteProvenance -> PurlExtras()
105106
}
106107

107108
/**

scanner/src/main/kotlin/ScanController.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.ossreviewtoolkit.model.Issue
2424
import org.ossreviewtoolkit.model.KnownProvenance
2525
import org.ossreviewtoolkit.model.Package
2626
import org.ossreviewtoolkit.model.Provenance
27+
import org.ossreviewtoolkit.model.RemoteProvenance
2728
import org.ossreviewtoolkit.model.RepositoryProvenance
2829
import org.ossreviewtoolkit.model.ScanResult
2930
import org.ossreviewtoolkit.model.ScanSummary
@@ -79,7 +80,7 @@ internal class ScanController(
7980
* A map of package [Identifier]s to their resolved [KnownProvenance]s. These provenances are used to filter the
8081
* scan results for a package based on the VCS path.
8182
*/
82-
private val packageProvenances = mutableMapOf<Identifier, KnownProvenance>()
83+
private val packageProvenances = mutableMapOf<Identifier, RemoteProvenance>()
8384

8485
/**
8586
* A map of package [Identifier]s to their resolved [KnownProvenance]s with the VCS path removed. These provenances
@@ -130,7 +131,7 @@ internal class ScanController(
130131
/**
131132
* Set the [provenance] for the package denoted by [id], overwriting any existing values.
132133
*/
133-
fun putPackageProvenance(id: Identifier, provenance: KnownProvenance) {
134+
fun putPackageProvenance(id: Identifier, provenance: RemoteProvenance) {
134135
packageProvenances[id] = provenance
135136
packageProvenancesWithoutVcsPath[id] = when (provenance) {
136137
is RepositoryProvenance -> provenance.copy(vcsInfo = provenance.vcsInfo.copy(path = ""))
@@ -262,7 +263,7 @@ internal class ScanController(
262263
fun getPackagesForProvenanceWithoutVcsPath(provenance: KnownProvenance): Set<Identifier> =
263264
packageProvenancesWithoutVcsPath.filter { (_, packageProvenance) -> packageProvenance == provenance }.keys
264265

265-
fun getPackageProvenance(id: Identifier): KnownProvenance? = packageProvenances[id]
266+
fun getPackageProvenance(id: Identifier): RemoteProvenance? = packageProvenances[id]
266267

267268
/**
268269
* Return the package provenanceResolutionIssue associated with the given [id].

scanner/src/main/kotlin/Scanner.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import org.ossreviewtoolkit.model.OrtResult
4141
import org.ossreviewtoolkit.model.Package
4242
import org.ossreviewtoolkit.model.PackageType
4343
import org.ossreviewtoolkit.model.ProvenanceResolutionResult
44+
import org.ossreviewtoolkit.model.RemoteProvenance
4445
import org.ossreviewtoolkit.model.ScanResult
4546
import org.ossreviewtoolkit.model.ScanSummary
4647
import org.ossreviewtoolkit.model.ScannerRun
@@ -258,7 +259,7 @@ class Scanner(
258259
}.awaitAll()
259260
}.forEach { (pkg, result) ->
260261
result.onSuccess { provenance ->
261-
controller.putPackageProvenance(pkg.id, provenance)
262+
controller.putPackageProvenance(pkg.id, provenance as RemoteProvenance)
262263
}.onFailure {
263264
controller.putPackageProvenanceResolutionIssue(
264265
pkg.id,
@@ -279,7 +280,7 @@ class Scanner(
279280
controller.getPackageProvenancesWithoutVcsPath().map { provenance ->
280281
async {
281282
provenance to runCatching {
282-
nestedProvenanceResolver.resolveNestedProvenance(provenance)
283+
nestedProvenanceResolver.resolveNestedProvenance(provenance as RemoteProvenance)
283284
}
284285
}
285286
}.awaitAll()
@@ -574,7 +575,7 @@ class Scanner(
574575
context: ScanContext
575576
): Map<PathScannerWrapper, ScanResult> {
576577
val downloadDir = try {
577-
provenanceDownloader.download(provenance)
578+
provenanceDownloader.download(provenance as RemoteProvenance)
578579
} catch (e: DownloadException) {
579580
val issue = createAndLogIssue(
580581
"Downloader", "Could not download provenance $provenance: ${e.collectMessages()}"

scanner/src/main/kotlin/provenance/NestedProvenanceResolver.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import org.apache.logging.log4j.kotlin.logger
2323

2424
import org.ossreviewtoolkit.downloader.WorkingTreeCache
2525
import org.ossreviewtoolkit.model.ArtifactProvenance
26-
import org.ossreviewtoolkit.model.KnownProvenance
2726
import org.ossreviewtoolkit.model.Provenance
27+
import org.ossreviewtoolkit.model.RemoteProvenance
2828
import org.ossreviewtoolkit.model.RepositoryProvenance
2929

3030
/**
@@ -36,7 +36,7 @@ interface NestedProvenanceResolver {
3636
* [NestedProvenance] always contains only the provided [ArtifactProvenance]. For a [RepositoryProvenance] the
3737
* resolver looks for nested repositories, for example Git submodules or Mercurial subrepositories.
3838
*/
39-
suspend fun resolveNestedProvenance(provenance: KnownProvenance): NestedProvenance
39+
suspend fun resolveNestedProvenance(provenance: RemoteProvenance): NestedProvenance
4040
}
4141

4242
/**
@@ -46,7 +46,7 @@ class DefaultNestedProvenanceResolver(
4646
private val storage: NestedProvenanceStorage,
4747
private val workingTreeCache: WorkingTreeCache
4848
) : NestedProvenanceResolver {
49-
override suspend fun resolveNestedProvenance(provenance: KnownProvenance): NestedProvenance {
49+
override suspend fun resolveNestedProvenance(provenance: RemoteProvenance): NestedProvenance {
5050
return when (provenance) {
5151
is ArtifactProvenance -> NestedProvenance(root = provenance, subRepositories = emptyMap())
5252
is RepositoryProvenance -> resolveNestedRepository(provenance)

scanner/src/main/kotlin/provenance/ProvenanceDownloader.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import org.ossreviewtoolkit.downloader.DownloadException
2929
import org.ossreviewtoolkit.downloader.Downloader
3030
import org.ossreviewtoolkit.downloader.WorkingTreeCache
3131
import org.ossreviewtoolkit.model.ArtifactProvenance
32-
import org.ossreviewtoolkit.model.KnownProvenance
3332
import org.ossreviewtoolkit.model.Package
33+
import org.ossreviewtoolkit.model.RemoteProvenance
3434
import org.ossreviewtoolkit.model.RepositoryProvenance
3535
import org.ossreviewtoolkit.model.config.DownloaderConfiguration
3636
import org.ossreviewtoolkit.utils.common.safeDeleteRecursively
@@ -48,7 +48,7 @@ fun interface ProvenanceDownloader {
4848
*
4949
* Throws a [DownloadException] if the download fails.
5050
*/
51-
fun download(provenance: KnownProvenance): File
51+
fun download(provenance: RemoteProvenance): File
5252

5353
/**
5454
* Download the source code specified by the provided [nestedProvenance] incl. sub-repositories and return the path
@@ -61,7 +61,7 @@ fun interface ProvenanceDownloader {
6161
// Use the provenanceDownloader to download each provenance from nestedProvenance separately, because they are
6262
// likely already cached if a path scanner wrapper is used.
6363

64-
val root = download(nestedProvenance.root)
64+
val root = download(nestedProvenance.root as RemoteProvenance)
6565

6666
nestedProvenance.subRepositories.forEach { (path, provenance) ->
6767
val tempDir = download(provenance)
@@ -83,7 +83,7 @@ class DefaultProvenanceDownloader(
8383
) : ProvenanceDownloader {
8484
private val downloader = Downloader(config)
8585

86-
override fun download(provenance: KnownProvenance): File {
86+
override fun download(provenance: RemoteProvenance): File {
8787
val downloadDir = createOrtTempDir()
8888

8989
when (provenance) {

scanner/src/main/kotlin/storages/ProvenanceBasedFileStorage.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.apache.logging.log4j.kotlin.logger
2929

3030
import org.ossreviewtoolkit.model.ArtifactProvenance
3131
import org.ossreviewtoolkit.model.KnownProvenance
32+
import org.ossreviewtoolkit.model.RemoteProvenance
3233
import org.ossreviewtoolkit.model.RepositoryProvenance
3334
import org.ossreviewtoolkit.model.ScanResult
3435
import org.ossreviewtoolkit.model.yamlMapper
@@ -45,7 +46,7 @@ class ProvenanceBasedFileStorage(private val backend: FileStorage) : ProvenanceB
4546
override fun read(provenance: KnownProvenance, scannerMatcher: ScannerMatcher?): List<ScanResult> {
4647
requireEmptyVcsPath(provenance)
4748

48-
val path = storagePath(provenance)
49+
val path = storagePath(provenance as RemoteProvenance)
4950

5051
return runCatching {
5152
backend.read(path).use { input ->
@@ -94,7 +95,7 @@ class ProvenanceBasedFileStorage(private val backend: FileStorage) : ProvenanceB
9495

9596
val scanResults = existingScanResults + scanResult
9697

97-
val path = storagePath(provenance)
98+
val path = storagePath(provenance as RemoteProvenance)
9899
val yamlBytes = yamlMapper.writeValueAsBytes(scanResults)
99100
val input = ByteArrayInputStream(yamlBytes)
100101

@@ -117,7 +118,7 @@ class ProvenanceBasedFileStorage(private val backend: FileStorage) : ProvenanceB
117118
}
118119
}
119120

120-
private fun storagePath(provenance: KnownProvenance) =
121+
private fun storagePath(provenance: RemoteProvenance) =
121122
when (provenance) {
122123
is ArtifactProvenance -> "artifact/${provenance.sourceArtifact.url.fileSystemEncode()}/$SCAN_RESULTS_FILE_NAME"
123124
is RepositoryProvenance -> {

scanner/src/main/kotlin/storages/ProvenanceBasedPostgresStorage.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import org.jetbrains.exposed.sql.selectAll
3636

3737
import org.ossreviewtoolkit.model.ArtifactProvenance
3838
import org.ossreviewtoolkit.model.KnownProvenance
39+
import org.ossreviewtoolkit.model.RemoteProvenance
3940
import org.ossreviewtoolkit.model.RepositoryProvenance
4041
import org.ossreviewtoolkit.model.ScanResult
4142
import org.ossreviewtoolkit.model.ScanSummary
@@ -86,6 +87,10 @@ class ProvenanceBasedPostgresStorage(
8687
return database.transaction {
8788
val query = table.selectAll()
8889

90+
if (provenance !is RemoteProvenance) {
91+
throw ScanStorageException("Scan result must have a known provenance, but it is $provenance.")
92+
}
93+
8994
when (provenance) {
9095
is ArtifactProvenance -> {
9196
query.andWhere {
@@ -137,7 +142,7 @@ class ProvenanceBasedPostgresStorage(
137142

138143
requireEmptyVcsPath(provenance)
139144

140-
if (provenance !is KnownProvenance) {
145+
if (provenance !is RemoteProvenance) {
141146
throw ScanStorageException("Scan result must have a known provenance, but it is $provenance.")
142147
}
143148

scanner/src/main/kotlin/utils/FileListResolver.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import java.io.File
2626

2727
import org.ossreviewtoolkit.model.HashAlgorithm
2828
import org.ossreviewtoolkit.model.KnownProvenance
29+
import org.ossreviewtoolkit.model.RemoteProvenance
2930
import org.ossreviewtoolkit.model.toYaml
3031
import org.ossreviewtoolkit.model.utils.ProvenanceFileStorage
3132
import org.ossreviewtoolkit.model.yamlMapper
@@ -58,7 +59,7 @@ class FileListResolver(
5859
fun resolve(provenance: KnownProvenance): FileList {
5960
storage.getFileList(provenance)?.let { return it }
6061

61-
val dir = provenanceDownloader.download(provenance)
62+
val dir = provenanceDownloader.download(provenance as RemoteProvenance)
6263

6364
return createFileList(dir).also {
6465
try {

scanner/src/test/kotlin/ScannerTest.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import org.ossreviewtoolkit.model.Package
4949
import org.ossreviewtoolkit.model.PackageType
5050
import org.ossreviewtoolkit.model.Provenance
5151
import org.ossreviewtoolkit.model.RemoteArtifact
52+
import org.ossreviewtoolkit.model.RemoteProvenance
5253
import org.ossreviewtoolkit.model.RepositoryProvenance
5354
import org.ossreviewtoolkit.model.ScanResult
5455
import org.ossreviewtoolkit.model.ScanSummary
@@ -973,7 +974,7 @@ private class FakePathScannerWrapper : PathScannerWrapper {
973974
* provenance, instead of actually downloading the source code.
974975
*/
975976
private class FakeProvenanceDownloader(val filename: String = "fake.txt") : ProvenanceDownloader {
976-
override fun download(provenance: KnownProvenance): File =
977+
override fun download(provenance: RemoteProvenance): File =
977978
createOrtTempDir().apply {
978979
resolve(filename).writeText(provenance.toYaml())
979980
}
@@ -1012,7 +1013,7 @@ private class FakePackageProvenanceResolver : PackageProvenanceResolver {
10121013
* An implementation of [NestedProvenanceResolver] that always returns a non-nested provenance.
10131014
*/
10141015
private class FakeNestedProvenanceResolver : NestedProvenanceResolver {
1015-
override suspend fun resolveNestedProvenance(provenance: KnownProvenance): NestedProvenance =
1016+
override suspend fun resolveNestedProvenance(provenance: RemoteProvenance): NestedProvenance =
10161017
NestedProvenance(root = provenance, subRepositories = emptyMap())
10171018
}
10181019

utils/test/src/main/kotlin/Utils.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import org.ossreviewtoolkit.model.OrtResult
3737
import org.ossreviewtoolkit.model.Package
3838
import org.ossreviewtoolkit.model.ProvenanceResolutionResult
3939
import org.ossreviewtoolkit.model.RemoteArtifact
40+
import org.ossreviewtoolkit.model.RemoteProvenance
4041
import org.ossreviewtoolkit.model.RepositoryProvenance
4142
import org.ossreviewtoolkit.model.ScanResult
4243
import org.ossreviewtoolkit.model.ScannerRun
@@ -198,7 +199,7 @@ fun scannerRunOf(vararg pkgScanResults: Pair<Identifier, List<ScanResult>>): Sca
198199

199200
return ScannerRun.EMPTY.copy(
200201
provenances = pkgScanResultsWithKnownProvenance.mapTo(mutableSetOf()) { (id, scanResultsForId) ->
201-
val packageProvenance = scanResultsForId.firstOrNull()?.provenance as KnownProvenance
202+
val packageProvenance = scanResultsForId.firstOrNull()?.provenance as RemoteProvenance
202203

203204
ProvenanceResolutionResult(
204205
id = id,

0 commit comments

Comments
 (0)