Skip to content

Commit

Permalink
analyzer: Support references to local modules with GoMod
Browse files Browse the repository at this point in the history
The only way of referencing a module on the local file system from
another is by specifying a relative or absolute path via a replace
directive. This is currently not supported by ORT's Go Modules
integration.

Implement that support, so that a reference inbetween two modules on
the local file system gets represent as a `PackageReference` between
the corresponding `Project`s (in ORT speak). However, keep on failing
in case a referenced module is not located within the analysis root,
because that would violate ORT's package manager API.

Signed-off-by: Frank Viernau <[email protected]>
  • Loading branch information
fviernau committed Oct 19, 2023
1 parent d351a59 commit b778c25
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 18 deletions.
32 changes: 25 additions & 7 deletions analyzer/src/funTest/kotlin/managers/GoModFunTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ package org.ossreviewtoolkit.analyzer.managers
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.should
import org.ossreviewtoolkit.downloader.VersionControlSystem

import org.ossreviewtoolkit.model.toYaml
import org.ossreviewtoolkit.utils.test.getAssetFile
import org.ossreviewtoolkit.utils.test.matchExpectedResult
import java.io.File

class GoModFunTest : StringSpec({
val testDir = getAssetFile("projects/synthetic")
Expand Down Expand Up @@ -93,12 +95,28 @@ class GoModFunTest : StringSpec({
result.toYaml() should matchExpectedResult(expectedResultFile, definitionFile)
}

"Local module dependencies make the analysis fail" {
// TODO: Implement support for local dependencies, see https://github.com/oss-review-toolkit/ort/issues/7649.
val definitionFile = testDir.resolve("gomod-submodules/app/go.mod")

val result = create("GoMod").resolveSingleProject(definitionFile)

result.issues shouldHaveSize 1
"Project dependencies with a (relative) local module dependency are detected correctly" {
val definitionFileApp = testDir.resolve("gomod-submodules/app/go.mod")
val definitionFileUtils = testDir.resolve("gomod-submodules/utils/go.mod")
val expectedResultFile = testDir.resolve("gomod-submodules-embed-expected-output.yml")
val expectedDefinitionFilePathUtils = getDefinitionFilePath(definitionFileUtils)

val result = create("GoMod").collateMultipleProjects(definitionFileApp, definitionFileUtils)

result.withResolvedScopes().toYaml() should matchExpectedResult(
expectedResultFile,
definitionFileApp,
custom = mapOf(
"<REPLACE_DEFINITION_FILE_PATH_UTILS>" to expectedDefinitionFilePathUtils,
"<REPLACE_PATH_UTILS>" to expectedDefinitionFilePathUtils.substringBeforeLast('/')
)
)
}
})

private fun getDefinitionFilePath(definitionFile: File): String {
val projectDir = definitionFile.parentFile
val vcsDir = VersionControlSystem.forDirectory(projectDir)!!
val path = vcsDir.getPathToRoot(projectDir)
return "$path/${definitionFile.name}"
}
26 changes: 22 additions & 4 deletions analyzer/src/main/kotlin/managers/GoMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,20 @@ class GoMod(

return if (version.isBlank()) {
// If the version is blank, it is a project in ORT speak.
check(main) { "Found a local module dependency which is not supported." }

checkNotNull(dir) { "For projects, the directory is expected to not be null." }

val projectDir = File(dir)

Check warning on line 225 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L225

Added line #L225 was not covered by tests

require(projectDir.absoluteFile.startsWith(analysisRoot.absoluteFile)) {
"A replace directive references a module in '${projectDir.absolutePath}' outside of analysis root " +

Check warning on line 228 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L228

Added line #L228 was not covered by tests
"which is not supported."
}

Identifier(
type = managerName,
namespace = "",
name = path,
version = processProjectVcs(File(dir)).revision
name = getProjectName(projectDir),
version = processProjectVcs(projectDir).revision

Check warning on line 236 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L235-L236

Added lines #L235 - L236 were not covered by tests
)
} else {
// If the version is not blank, it is a package in ORT speak.
Expand All @@ -241,6 +246,19 @@ class GoMod(
}
}

private fun getProjectName(projectDir: File): String {
projectDir.resolve("go.mod").let { goModFile ->

Check warning on line 250 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L250

Added line #L250 was not covered by tests
require(goModFile.isFile) {
"Expected file '$goModFile.' which does not exist."

Check warning on line 252 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L252

Added line #L252 was not covered by tests
}
}

Check warning on line 254 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L254

Added line #L254 was not covered by tests

val list = runGo("list", "-m", "-json", "-buildvcs=false", workingDir = projectDir)

Check warning on line 256 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L256

Added line #L256 was not covered by tests

return list.stdout.byteInputStream().use { JSON.decodeToSequence<ModuleInfo>(it) }.single().path

Check warning on line 258 in analyzer/src/main/kotlin/managers/GoMod.kt

View check run for this annotation

Codecov / codecov/patch

analyzer/src/main/kotlin/managers/GoMod.kt#L258

Added line #L258 was not covered by tests
}


/**
* Return the list of all modules contained in the dependency tree with resolved versions and the 'replace'
* directive applied.
Expand Down
17 changes: 10 additions & 7 deletions analyzer/src/testFixtures/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.ossreviewtoolkit.analyzer.managers

import io.kotest.core.spec.Spec
import io.kotest.inspectors.forAll
import io.kotest.matchers.collections.haveSize
import io.kotest.matchers.collections.shouldHaveAtLeastSize
import io.kotest.matchers.nulls.shouldNotBeNull
Expand Down Expand Up @@ -63,19 +64,21 @@ fun PackageManager.resolveSingleProject(definitionFile: File, resolveScopes: Boo
}

/**
* Resolve the dependencies of a [definitionFile] which should create at least one project. All created projects will be
* collated in an [AnalyzerResult] with their dependency graph.
* Resolve the dependencies of all [definitionFiles] which should create at least one project. All created projects will
* be collated in an [AnalyzerResult] with their dependency graph.
*/
fun PackageManager.collateMultipleProjects(definitionFile: File): AnalyzerResult {
val managerResult = resolveDependencies(listOf(definitionFile), emptyMap())
fun PackageManager.collateMultipleProjects(vararg definitionFiles: File): AnalyzerResult {
val managerResult = resolveDependencies(definitionFiles.toList(), emptyMap())

val builder = AnalyzerResultBuilder()
managerResult.dependencyGraph?.let {
builder.addDependencyGraph(managerName, it).addPackages(managerResult.sharedPackages)
}
managerResult.projectResults[definitionFile].shouldNotBeNull {
this shouldHaveAtLeastSize 1
forEach { builder.addResult(it) }
definitionFiles.forAll { definitionFile ->
managerResult.projectResults[definitionFile].shouldNotBeNull {
this shouldHaveAtLeastSize 1
forEach { builder.addResult(it) }
}
}

return builder.build()
Expand Down

0 comments on commit b778c25

Please sign in to comment.