Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor FossID config #9745

Merged
merged 5 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions model/src/main/resources/reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,8 @@ ort:
options:
serverUrl: 'https://fossid.example.com/instance/'

namingProjectPattern: '$Var1_$Var2'
namingScanPattern: '$Var1_#projectBaseCode_$Var3'
namingVariableVar1: myOrg
namingVariableVar2: myTeam
namingVariableVar3: myUnit
projectName: 'My Project'
mnonnenmacher marked this conversation as resolved.
Show resolved Hide resolved
namingScanPattern: '#projectName_#repositoryName_#currentTimestamp_#deltaTag_#branch'

waitForResult: false

Expand All @@ -271,7 +268,7 @@ ort:

timeout: 60

urlMappingExample: "https://my-repo.example.org(?<repoPath>.*) -> ssh://my-mapped-repo.example.org${repoPath}"
urlMappings: "https://my-repo.example.org(?<repoPath>.*) -> ssh://my-mapped-repo.example.org${repoPath}"

sensitivity: 10

Expand Down
9 changes: 3 additions & 6 deletions model/src/test/kotlin/config/OrtConfigurationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -255,19 +255,16 @@ class OrtConfigurationTest : WordSpec({

options shouldContainExactly mapOf(
"serverUrl" to "https://fossid.example.com/instance/",
"namingProjectPattern" to "\$Var1_\$Var2",
"namingScanPattern" to "\$Var1_#projectBaseCode_\$Var3",
"namingVariableVar1" to "myOrg",
"namingVariableVar2" to "myTeam",
"namingVariableVar3" to "myUnit",
"projectName" to "My Project",
"namingScanPattern" to "#projectName_#repositoryName_#currentTimestamp_#deltaTag_#branch",
"waitForResult" to "false",
"keepFailedScans" to "false",
"deltaScans" to "true",
"deltaScanLimit" to "10",
"detectLicenseDeclarations" to "true",
"detectCopyrightStatements" to "true",
"timeout" to "60",
"urlMappingExample" to urlMapping,
"urlMappings" to urlMapping,
"sensitivity" to "10"
)

Expand Down
2 changes: 1 addition & 1 deletion plugins/scanners/fossid/src/main/kotlin/FossId.kt
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ class FossId internal constructor(

mnonnenmacher marked this conversation as resolved.
Show resolved Hide resolved
val result = runBlocking {
try {
val projectCode = namingProvider.createProjectCode(repositoryName)
val projectCode = config.projectName ?: repositoryName

if (getProject(projectCode) == null) {
logger.info { "Creating project '$projectCode'..." }
Expand Down
59 changes: 26 additions & 33 deletions plugins/scanners/fossid/src/main/kotlin/FossIdConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,8 @@ import org.ossreviewtoolkit.utils.common.Options
* license declarations.
* * **"options.detectCopyrightStatements":** When set, the FossID scan is configured to automatically detect copyright
* statements.
*
* Naming conventions options. If they are not set, default naming conventions are used.
* * **"options.namingProjectPattern":** A pattern for project names when projects are created on the FossID instance.
* Contains variables prefixed by "$" e.g. "$Var1_$Var2". Variables are also passed as options and are prefixed by
* [NAMING_CONVENTION_VARIABLE_PREFIX] e.g. namingVariableVar1 = "foo".
* * **"options.namingScanPattern":** A pattern for scan names when scans are created on the FossID instance.
* * **"options.namingScanPattern":** A pattern for scan names when scans are created on the FossID instance. If not
* set, a default pattern is used.
*
* URL mapping options. These options allow transforming the URLs of specific repositories before they are passed to
* the FossID service. This may be necessary if FossID uses a different mechanism to clone a repository, e.g. via SSH
Expand Down Expand Up @@ -84,6 +80,12 @@ data class FossIdConfig(
/** The API key to access the FossID server. */
val apiKey: String,

/** The name of the FossID project. If `null`, the name will be determined from the repository URL. */
val projectName: String?,

/** The pattern for scan names when scans are created on the FossID instance. If null, a default pattern is used. */
val namingScanPattern: String?,

/** Flag whether the scanner should wait for the completion of FossID scans. */
val waitForResult: Boolean,

Expand Down Expand Up @@ -116,8 +118,8 @@ data class FossIdConfig(
/** The sensitivity of the scan. */
val sensitivity: Int,

/** Stores the map with FossID-specific configuration options. */
private val options: Map<String, String>
/** A comma-separated list of URL mappings. */
val urlMappings: String?
) {
companion object {
/** Name of the configuration property for the server URL. */
Expand All @@ -129,12 +131,12 @@ data class FossIdConfig(
/** Name of the configuration property for the API key. */
private const val PROP_API_KEY = "apiKey"

/** Name of the configuration property to set the project name. */
private const val PROP_PROJECT_NAME = "projectName"

/** Name of the configuration property controlling whether ORT should wait for FossID results. */
private const val PROP_WAIT_FOR_RESULT = "waitForResult"

/** Name of the configuration property defining the naming convention for projects. */
private const val PROP_NAMING_PROJECT_PATTERN = "namingProjectPattern"

/** Name of the configuration property defining the naming convention for scans. */
private const val PROP_NAMING_SCAN_PATTERN = "namingScanPattern"

Expand Down Expand Up @@ -163,10 +165,8 @@ data class FossIdConfig(
/** Name of the configuration property defining the sensitivity of the scan. */
private const val PROP_SENSITIVITY = "sensitivity"

/**
* The scanner options beginning with this prefix will be used to parameterize project and scan names.
*/
private const val NAMING_CONVENTION_VARIABLE_PREFIX = "namingVariable"
/** Name of the configuration property defining the URL mappings. */
private const val PROP_URL_MAPPINGS = "urlMappings"

/**
* Default timeout in minutes for communication with FossID.
Expand Down Expand Up @@ -196,6 +196,9 @@ data class FossIdConfig(
val apiKey = secrets[PROP_API_KEY]
?: throw IllegalArgumentException("No FossID API Key configuration found.")

val projectName = options[PROP_PROJECT_NAME]
val namingScanPattern = options[PROP_NAMING_SCAN_PATTERN]

val waitForResult = options[PROP_WAIT_FOR_RESULT]?.toBooleanStrict() ?: true

val keepFailedScans = options[PROP_KEEP_FAILED_SCANS]?.toBooleanStrict() ?: false
Expand All @@ -212,6 +215,8 @@ data class FossIdConfig(

val sensitivity = options[PROP_SENSITIVITY]?.toInt() ?: DEFAULT_SENSITIVITY

val urlMappings = options[PROP_URL_MAPPINGS]

require(deltaScanLimit > 0) {
"deltaScanLimit must be > 0, current value is $deltaScanLimit."
}
Expand All @@ -226,6 +231,8 @@ data class FossIdConfig(
serverUrl = serverUrl,
user = user,
apiKey = apiKey,
projectName = projectName,
namingScanPattern = namingScanPattern,
waitForResult = waitForResult,
keepFailedScans = keepFailedScans,
deltaScans = deltaScans,
Expand All @@ -234,34 +241,20 @@ data class FossIdConfig(
detectCopyrightStatements = detectCopyrightStatements,
timeout = timeout,
fetchSnippetMatchedLines = fetchSnippetMatchedLines,
options = options,
snippetsLimit = snippetsLimit,
sensitivity = sensitivity
sensitivity = sensitivity,
urlMappings = urlMappings
)
}
}

/**
* Create a [FossIdNamingProvider] helper object based on the configuration stored in this object.
*/
fun createNamingProvider(): FossIdNamingProvider {
val namingProjectPattern = options[PROP_NAMING_PROJECT_PATTERN]?.also {
logger.info { "Naming pattern for projects is $it." }
}

val namingScanPattern = options[PROP_NAMING_SCAN_PATTERN]?.also {
logger.info { "Naming pattern for scans is $it." }
}

val namingConventionVariables = options
.filterKeys { it.startsWith(NAMING_CONVENTION_VARIABLE_PREFIX) }
.mapKeys { it.key.substringAfter(NAMING_CONVENTION_VARIABLE_PREFIX) }

return FossIdNamingProvider(namingProjectPattern, namingScanPattern, namingConventionVariables)
}
fun createNamingProvider() = FossIdNamingProvider(namingScanPattern, projectName)

/**
* Create a [FossIdUrlProvider] helper object based on the configuration stored in this object.
*/
fun createUrlProvider() = FossIdUrlProvider.create(options)
fun createUrlProvider() = FossIdUrlProvider.create(urlMappings?.split(',').orEmpty())
}
37 changes: 12 additions & 25 deletions plugins/scanners/fossid/src/main/kotlin/FossIdNamingProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,22 @@ import java.time.format.DateTimeFormatter
import org.apache.logging.log4j.kotlin.logger

/**
* This class provides names for projects and scans when the FossID scanner creates them, following a given pattern.
* If one or both patterns is null, a default naming convention is used.
* This class provides names for scans when the FossID scanner creates them, based on the provided [namingScanPattern].
* If the pattern is null, a default pattern is used.
*
* [namingScanPattern] and [namingProjectPattern] are patterns describing the name using variables, e.g. "$var1_$var2".
* Variable values are given in the map [namingConventionVariables].
* The pattern can include built-in variables which are prefixed in the pattern with `#`, e.g., `#projectName_#branch`.
*
* There are also built-in variables. Built-in variables are prefixed in the pattern with "#" e.g. "$var1_#builtin".
* Available built-in variables:
* The following built-in variables are available:
* * **projectName**: The project name, if configured in the scanner options.
* * **repositoryName**: The name of the repository (i.e., the part of the URL before .git).
* * **currentTimestamp**: The current time.
* * **deltaTag** (scan code only): If delta scans are enabled, this qualifies the scan as an *origin* scan or a *delta*
* scan.
* * **deltaTag**: If delta scans are enabled, this qualifies the scan as an `origin` scan or a `delta` scan.
* * **branch**: The branch name (revision) to scan. FossID only allows alphanumeric characters and '-' in names, all
* other characters are replaced with underscores. Might be shortened to fit the scan code length limit.
*/
class FossIdNamingProvider(
private val namingProjectPattern: String?,
private val namingScanPattern: String?,
private val namingConventionVariables: Map<String, String>
private val projectName: String?
) {
companion object {
val ALLOWED_CHARACTERS_REGEX = Regex("[^\\w-]")
Expand All @@ -54,14 +51,6 @@ class FossIdNamingProvider(
const val MAX_SCAN_CODE_LEN = 254
}

fun createProjectCode(repositoryName: String): String =
namingProjectPattern?.let {
val builtins = mapOf(
"#repositoryName" to repositoryName
)
replaceNamingConventionVariables(namingProjectPattern, builtins, namingConventionVariables)
} ?: repositoryName

fun createScanCode(repositoryName: String, deltaTag: FossId.DeltaTag? = null, branch: String = ""): String {
val pattern = namingScanPattern ?: buildString {
append("#repositoryName_#currentTimestamp")
Expand All @@ -70,13 +59,14 @@ class FossIdNamingProvider(
}

val builtins = mutableMapOf(
"#projectName" to projectName.orEmpty(),
"#repositoryName" to repositoryName,
"#deltaTag" to deltaTag?.name?.lowercase().orEmpty()
)

builtins += "#branch" to normalizeBranchName(branch, pattern, builtins)

return replaceNamingConventionVariables(pattern, builtins, namingConventionVariables)
return replaceNamingConventionVariables(pattern, builtins)
}

/**
Expand All @@ -91,8 +81,7 @@ class FossIdNamingProvider(
val noBranchScanCode =
replaceNamingConventionVariables(
scanCodeNamingPattern.replace("#branch", ""),
scanCodeVariables,
namingConventionVariables
scanCodeVariables
)

require(noBranchScanCode.length < MAX_SCAN_CODE_LEN) {
Expand All @@ -111,14 +100,12 @@ class FossIdNamingProvider(
*/
private fun replaceNamingConventionVariables(
namingConventionPattern: String,
builtins: Map<String, String>,
namingConventionVariables: Map<String, String>
builtins: Map<String, String>
): String {
logger.info { "Parameterizing the name with pattern '$namingConventionPattern'." }
val currentTimestamp = FORMATTER.format(LocalDateTime.now())

val allVariables =
namingConventionVariables.mapKeys { "\$${it.key}" } + builtins + ("#currentTimestamp" to currentTimestamp)
val allVariables = builtins + ("#currentTimestamp" to currentTimestamp)

return allVariables.entries.fold(namingConventionPattern) { acc, entry ->
acc.replace(entry.key, entry.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ class FossIdUrlProvider private constructor(
return FossIdUrlProvider(mappings)
}

/**
* Create a new instance of [FossIdUrlProvider] and configure the URL mappings from the given configuration
* [options].
*/
fun create(options: Map<String, String>): FossIdUrlProvider =
create(options.filter { it.key.startsWith(PREFIX_URL_MAPPING) }.values)

/**
* Try to fetch credentials for [repoUrl] from the current [Authenticator]. Return *null* if no matching host
* is found.
Expand Down
Loading
Loading