diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/precommit/LicenseHeadersTask.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/precommit/LicenseHeadersTask.java index 4f07187f7fc38..45b6b1d142963 100644 --- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/precommit/LicenseHeadersTask.java +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/precommit/LicenseHeadersTask.java @@ -36,7 +36,7 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; - +import org.gradle.api.model.ObjectFactory; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.BufferedWriter; @@ -51,6 +51,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import javax.inject.Inject; +import java.io.Serializable; /** * Checks files for license headers.. @@ -95,10 +97,6 @@ public List getExcludes() { return excludes; } - public Map getAdditionalLicenses() { - return additionalLicenses; - } - public void setExcludes(List excludes) { this.excludes = excludes; } @@ -106,6 +104,11 @@ public void setExcludes(List excludes) { @OutputFile private File reportFile = new File(getProject().getBuildDir(), "reports/licenseHeaders/rat.xml"); + private static List conventionalLicenses = Arrays.asList( + // Dual SSPLv1 and Elastic + new License("DUAL", "SSPL+Elastic License", "the Elastic License 2.0 or the Server") + ); + /** * Allowed license families for this project. */ @@ -118,13 +121,17 @@ public void setExcludes(List excludes) { */ @Input private List excludes = new ArrayList(); + + private ListProperty additionalLicenses; + /** * Additional license families that may be found. The key is the license category name (5 characters), * followed by the family name and the value list of patterns to search for. */ @Input - protected Map additionalLicenses = new HashMap(); - + public ListProperty getAdditionalLicenses() { + return additionalLicenses; + } /** * Add a new license type. *

@@ -139,7 +146,12 @@ public void additionalLicense(final String categoryName, String familyName, Stri throw new IllegalArgumentException("License category name must be exactly 5 characters, got " + categoryName); } - additionalLicenses.put(categoryName + familyName, pattern); + additionalLicenses.add(new License(categoryName, familyName, pattern)); + } + + @Inject + public LicenseHeadersTask(ObjectFactory objectFactory) { + additionalLicenses = objectFactory.listProperty(License.class).convention(conventionalLicenses); } @TaskAction @@ -160,14 +172,10 @@ public void runRat() { matchers.add(subStringMatcher("GEN ", "Generated", "ANTLR GENERATED CODE")); // Vendored Code matchers.add(subStringMatcher("VEN ", "Vendored", "@notice")); - // Dual SSPLv1 and Elastic - matchers.add(subStringMatcher("DUAL", "SSPL+Elastic License", "the Elastic License 2.0 or the Server")); - for (Map.Entry additional : additionalLicenses.entrySet()) { - String category = additional.getKey().substring(0, 5); - String family = additional.getKey().substring(5); - matchers.add(subStringMatcher(category, family, additional.getValue())); - } + additionalLicenses.get().forEach(l -> + matchers.add(subStringMatcher(l.licenseFamilyCategory, l.licenseFamilyName, l.substringPattern)) + ); reportConfiguration.setHeaderMatcher(new HeaderMatcherMultiplexer(matchers.toArray(IHeaderMatcher[]::new))); reportConfiguration.setApprovedLicenseNames(approvedLicenses.stream().map(license -> { @@ -190,7 +198,6 @@ private IHeaderMatcher subStringMatcher(String licenseFamilyCategory, String lic SubstringLicenseMatcher substringLicenseMatcher = new SubstringLicenseMatcher(); substringLicenseMatcher.setLicenseFamilyCategory(licenseFamilyCategory); substringLicenseMatcher.setLicenseFamilyName(licenseFamilyName); - SubstringLicenseMatcher.Pattern pattern = new SubstringLicenseMatcher.Pattern(); pattern.setSubstring(substringPattern); substringLicenseMatcher.addConfiguredPattern(pattern); @@ -249,4 +256,16 @@ private static List elementList(NodeList resourcesNodes) { } return nodeList; } + + static class License implements Serializable { + private String licenseFamilyCategory; + private String licenseFamilyName; + private String substringPattern; + + public License(String licenseFamilyCategory, String licenseFamilyName, String substringPattern) { + this.licenseFamilyCategory = licenseFamilyCategory; + this.licenseFamilyName = licenseFamilyName; + this.substringPattern = substringPattern; + } + } } diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/precommit/LicenseHeadersPrecommitPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/precommit/LicenseHeadersPrecommitPluginFuncTest.groovy index 0481d3315d010..67d2b96fb7b8f 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/precommit/LicenseHeadersPrecommitPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/precommit/LicenseHeadersPrecommitPluginFuncTest.groovy @@ -59,6 +59,45 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest { result.task(":licenseHeaders").outcome == TaskOutcome.SUCCESS } + def "supports sspl by convention"() { + given: + buildFile << """ + plugins { + id 'java' + id 'elasticsearch.internal-licenseheaders' + } + """ + dualLicensedFile() + + when: + def result = gradleRunner("licenseHeaders").build() + + then: + result.task(":licenseHeaders").outcome == TaskOutcome.SUCCESS + } + + def "sspl default additional license can be overridden"() { + given: + buildFile << """ + plugins { + id 'java' + id 'elasticsearch.internal-licenseheaders' + } + + tasks.named("licenseHeaders").configure { + additionalLicense 'ELAST', 'Elastic License 2.0', '2.0; you may not use this file except in compliance with the Elastic License' + } + """ + elasticLicensed() + dualLicensedFile() + + when: + def result = gradleRunner("licenseHeaders").buildAndFail() + + then: + result.task(":licenseHeaders").outcome == TaskOutcome.FAILED + } + private File unapprovedSourceFile(String filePath = "src/main/java/org/acme/UnapprovedLicensed.java") { File sourceFile = file(filePath); sourceFile << """ @@ -115,6 +154,21 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest { """ } + private File elasticLicensed() { + file("src/main/java/org/acme/ElasticLicensed.java") << """ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + + package org.acme; + public class ElasticLicensed { + } + """ + } + private String packageString(File sourceFile) { String normalizedPath = normalized(sourceFile.getPath()) (normalizedPath.substring(normalizedPath.indexOf("src/main/java")) - "src/main/java/" - ("/" + sourceFile.getName())).replaceAll("/", ".")