Skip to content

Commit 359ee5c

Browse files
committed
Fix unknown licenses (#31223)
The goal of this commit is to address unknown licenses when producing the dependencies info report. We have two different checks that we run on licenses. The first check is whether or not we have stashed a copy of the license text for a dependency in the repository. The second is to map every dependency to a license type (e.g., BSD 3-clause). The problem here is that the way we were handling licenses in the second check differs from how we handle licenses in the first check. The first check works by finding a license file with the name of the artifact followed by the text -LICENSE.txt. Yet in some cases we allow mapping an artifact name to another name used to check for the license (e.g., we map lucene-.* to lucene, and opensaml-.* to shibboleth. The second check understood the first way of looking for a license file but not the second way. So in this commit we teach the second check about the mappings from artifact names to license names. We do this by copying the configuration from the dependencyLicenses task to the dependenciesInfo task and then reusing the code from the first check in the second check. There were some other challenges here though. For example, dependenciesInfo was checking too many dependencies. For now, we should only be checking direct dependencies and leaving transitive dependencies from another org.elasticsearch artifact to that artifact (we want to do this differently in a follow-up). We also want to disable dependenciesInfo for projects that we do not publish, users only care about licenses they might be exposed to if they use our assembled products. With all of the changes in this commit we have eliminated all unknown licenses. A follow-up will enforce that when we add a new dependency it does not get mapped to unknown, these will be forbidden in the future. Therefore, with this change and earlier changes are left having no unknown licenses and two custom licenses; custom here means it does not map to an SPDX license type. Those two licenses are xz and ldapsdk. A future change will not allow additional custom licenses unless they are explicitly whitelisted. This ensures that if a new dependency is added it is mapped to an SPDX license or mapped to custom because it does not have an SPDX license.
1 parent 4f1d55a commit 359ee5c

File tree

10 files changed

+87
-24
lines changed

10 files changed

+87
-24
lines changed

build.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ gradle.projectsEvaluated {
482482

483483
}
484484

485-
/* Remove assemble on all qa projects because we don't need to publish
485+
/* Remove assemble/dependenciesInfo on all qa projects because we don't need to publish
486486
* artifacts for them. */
487487
gradle.projectsEvaluated {
488488
subprojects {
@@ -492,6 +492,11 @@ gradle.projectsEvaluated {
492492
project.tasks.remove(assemble)
493493
project.build.dependsOn.remove('assemble')
494494
}
495+
Task dependenciesInfo = project.tasks.findByName('dependenciesInfo')
496+
if (dependenciesInfo) {
497+
project.tasks.remove(dependenciesInfo)
498+
project.precommit.dependsOn.remove('dependenciesInfo')
499+
}
495500
}
496501
}
497502
}

buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ class BuildPlugin implements Plugin<Project> {
746746

747747
private static configureDependenciesInfo(Project project) {
748748
Task deps = project.tasks.create("dependenciesInfo", DependenciesInfoTask.class)
749-
deps.dependencies = project.configurations.compile.allDependencies
749+
deps.runtimeConfiguration = project.configurations.runtime
750+
deps.compileOnlyConfiguration = project.configurations.compileOnly
751+
project.afterEvaluate {
752+
deps.mappings = project.dependencyLicenses.mappings
753+
}
750754
}
751755
}

buildSrc/src/main/groovy/org/elasticsearch/gradle/DependenciesInfoTask.groovy

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
1919

2020
package org.elasticsearch.gradle
2121

22+
import org.elasticsearch.gradle.precommit.DependencyLicensesTask
2223
import org.gradle.api.DefaultTask
24+
import org.gradle.api.artifacts.Configuration
2325
import org.gradle.api.artifacts.Dependency
26+
import org.gradle.api.artifacts.DependencyResolutionListener
2427
import org.gradle.api.artifacts.DependencySet
2528
import org.gradle.api.tasks.Input
2629
import org.gradle.api.tasks.InputDirectory
2730
import org.gradle.api.tasks.OutputFile
2831
import org.gradle.api.tasks.TaskAction
2932

33+
import java.util.regex.Matcher
34+
import java.util.regex.Pattern
3035

3136
/**
3237
* A task to gather information about the dependencies and export them into a csv file.
@@ -44,7 +49,14 @@ public class DependenciesInfoTask extends DefaultTask {
4449

4550
/** Dependencies to gather information from. */
4651
@Input
47-
public DependencySet dependencies
52+
public Configuration runtimeConfiguration
53+
54+
/** We subtract compile-only dependencies. */
55+
@Input
56+
public Configuration compileOnlyConfiguration
57+
58+
@Input
59+
public LinkedHashMap<String, String> mappings
4860

4961
/** Directory to read license files */
5062
@InputDirectory
@@ -59,15 +71,34 @@ public class DependenciesInfoTask extends DefaultTask {
5971

6072
@TaskAction
6173
public void generateDependenciesInfo() {
74+
75+
final DependencySet runtimeDependencies = runtimeConfiguration.getAllDependencies()
76+
// we have to resolve the transitive dependencies and create a group:artifactId:version map
77+
final Set<String> compileOnlyArtifacts =
78+
compileOnlyConfiguration
79+
.getResolvedConfiguration()
80+
.resolvedArtifacts
81+
.collect { it -> "${it.moduleVersion.id.group}:${it.moduleVersion.id.name}:${it.moduleVersion.id.version}" }
82+
6283
final StringBuilder output = new StringBuilder()
6384

64-
for (Dependency dependency : dependencies) {
65-
// Only external dependencies are checked
66-
if (dependency.group != null && dependency.group.contains("elasticsearch") == false) {
67-
final String url = createURL(dependency.group, dependency.name, dependency.version)
68-
final String licenseType = getLicenseType(dependency.group, dependency.name)
69-
output.append("${dependency.group}:${dependency.name},${dependency.version},${url},${licenseType}\n")
85+
for (final Dependency dependency : runtimeDependencies) {
86+
// we do not need compile-only dependencies here
87+
if (compileOnlyArtifacts.contains("${dependency.group}:${dependency.name}:${dependency.version}")) {
88+
continue
89+
}
90+
// only external dependencies are checked
91+
if (dependency.group != null && dependency.group.contains("org.elasticsearch")) {
92+
continue
7093
}
94+
95+
final String url = createURL(dependency.group, dependency.name, dependency.version)
96+
final String dependencyName = DependencyLicensesTask.getDependencyName(mappings, dependency.name)
97+
logger.info("mapped dependency ${dependency.group}:${dependency.name} to ${dependencyName} for license info")
98+
99+
final String licenseType = getLicenseType(dependency.group, dependencyName)
100+
output.append("${dependency.group}:${dependency.name},${dependency.version},${url},${licenseType}\n")
101+
71102
}
72103
outputFile.setText(output.toString(), 'UTF-8')
73104
}
@@ -173,7 +204,7 @@ are met:
173204
derived from this software without specific prior written permission\\.|
174205
(3\\.)? Neither the name of .+ nor the names of its
175206
contributors may be used to endorse or promote products derived from
176-
this software without specific prior written permission\\.)
207+
this software without specific prior written permission\\.)
177208
178209
THIS SOFTWARE IS PROVIDED BY .+ (``|''|")AS IS(''|") AND ANY EXPRESS OR
179210
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/DependencyLicensesTask.groovy

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ public class DependencyLicensesTask extends DefaultTask {
109109
mappings.put(from, to)
110110
}
111111

112+
public LinkedHashMap<String, String> getMappings() {
113+
return new LinkedHashMap<>(mappings)
114+
}
115+
112116
/**
113117
* Add a rule which will skip SHA checking for the given dependency name. This should be used for
114118
* locally build dependencies, which cause the sha to change constantly.
@@ -129,10 +133,6 @@ public class DependencyLicensesTask extends DefaultTask {
129133
throw new GradleException("Licences dir ${licensesDir} does not exist, but there are dependencies")
130134
}
131135

132-
133-
// order is the same for keys and values iteration since we use a linked hashmap
134-
List<String> mapped = new ArrayList<>(mappings.values())
135-
Pattern mappingsPattern = Pattern.compile('(' + mappings.keySet().join(')|(') + ')')
136136
Map<String, Integer> licenses = new HashMap<>()
137137
Map<String, Integer> notices = new HashMap<>()
138138
Set<File> shaFiles = new HashSet<File>()
@@ -162,16 +162,10 @@ public class DependencyLicensesTask extends DefaultTask {
162162
checkSha(dependency, jarName, shaFiles)
163163
}
164164

165-
logger.info("Checking license/notice for " + depName)
166-
Matcher match = mappingsPattern.matcher(depName)
167-
if (match.matches()) {
168-
int i = 0
169-
while (i < match.groupCount() && match.group(i + 1) == null) ++i;
170-
logger.info("Mapped dependency name ${depName} to ${mapped.get(i)} for license check")
171-
depName = mapped.get(i)
172-
}
173-
checkFile(depName, jarName, licenses, 'LICENSE')
174-
checkFile(depName, jarName, notices, 'NOTICE')
165+
final String dependencyName = getDependencyName(mappings, depName)
166+
logger.info("mapped dependency name ${depName} to ${dependencyName} for license/notice check")
167+
checkFile(dependencyName, jarName, licenses, 'LICENSE')
168+
checkFile(dependencyName, jarName, notices, 'NOTICE')
175169
}
176170

177171
licenses.each { license, count ->
@@ -189,6 +183,19 @@ public class DependencyLicensesTask extends DefaultTask {
189183
}
190184
}
191185

186+
public static String getDependencyName(final LinkedHashMap<String, String> mappings, final String dependencyName) {
187+
// order is the same for keys and values iteration since we use a linked hashmap
188+
List<String> mapped = new ArrayList<>(mappings.values())
189+
Pattern mappingsPattern = Pattern.compile('(' + mappings.keySet().join(')|(') + ')')
190+
Matcher match = mappingsPattern.matcher(dependencyName)
191+
if (match.matches()) {
192+
int i = 0
193+
while (i < match.groupCount() && match.group(i + 1) == null) ++i;
194+
return mapped.get(i)
195+
}
196+
return dependencyName
197+
}
198+
192199
private File getShaFile(String jarName) {
193200
return new File(licensesDir, jarName + SHA_EXTENSION)
194201
}

client/client-benchmark-noop-api-plugin/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ esplugin {
3131
tasks.remove(assemble)
3232
build.dependsOn.remove('assemble')
3333

34+
dependencyLicenses.enabled = false
35+
dependenciesInfo.enabled = false
36+
3437
compileJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked"
3538

3639
// no unit tests

distribution/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ Collection distributions = project('archives').subprojects + project('packages')
3232

3333
// Concatenates the dependencies CSV files into a single file
3434
task generateDependenciesReport(type: ConcatFilesTask) {
35+
project.rootProject.allprojects {
36+
afterEvaluate {
37+
if (it.tasks.findByName("dependenciesInfo")) dependsOn it.tasks.dependenciesInfo
38+
}
39+
}
3540
files = fileTree(dir: project.rootDir, include: '**/dependencies.csv' )
3641
headerLine = "name,version,url,license"
3742
target = new File(System.getProperty('csv')?: "${project.buildDir}/dependencies/es-dependencies.csv")

test/fixtures/example-fixture/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ test.enabled = false
2222
// Not published so no need to assemble
2323
tasks.remove(assemble)
2424
build.dependsOn.remove('assemble')
25+
26+
dependenciesInfo.enabled = false

x-pack/qa/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ gradle.projectsEvaluated {
2828
project.tasks.remove(assemble)
2929
project.build.dependsOn.remove('assemble')
3030
}
31+
Task dependenciesInfo = project.tasks.findByName('dependenciesInfo')
32+
if (dependenciesInfo) {
33+
project.precommit.dependsOn.remove('dependenciesInfo')
34+
}
3135
}
3236
}

x-pack/qa/sql/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies {
2222
test.enabled = false
2323

2424
dependencyLicenses.enabled = false
25+
dependenciesInfo.enabled = false
2526

2627
// the main files are actually test files, so use the appropriate forbidden api sigs
2728
forbiddenApisMain {

x-pack/test/feature-aware/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
forbiddenApisMain.enabled = true
1111

1212
dependencyLicenses.enabled = false
13+
dependenciesInfo.enabled = false
1314

1415
jarHell.enabled = false
1516

0 commit comments

Comments
 (0)