|
@@ -11,15 +11,15 @@ import de.thetaphi.forbiddenapis.cli.CliMain;
|
|
|
|
|
|
import org.apache.commons.io.output.NullOutputStream;
|
|
|
import org.elasticsearch.gradle.OS;
|
|
|
-import org.elasticsearch.gradle.dependencies.CompileOnlyResolvePlugin;
|
|
|
import org.gradle.api.DefaultTask;
|
|
|
import org.gradle.api.JavaVersion;
|
|
|
-import org.gradle.api.artifacts.Configuration;
|
|
|
-import org.gradle.api.artifacts.Dependency;
|
|
|
+import org.gradle.api.file.ArchiveOperations;
|
|
|
import org.gradle.api.file.FileCollection;
|
|
|
+import org.gradle.api.file.FileSystemOperations;
|
|
|
import org.gradle.api.file.FileTree;
|
|
|
+import org.gradle.api.file.ProjectLayout;
|
|
|
+import org.gradle.api.model.ObjectFactory;
|
|
|
import org.gradle.api.provider.Property;
|
|
|
-import org.gradle.api.specs.Spec;
|
|
|
import org.gradle.api.tasks.CacheableTask;
|
|
|
import org.gradle.api.tasks.Classpath;
|
|
|
import org.gradle.api.tasks.CompileClasspath;
|
|
@@ -33,6 +33,7 @@ import org.gradle.api.tasks.PathSensitive;
|
|
|
import org.gradle.api.tasks.PathSensitivity;
|
|
|
import org.gradle.api.tasks.SkipWhenEmpty;
|
|
|
import org.gradle.api.tasks.TaskAction;
|
|
|
+import org.gradle.process.ExecOperations;
|
|
|
import org.gradle.process.ExecResult;
|
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
@@ -51,6 +52,8 @@ import java.util.stream.Collectors;
|
|
|
import java.util.stream.IntStream;
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
+import javax.inject.Inject;
|
|
|
+
|
|
|
@CacheableTask
|
|
|
public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
@@ -79,7 +82,36 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
private FileCollection jdkJarHellClasspath;
|
|
|
|
|
|
- private final Property<JavaVersion> targetCompatibility = getProject().getObjects().property(JavaVersion.class);
|
|
|
+ private final Property<JavaVersion> targetCompatibility;
|
|
|
+
|
|
|
+ private final ArchiveOperations archiveOperations;
|
|
|
+
|
|
|
+ private final ExecOperations execOperations;
|
|
|
+
|
|
|
+ private final FileSystemOperations fileSystemOperations;
|
|
|
+
|
|
|
+ private final ProjectLayout projectLayout;
|
|
|
+
|
|
|
+ private FileCollection classpath;
|
|
|
+
|
|
|
+ private FileCollection jarsToScan;
|
|
|
+
|
|
|
+ private FileCollection forbiddenApisClasspath;
|
|
|
+
|
|
|
+ @Inject
|
|
|
+ public ThirdPartyAuditTask(
|
|
|
+ ArchiveOperations archiveOperations,
|
|
|
+ ExecOperations execOperations,
|
|
|
+ FileSystemOperations fileSystemOperations,
|
|
|
+ ProjectLayout projectLayout,
|
|
|
+ ObjectFactory objectFactory
|
|
|
+ ) {
|
|
|
+ this.archiveOperations = archiveOperations;
|
|
|
+ this.execOperations = execOperations;
|
|
|
+ this.fileSystemOperations = fileSystemOperations;
|
|
|
+ this.projectLayout = projectLayout;
|
|
|
+ this.targetCompatibility = objectFactory.property(JavaVersion.class);
|
|
|
+ }
|
|
|
|
|
|
@Input
|
|
|
public Property<JavaVersion> getTargetCompatibility() {
|
|
@@ -88,8 +120,12 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
@InputFiles
|
|
|
@PathSensitive(PathSensitivity.NAME_ONLY)
|
|
|
- public Configuration getForbiddenAPIsConfiguration() {
|
|
|
- return getProject().getConfigurations().getByName("forbiddenApisCliJar");
|
|
|
+ public FileCollection getForbiddenAPIsClasspath() {
|
|
|
+ return forbiddenApisClasspath;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setForbiddenAPIsClasspath(FileCollection forbiddenApisClasspath) {
|
|
|
+ this.forbiddenApisClasspath = forbiddenApisClasspath;
|
|
|
}
|
|
|
|
|
|
@InputFile
|
|
@@ -114,12 +150,12 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
@Internal
|
|
|
public File getJarExpandDir() {
|
|
|
- return new File(new File(getProject().getBuildDir(), "precommit/thirdPartyAudit"), getName());
|
|
|
+ return projectLayout.getBuildDirectory().dir("precommit/thirdPartyAudit").get().dir(getName()).getAsFile();
|
|
|
}
|
|
|
|
|
|
@OutputFile
|
|
|
public File getSuccessMarker() {
|
|
|
- return new File(getProject().getBuildDir(), "markers/" + getName());
|
|
|
+ return projectLayout.getBuildDirectory().dir("precommit/thirdPartyAudit").get().dir("markers/").file(getName()).getAsFile();
|
|
|
}
|
|
|
|
|
|
// We use compile classpath normalization here because class implementation changes are irrelevant for the purposes of jdk jar hell.
|
|
@@ -171,31 +207,15 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
@Classpath
|
|
|
@SkipWhenEmpty
|
|
|
- public Set<File> getJarsToScan() {
|
|
|
- // These are SelfResolvingDependency, and some of them backed by file collections, like the Gradle API files,
|
|
|
- // or dependencies added as `files(...)`, we can't be sure if those are third party or not.
|
|
|
- // err on the side of scanning these to make sure we don't miss anything
|
|
|
- Spec<Dependency> reallyThirdParty = dep -> dep.getGroup() != null && dep.getGroup().startsWith("org.elasticsearch") == false;
|
|
|
- Set<File> jars = getRuntimeConfiguration().getResolvedConfiguration().getFiles(reallyThirdParty);
|
|
|
- Set<File> compileOnlyConfiguration = getProject().getConfigurations()
|
|
|
- .getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME)
|
|
|
- .getResolvedConfiguration()
|
|
|
- .getFiles(reallyThirdParty);
|
|
|
- // don't scan provided dependencies that we already scanned, e.x. don't scan cores dependencies for every plugin
|
|
|
- if (compileOnlyConfiguration != null) {
|
|
|
- jars.removeAll(compileOnlyConfiguration);
|
|
|
- }
|
|
|
- return jars;
|
|
|
+ public FileCollection getJarsToScan() {
|
|
|
+ return jarsToScan;
|
|
|
}
|
|
|
|
|
|
@TaskAction
|
|
|
public void runThirdPartyAudit() throws IOException {
|
|
|
- Set<File> jars = getJarsToScan();
|
|
|
-
|
|
|
- extractJars(jars);
|
|
|
-
|
|
|
+ Set<File> jars = jarsToScan.getFiles();
|
|
|
+ extractJars(jars, getJarExpandDir());
|
|
|
final String forbiddenApisOutput = runForbiddenAPIsCli();
|
|
|
-
|
|
|
final Set<String> missingClasses = new TreeSet<>();
|
|
|
Matcher missingMatcher = MISSING_CLASS_PATTERN.matcher(forbiddenApisOutput);
|
|
|
while (missingMatcher.find()) {
|
|
@@ -248,7 +268,11 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
assertNoJarHell(jdkJarHellClasses);
|
|
|
|
|
|
- // Mark successful third party audit check
|
|
|
+ success();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Mark successful third party audit check
|
|
|
+ private void success() throws IOException {
|
|
|
getSuccessMarker().getParentFile().mkdirs();
|
|
|
Files.write(getSuccessMarker().toPath(), new byte[] {});
|
|
|
}
|
|
@@ -261,14 +285,18 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
throw new IllegalArgumentException("Audit of third party dependencies is not configured correctly");
|
|
|
}
|
|
|
|
|
|
- private void extractJars(Set<File> jars) {
|
|
|
- File jarExpandDir = getJarExpandDir();
|
|
|
+ /**
|
|
|
+ * Ideally we would do unpacking already via artifact transform and keep unpacked jars across builds.
|
|
|
+ * At the moment transform target folder is not configurable and forbidden CLI only takes one common
|
|
|
+ * directory as input which makes it incompatible with gradle artifact transforms as we use them today.
|
|
|
+ * */
|
|
|
+ private void extractJars(Set<File> jars, File jarExpandDir) {
|
|
|
// We need to clean up to make sure old dependencies don't linger
|
|
|
- getProject().delete(jarExpandDir);
|
|
|
+ fileSystemOperations.delete(d -> d.delete(jarExpandDir));
|
|
|
|
|
|
jars.forEach(jar -> {
|
|
|
- FileTree jarFiles = getProject().zipTree(jar);
|
|
|
- getProject().copy(spec -> {
|
|
|
+ FileTree jarFiles = archiveOperations.zipTree(jar);
|
|
|
+ fileSystemOperations.copy(spec -> {
|
|
|
spec.from(jarFiles);
|
|
|
spec.into(jarExpandDir);
|
|
|
// exclude classes from multi release jars
|
|
@@ -287,8 +315,8 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
IntStream.rangeClosed(
|
|
|
Integer.parseInt(JavaVersion.VERSION_1_9.getMajorVersion()),
|
|
|
Integer.parseInt(targetCompatibility.get().getMajorVersion())
|
|
|
- ).forEach(majorVersion -> getProject().copy(spec -> {
|
|
|
- spec.from(getProject().zipTree(jar));
|
|
|
+ ).forEach(majorVersion -> fileSystemOperations.copy(spec -> {
|
|
|
+ spec.from(archiveOperations.zipTree(jar));
|
|
|
spec.into(jarExpandDir);
|
|
|
String metaInfPrefix = "META-INF/versions/" + majorVersion;
|
|
|
spec.include(metaInfPrefix + "/**");
|
|
@@ -325,15 +353,11 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
private String runForbiddenAPIsCli() throws IOException {
|
|
|
ByteArrayOutputStream errorOut = new ByteArrayOutputStream();
|
|
|
- ExecResult result = getProject().javaexec(spec -> {
|
|
|
+ ExecResult result = execOperations.javaexec(spec -> {
|
|
|
if (javaHome != null) {
|
|
|
spec.setExecutable(javaHome + "/bin/java");
|
|
|
}
|
|
|
- spec.classpath(
|
|
|
- getForbiddenAPIsConfiguration(),
|
|
|
- getRuntimeConfiguration(),
|
|
|
- getProject().getConfigurations().getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME)
|
|
|
- );
|
|
|
+ spec.classpath(forbiddenApisClasspath, classpath);
|
|
|
spec.jvmArgs("-Xmx1g");
|
|
|
spec.getMainClass().set("de.thetaphi.forbiddenapis.cli.CliMain");
|
|
|
spec.args("-f", getSignatureFile().getAbsolutePath(), "-d", getJarExpandDir(), "--allowmissingclasses");
|
|
@@ -358,12 +382,8 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
|
|
|
private Set<String> runJdkJarHellCheck() throws IOException {
|
|
|
ByteArrayOutputStream standardOut = new ByteArrayOutputStream();
|
|
|
- ExecResult execResult = getProject().javaexec(spec -> {
|
|
|
- spec.classpath(
|
|
|
- jdkJarHellClasspath,
|
|
|
- getRuntimeConfiguration(),
|
|
|
- getProject().getConfigurations().getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME)
|
|
|
- );
|
|
|
+ ExecResult execResult = execOperations.javaexec(spec -> {
|
|
|
+ spec.classpath(jdkJarHellClasspath, classpath);
|
|
|
|
|
|
spec.getMainClass().set(JDK_JAR_HELL_MAIN_CLASS);
|
|
|
spec.args(getJarExpandDir());
|
|
@@ -383,11 +403,11 @@ public class ThirdPartyAuditTask extends DefaultTask {
|
|
|
return new TreeSet<>(Arrays.asList(jdkJarHellCheckList.split("\\r?\\n")));
|
|
|
}
|
|
|
|
|
|
- private Configuration getRuntimeConfiguration() {
|
|
|
- Configuration runtime = getProject().getConfigurations().findByName("runtimeClasspath");
|
|
|
- if (runtime == null) {
|
|
|
- return getProject().getConfigurations().getByName("testCompileClasspath");
|
|
|
- }
|
|
|
- return runtime;
|
|
|
+ public void setClasspath(FileCollection classpath) {
|
|
|
+ this.classpath = classpath;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setJarsToScan(FileCollection jarsToScan) {
|
|
|
+ this.jarsToScan = jarsToScan;
|
|
|
}
|
|
|
}
|