From 42ee6b9e74e097300b7532be65d09cace24855b0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Oct 2023 13:00:05 +0100 Subject: [PATCH] Use new permissions API with Gradle 8.3+ Closes gh-37878 --- .../plugin/ApplicationPluginAction.java | 37 +++++++++++++- .../tasks/bundling/BootArchiveSupport.java | 48 ++++++++++++++++++- .../tasks/bundling/BootZipCopyAction.java | 21 +++++++- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java index e9030e83dcc..bedaddb1bd4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java @@ -19,8 +19,10 @@ package org.springframework.boot.gradle.plugin; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; +import java.lang.reflect.Method; import java.util.concurrent.Callable; +import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -34,6 +36,7 @@ import org.gradle.api.plugins.JavaApplication; import org.gradle.api.tasks.TaskProvider; import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator; import org.gradle.jvm.application.tasks.CreateStartScripts; +import org.gradle.util.GradleVersion; import org.springframework.boot.gradle.tasks.run.BootRun; @@ -55,7 +58,7 @@ final class ApplicationPluginAction implements PluginApplicationAction { .register("bootStartScripts", CreateStartScripts.class, (task) -> configureCreateStartScripts(project, javaApplication, distribution, task)); CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts); - binCopySpec.setFileMode(0755); + configureFilePermissions(binCopySpec, 0755); distribution.getContents().with(binCopySpec); project.getTasks() .named(SpringBootPlugin.BOOT_RUN_TASK_NAME, BootRun.class) @@ -85,7 +88,7 @@ final class ApplicationPluginAction implements PluginApplicationAction { private CopySpec artifactFilesToLibCopySpec(Project project, Configuration configuration) { CopySpec copySpec = project.copySpec().into("lib").from(artifactFiles(configuration)); - copySpec.setFileMode(0644); + configureFilePermissions(copySpec, 0644); return copySpec; } @@ -113,4 +116,34 @@ final class ApplicationPluginAction implements PluginApplicationAction { } } + private void configureFilePermissions(CopySpec copySpec, int mode) { + if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) >= 0) { + try { + Method filePermissions = copySpec.getClass().getMethod("filePermissions", Action.class); + filePermissions.invoke(copySpec, new Action() { + + @Override + public void execute(Object filePermissions) { + String unixPermissions = Integer.toString(mode, 8); + try { + Method unix = filePermissions.getClass().getMethod("unix", String.class); + unix.invoke(filePermissions, unixPermissions); + } + catch (Exception ex) { + throw new GradleException("Failed to set file permissions to '" + unixPermissions + "'", + ex); + } + } + + }); + } + catch (Exception ex) { + throw new GradleException("Failed to set file permissions", ex); + } + } + else { + copySpec.setFileMode(mode); + } + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java index 87224d48208..57e9eac49f5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java @@ -26,7 +26,9 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.function.Function; +import java.util.function.Supplier; +import org.gradle.api.GradleException; import org.gradle.api.file.CopySpec; import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; @@ -36,11 +38,13 @@ import org.gradle.api.internal.file.copy.CopyActionProcessingStream; import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; import org.gradle.api.java.archives.Attributes; import org.gradle.api.java.archives.Manifest; +import org.gradle.api.provider.Property; import org.gradle.api.specs.Spec; import org.gradle.api.specs.Specs; import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.util.PatternSet; +import org.gradle.util.GradleVersion; /** * Support class for implementations of {@link BootArchive}. @@ -114,8 +118,8 @@ class BootArchiveSupport { File output = jar.getArchiveFile().get().getAsFile(); Manifest manifest = jar.getManifest(); boolean preserveFileTimestamps = jar.isPreserveFileTimestamps(); - Integer dirMode = jar.getDirMode(); - Integer fileMode = jar.getFileMode(); + Integer dirMode = getDirMode(jar); + Integer fileMode = getFileMode(jar); boolean includeDefaultLoader = isUsingDefaultLoader(jar); Spec requiresUnpack = this.requiresUnpack.getAsSpec(); Spec exclusions = this.exclusions.getAsExcludeSpec(); @@ -129,6 +133,46 @@ class BootArchiveSupport { return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action; } + private Integer getDirMode(CopySpec copySpec) { + return getMode(copySpec, "getDirPermissions", copySpec::getDirMode); + } + + private Integer getFileMode(CopySpec copySpec) { + return getMode(copySpec, "getFilePermissions", copySpec::getFileMode); + } + + @SuppressWarnings("unchecked") + private Integer getMode(CopySpec copySpec, String methodName, Supplier fallback) { + if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) >= 0) { + try { + Object filePermissions = ((Property) copySpec.getClass().getMethod(methodName).invoke(copySpec)) + .getOrNull(); + return getMode(filePermissions); + } + catch (Exception ex) { + throw new GradleException("Failed to get permissions", ex); + } + } + return fallback.get(); + } + + private Integer getMode(Object permissions) throws Exception { + if (permissions == null) { + return null; + } + String user = asIntegerString(permissions.getClass().getMethod("getUser").invoke(permissions)); + String group = asIntegerString(permissions.getClass().getMethod("getGroup").invoke(permissions)); + String other = asIntegerString(permissions.getClass().getMethod("getOther").invoke(permissions)); + return Integer.parseInt("0" + user + group + other, 8); + } + + private String asIntegerString(Object permissions) throws Exception { + boolean read = (boolean) permissions.getClass().getMethod("getRead").invoke(permissions); + boolean write = (boolean) permissions.getClass().getMethod("getWrite").invoke(permissions); + boolean execute = (boolean) permissions.getClass().getMethod("getExecute").invoke(permissions); + return Integer.toString(((read) ? 4 : 0) + ((write) ? 2 : 0) + ((execute) ? 1 : 0)); + } + private boolean isUsingDefaultLoader(Jar jar) { return DEFAULT_LAUNCHER_CLASSES.contains(jar.getManifest().getAttributes().get("Main-Class")); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java index 6945329e780..ae8d6f14a4e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java @@ -44,9 +44,11 @@ import org.gradle.api.internal.file.copy.CopyAction; import org.gradle.api.internal.file.copy.CopyActionProcessingStream; import org.gradle.api.java.archives.Attributes; import org.gradle.api.java.archives.Manifest; +import org.gradle.api.provider.Provider; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; +import org.gradle.util.GradleVersion; import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.FileUtils; @@ -412,7 +414,24 @@ class BootZipCopyAction implements CopyAction { private int getFileMode(FileCopyDetails details) { return (BootZipCopyAction.this.fileMode != null) ? BootZipCopyAction.this.fileMode - : UnixStat.FILE_FLAG | details.getMode(); + : UnixStat.FILE_FLAG | getPermissions(details); + } + + @SuppressWarnings("unchecked") + private int getPermissions(FileCopyDetails details) { + if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) >= 0) { + try { + Object permissions = ((Provider) details.getClass() + .getMethod("getImmutablePermissions") + .invoke(details)).get(); + return ((Provider) permissions.getClass().getMethod("toUnixNumeric").invoke(permissions)) + .get(); + } + catch (Exception ex) { + throw new GradleException("Failed to get permissions", ex); + } + } + return details.getMode(); } }