@@ -48,6 +48,9 @@ import org.ossreviewtoolkit.downloader.VersionControlSystem
48
48
import org.ossreviewtoolkit.downloader.WorkingTree
49
49
import org.ossreviewtoolkit.model.VcsInfo
50
50
import org.ossreviewtoolkit.model.VcsType
51
+ import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.OptionKey.Companion.getOrDefault
52
+ import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.OptionKey.HISTORY_DEPTH
53
+ import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.OptionKey.UPDATE_NESTED_SUBMODULES
51
54
import org.ossreviewtoolkit.utils.common.CommandLineTool
52
55
import org.ossreviewtoolkit.utils.common.Options
53
56
import org.ossreviewtoolkit.utils.common.Os
@@ -60,14 +63,50 @@ import org.ossreviewtoolkit.utils.ort.showStackTrace
60
63
import org.semver4j.RangesList
61
64
import org.semver4j.RangesListFactory
62
65
63
- // TODO: Make this configurable.
64
- const val GIT_HISTORY_DEPTH = 50
65
-
66
66
// Replace prefixes of Git submodule repository URLs.
67
67
private val REPOSITORY_URL_PREFIX_REPLACEMENTS = listOf (
68
68
" git://" to " https://"
69
69
)
70
70
71
+ enum class OptionKey (val key : String , val defaultValue : String , val deprecated : Boolean = false ) {
72
+ // Git-specific configuration option for the depth of commit history to fetch
73
+ HISTORY_DEPTH (" historyDepth" , " 50" ),
74
+
75
+ // Git-specific configuration option to define if nested submodules should be updated, or if only the
76
+ // submodules on the top-level should be initialized, updated, and cloned.
77
+ UPDATE_NESTED_SUBMODULES (" updateNestedSubmodules" , " true" ),
78
+
79
+ // Example for deprecating a configuration option
80
+ DO_NOT_USE (" doNotUse" , " some-value" , deprecated = true );
81
+
82
+ companion object {
83
+ private val map = entries.associateBy(OptionKey ::key)
84
+ private val validKeys: Set <String > get() = map.keys
85
+
86
+ fun validate (options : Options ): OptionsValidationResult {
87
+ val unknownKeys = options.keys - OptionKey .validKeys
88
+ val deprecatedKeys = entries.filter { it.deprecated }.map { it.key }.toSet()
89
+ val usedDeprecatedKeys = options.keys.intersect(deprecatedKeys)
90
+
91
+ return OptionsValidationResult (
92
+ isSuccess = unknownKeys.isEmpty(),
93
+ errors = unknownKeys.map { " Unknown Git-specific configuration key: '$it '" },
94
+ warnings = usedDeprecatedKeys.map {
95
+ " Git-specific configuration key '$it ' is deprecated and may be removed in future versions."
96
+ }
97
+ )
98
+ }
99
+
100
+ fun getOrDefault (options : Options , key : OptionKey ): String = options[key.key] ? : key.defaultValue
101
+ }
102
+ }
103
+
104
+ data class OptionsValidationResult (
105
+ val isSuccess : Boolean ,
106
+ val errors : List <String > = emptyList(),
107
+ val warnings : List <String > = emptyList()
108
+ )
109
+
71
110
object GitCommand : CommandLineTool {
72
111
private val versionRegex = Regex (" [Gg]it [Vv]ersion (?<version>[\\ d.a-z-]+)(\\ s.+)?" )
73
112
@@ -178,9 +217,24 @@ class Git : VersionControlSystem(GitCommand) {
178
217
Git (this ).use { git ->
179
218
logger.info { " Updating working tree from ${workingTree.getRemoteUrl()} ." }
180
219
181
- updateWorkingTreeWithoutSubmodules(workingTree, git, revision).mapCatching {
220
+ val optionsValidationResult = OptionKey .validate(options)
221
+ optionsValidationResult.warnings.forEach { logger.warn { it } }
222
+ optionsValidationResult.warnings.forEach { logger.error { it } }
223
+ require(optionsValidationResult.isSuccess) {
224
+ optionsValidationResult.errors.joinToString()
225
+ }
226
+
227
+ val historyDepth = getOrDefault(options, HISTORY_DEPTH ).toInt()
228
+ updateWorkingTreeWithoutSubmodules(workingTree, git, revision, historyDepth).mapCatching {
182
229
// In case this throws the exception gets encapsulated as a failure.
183
- if (recursive) updateSubmodules(workingTree)
230
+ if (recursive) {
231
+ val updateNestedSubmodules = getOrDefault(options, UPDATE_NESTED_SUBMODULES ).toBoolean()
232
+ updateSubmodules(
233
+ workingTree,
234
+ recursive = updateNestedSubmodules,
235
+ historyDepth = historyDepth
236
+ )
237
+ }
184
238
185
239
revision
186
240
}
@@ -190,12 +244,13 @@ class Git : VersionControlSystem(GitCommand) {
190
244
private fun updateWorkingTreeWithoutSubmodules (
191
245
workingTree : WorkingTree ,
192
246
git : Git ,
193
- revision : String
247
+ revision : String ,
248
+ historyDepth : Int
194
249
): Result <String > =
195
250
runCatching {
196
- logger.info { " Trying to fetch only revision '$revision ' with depth limited to $GIT_HISTORY_DEPTH ." }
251
+ logger.info { " Trying to fetch only revision '$revision ' with depth limited to $historyDepth ." }
197
252
198
- val fetch = git.fetch().setDepth(GIT_HISTORY_DEPTH )
253
+ val fetch = git.fetch().setDepth(historyDepth )
199
254
200
255
// See https://git-scm.com/docs/gitrevisions#_specifying_revisions for how Git resolves ambiguous
201
256
// names. In particular, tag names have higher precedence than branch names.
@@ -213,13 +268,13 @@ class Git : VersionControlSystem(GitCommand) {
213
268
it.showStackTrace()
214
269
215
270
logger.info { " Could not fetch only revision '$revision ': ${it.collectMessages()} " }
216
- logger.info { " Falling back to fetching all refs with depth limited to $GIT_HISTORY_DEPTH ." }
271
+ logger.info { " Falling back to fetching all refs with depth limited to $historyDepth ." }
217
272
218
- git.fetch().setDepth(GIT_HISTORY_DEPTH ).setTagOpt(TagOpt .FETCH_TAGS ).call()
273
+ git.fetch().setDepth(historyDepth ).setTagOpt(TagOpt .FETCH_TAGS ).call()
219
274
}.recoverCatching {
220
275
it.showStackTrace()
221
276
222
- logger.info { " Could not fetch with only a depth of $GIT_HISTORY_DEPTH : ${it.collectMessages()} " }
277
+ logger.info { " Could not fetch with only a depth of $historyDepth : ${it.collectMessages()} " }
223
278
logger.info { " Falling back to fetch everything including tags." }
224
279
225
280
git.fetch().setUnshallow(true ).setTagOpt(TagOpt .FETCH_TAGS ).call()
@@ -274,7 +329,14 @@ class Git : VersionControlSystem(GitCommand) {
274
329
revision
275
330
}
276
331
277
- private fun updateSubmodules (workingTree : WorkingTree ) {
332
+ /* *
333
+ * Initialize, update, and clone all the submodules in a working tree.
334
+ *
335
+ * If [recursive] is set to true, then the operations are not only performed on the
336
+ * submodules in the top-level of the working tree, but also on the submodules of the submodules, and so on.
337
+ * If [recursive] is set to false, only the submodules on the top-level are initialized, updated, and cloned.
338
+ */
339
+ private fun updateSubmodules (workingTree : WorkingTree , recursive : Boolean , historyDepth : Int ) {
278
340
if (! workingTree.getRootPath().resolve(" .gitmodules" ).isFile) return
279
341
280
342
val insteadOf = REPOSITORY_URL_PREFIX_REPLACEMENTS .map { (prefix, replacement) ->
@@ -283,14 +345,27 @@ class Git : VersionControlSystem(GitCommand) {
283
345
284
346
runCatching {
285
347
// TODO: Migrate this to JGit once https://bugs.eclipse.org/bugs/show_bug.cgi?id=580731 is implemented.
286
- workingTree.runGit(" submodule" , " update" , " --init" , " --recursive" , " --depth" , " $GIT_HISTORY_DEPTH " )
348
+ val updateArgs = mutableListOf (" submodule" , " update" , " --init" , " --depth" , " $historyDepth " ).apply {
349
+ if (recursive) { add(" --recursive" ) }
350
+ }
351
+
352
+ workingTree.runGit(* updateArgs.toTypedArray())
287
353
288
354
insteadOf.forEach {
289
- workingTree.runGit(" submodule" , " foreach" , " --recursive" , " git config $it " )
355
+ val foreachArgs = mutableListOf (" submodule" , " foreach" ).apply {
356
+ if (recursive) { add(" --recursive" ) }
357
+ add(" git config $it " )
358
+ }
359
+
360
+ workingTree.runGit(* foreachArgs.toTypedArray())
290
361
}
291
362
}.recover {
292
363
// As Git's dumb HTTP transport does not support shallow capabilities, also try to not limit the depth.
293
- workingTree.runGit(" submodule" , " update" , " --recursive" )
364
+ val fallbackArgs = mutableListOf (" submodule" , " update" ).apply {
365
+ if (recursive) { add(" --recursive" ) }
366
+ }
367
+
368
+ workingTree.runGit(* fallbackArgs.toTypedArray())
294
369
}
295
370
}
296
371
0 commit comments