forked from oss-review-toolkit/ort
-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.gradle.kts
222 lines (181 loc) · 8.27 KB
/
build.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
* Copyright (C) 2017 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
*/
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.eclipse.jgit.ignore.FastIgnoreRule
import org.jetbrains.gradle.ext.Gradle
import org.jetbrains.gradle.ext.runConfigurations
import org.jetbrains.gradle.ext.settings
plugins {
// Apply third-party plugins.
alias(libs.plugins.dependencyAnalysis)
alias(libs.plugins.gitSemver)
alias(libs.plugins.ideaExt)
alias(libs.plugins.versions)
}
semver {
// Do not create an empty release commit when running the "releaseVersion" task.
createReleaseCommit = false
// Do not let untracked files bump the version or add a "-SNAPSHOT" suffix.
noDirtyCheck = true
}
// Only override a default version (which usually is "unspecified"), but not a custom version.
if (version == Project.DEFAULT_VERSION) {
version = semver.semVersion.takeIf { it.isPreRelease }
// To get rid of a build part's "+" prefix because Docker tags do not support it, use only the original "build"
// part as the "pre-release" part.
?.toString()?.replace("${semver.defaultPreRelease}+", "")
// Fall back to a plain version without pre-release or build parts.
?: semver.version
}
logger.lifecycle("Building ORT version $version.")
idea {
project {
settings {
runConfigurations {
// Disable "condensed" multi-line diffs when running tests from the IDE via Gradle run configurations to
// more easily accept actual results as expected results.
defaults(Gradle::class.java) {
jvmArgs = "-Dkotest.assertions.multi-line-diff=simple"
}
}
}
}
}
extensions.findByName("develocity")?.withGroovyBuilder {
getProperty("buildScan")?.withGroovyBuilder {
setProperty("termsOfUseUrl", "https://gradle.com/terms-of-service")
setProperty("termsOfUseAgree", "yes")
}
}
tasks.named<DependencyUpdatesTask>("dependencyUpdates") {
gradleReleaseChannel = "current"
outputFormatter = "json"
val nonFinalQualifiers = listOf(
"alpha", "b", "beta", "cr", "dev", "ea", "eap", "m", "milestone", "pr", "preview", "rc", "\\d{14}"
).joinToString("|", "(", ")")
val nonFinalQualifiersRegex = Regex(".*[.-]$nonFinalQualifiers[.\\d-+]*", RegexOption.IGNORE_CASE)
rejectVersionIf {
candidate.version.matches(nonFinalQualifiersRegex)
}
}
// Gradle's "dependencies" task selector only executes on a single / the current project [1]. However, sometimes viewing
// all dependencies at once is beneficial, e.g. for debugging version conflict resolution.
// [1]: https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#sec:listing_dependencies
tasks.register("allDependencies") {
group = "Help"
description = "Displays all dependencies declared in all projects."
val dependenciesTasks = getTasksByName("dependencies", /* recursive = */ true).sorted()
dependsOn(dependenciesTasks)
// Ensure deterministic output by requiring to run tasks after each other in always the same order.
dependenciesTasks.zipWithNext().forEach { (a, b) ->
b.mustRunAfter(a)
}
}
val checkCopyrightsInNoticeFile by tasks.registering {
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
val files = CopyrightableFiles.filter(gitFilesProvider)
val noticeFile = rootDir.resolve("NOTICE")
val genericHolderPrefix = "The ORT Project Authors"
inputs.files(files)
doLast {
val allCopyrights = mutableSetOf<String>()
var hasViolations = false
files.forEach { file ->
val copyrights = CopyrightUtils.extract(file)
if (copyrights.isNotEmpty()) {
allCopyrights += copyrights
} else {
hasViolations = true
logger.error("The file '$file' has no Copyright statement.")
}
}
val notices = noticeFile.readLines()
CopyrightUtils.extractHolders(allCopyrights).forEach { holder ->
if (!holder.startsWith(genericHolderPrefix) && notices.none { holder in it }) {
hasViolations = true
logger.error("The '$holder' Copyright holder is not captured in '$noticeFile'.")
}
}
if (hasViolations) throw GradleException("There were errors in Copyright statements.")
}
}
val checkLicenseHeaders by tasks.registering {
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
val files = CopyrightableFiles.filter(gitFilesProvider)
inputs.files(files)
// To be on the safe side in case any called helper functions are not thread safe.
mustRunAfter(checkCopyrightsInNoticeFile)
doLast {
var hasErrors = false
files.forEach { file ->
val headerLines = LicenseUtils.extractHeader(file)
val holders = CopyrightUtils.extractHolders(headerLines)
if (holders.singleOrNull() != CopyrightUtils.EXPECTED_HOLDER) {
hasErrors = true
logger.error("Unexpected copyright holder(s) in file '$file': $holders")
}
if (!headerLines.joinToString("\n").endsWith(LicenseUtils.expectedHeader)) {
hasErrors = true
logger.error("Unexpected license header in file '$file'.")
}
}
if (hasErrors) throw GradleException("There were errors in license headers.")
}
}
val checkGitAttributes by tasks.registering {
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
inputs.files(gitFilesProvider)
doLast {
var hasErrors = false
val files = gitFilesProvider.get()
val gitAttributesFiles = files.filter { it.endsWith(".gitattributes") }
val commentChars = setOf('#', '/')
gitAttributesFiles.forEach { gitAttributesFile ->
logger.lifecycle("Checking file '$gitAttributesFile'...")
val ignoreRules = gitAttributesFile.readLines()
// Skip empty and comment lines.
.map { it.trim() }
.filter { it.isNotEmpty() && it.first() !in commentChars }
// The pattern is the part before the first whitespace.
.mapTo(mutableSetOf()) { line -> line.takeWhile { !it.isWhitespace() } }
// Create ignore rules from valid patterns.
.mapIndexedNotNull { index, pattern ->
runCatching {
FastIgnoreRule(pattern)
}.onFailure {
logger.warn("File '$gitAttributesFile' contains an invalid pattern in line ${index + 1}: $it")
}.getOrNull()
}
// Check only those files that are in scope of this ".gitattributes" file.
val gitAttributesDir = gitAttributesFile.parentFile
val filesInScope = files.filter { it.startsWith(gitAttributesDir) }
ignoreRules.forEach { rule ->
val matchesAnything = filesInScope.any { file ->
val relativeFile = file.relativeTo(gitAttributesDir)
rule.isMatch(relativeFile.invariantSeparatorsPath, /* directory = */ false)
}
if (!matchesAnything) {
hasErrors = true
logger.error("Rule '$rule' does not match anything.")
}
}
}
if (hasErrors) throw GradleException("There were stale '.gitattribute' entries.")
}
}