diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/FilePermissionsTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/FilePermissionsTask.groovy deleted file mode 100644 index d8da9a4207b..00000000000 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/FilePermissionsTask.groovy +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you 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 - * - * http://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. - */ -package org.elasticsearch.gradle.precommit - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.util.PatternSet -import org.gradle.api.tasks.util.PatternFilterable -import org.apache.tools.ant.taskdefs.condition.Os - -import java.nio.file.Files -import java.nio.file.attribute.PosixFilePermission -import java.nio.file.attribute.PosixFileAttributeView - -import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE -import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE - -/** - * Checks source files for correct file permissions. - */ -public class FilePermissionsTask extends DefaultTask { - - /** A pattern set of which files should be checked. */ - private PatternFilterable filesFilter = new PatternSet() - - @OutputFile - File outputMarker = new File(project.buildDir, 'markers/filePermissions') - - FilePermissionsTask() { - onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) } - description = "Checks java source files for correct file permissions" - // we always include all source files, and exclude what should not be checked - filesFilter.include('**') - // exclude sh files that might have the executable bit set - filesFilter.exclude('**/*.sh') - } - - /** Returns the files this task will check */ - @InputFiles - FileCollection files() { - List collections = new ArrayList<>() - for (SourceSet sourceSet : project.sourceSets) { - collections.add(sourceSet.allSource.matching(filesFilter)) - } - return project.files(collections.toArray()) - } - - @TaskAction - void checkInvalidPermissions() { - List failures = new ArrayList<>() - for (File f : files()) { - PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(f.toPath(), PosixFileAttributeView.class) - Set permissions = fileAttributeView.readAttributes().permissions() - if (permissions.contains(OTHERS_EXECUTE) || permissions.contains(OWNER_EXECUTE) || - permissions.contains(GROUP_EXECUTE)) { - failures.add("Source file is executable: " + f) - } - } - if (failures.isEmpty() == false) { - throw new GradleException('Found invalid file permissions:\n' + failures.join('\n')) - } - outputMarker.setText('done', 'UTF-8') - } - -} diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/FilePermissionsTask.java b/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/FilePermissionsTask.java new file mode 100644 index 00000000000..100c3a22700 --- /dev/null +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/FilePermissionsTask.java @@ -0,0 +1,114 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you 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 + * + * http://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. + */ +package org.elasticsearch.gradle.precommit; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.tools.ant.taskdefs.condition.Os; +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTree; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.SkipWhenEmpty; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.StopExecutionException; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.util.PatternFilterable; +import org.gradle.api.tasks.util.PatternSet; + +/** + * Checks source files for correct file permissions. + */ +public class FilePermissionsTask extends DefaultTask { + + /** + * A pattern set of which files should be checked. + */ + private final PatternFilterable filesFilter = new PatternSet() + // we always include all source files, and exclude what should not be checked + .include("**") + // exclude sh files that might have the executable bit set + .exclude("**/*.sh"); + + private File outputMarker = new File(getProject().getBuildDir(), "markers/filePermissions"); + + public FilePermissionsTask() { + setDescription("Checks java source files for correct file permissions"); + } + + private static boolean isExecutableFile(File file) { + try { + Set permissions = Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class) + .readAttributes() + .permissions(); + return permissions.contains(PosixFilePermission.OTHERS_EXECUTE) + || permissions.contains(PosixFilePermission.OWNER_EXECUTE) + || permissions.contains(PosixFilePermission.GROUP_EXECUTE); + } catch (IOException e) { + throw new IllegalStateException("unable to read the file " + file + " attributes", e); + } + } + + /** + * Returns the files this task will check + */ + @InputFiles + @SkipWhenEmpty + public FileCollection getFiles() { + SourceSetContainer sourceSets = getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets(); + return sourceSets.stream() + .map(sourceSet -> sourceSet.getAllSource().matching(filesFilter)) + .reduce(FileTree::plus) + .orElse(getProject().files().getAsFileTree()); + } + + @TaskAction + public void checkInvalidPermissions() throws IOException { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + throw new StopExecutionException(); + } + List failures = getFiles().getFiles().stream() + .filter(FilePermissionsTask::isExecutableFile) + .map(file -> "Source file is executable: " + file) + .collect(Collectors.toList()); + + if (!failures.isEmpty()) { + throw new GradleException("Found invalid file permissions:\n" + String.join("\n", failures)); + } + + outputMarker.getParentFile().mkdirs(); + Files.write(outputMarker.toPath(), "done".getBytes("UTF-8")); + } + + @OutputFile + public File getOutputMarker() { + return outputMarker; + } + +} diff --git a/buildSrc/src/test/java/org/elasticsearch/gradle/precommit/FilePermissionsTaskTests.java b/buildSrc/src/test/java/org/elasticsearch/gradle/precommit/FilePermissionsTaskTests.java new file mode 100644 index 00000000000..4d73a548d8e --- /dev/null +++ b/buildSrc/src/test/java/org/elasticsearch/gradle/precommit/FilePermissionsTaskTests.java @@ -0,0 +1,100 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you 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 + * + * http://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. + */ +package org.elasticsearch.gradle.precommit; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.List; + +import org.elasticsearch.gradle.test.GradleUnitTestCase; +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +public class FilePermissionsTaskTests extends GradleUnitTestCase { + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + public void testCheckPermissionsWhenAnExecutableFileExists() throws Exception { + Project project = createProject(); + + FilePermissionsTask filePermissionsTask = createTask(project); + + File file = new File(project.getProjectDir(), "src/main/java/Code.java"); + file.getParentFile().mkdirs(); + file.createNewFile(); + file.setExecutable(true); + + try { + filePermissionsTask.checkInvalidPermissions(); + Assert.fail("the check should have failed because of the executable file permission"); + } catch (GradleException e) { + assertTrue(e.getMessage().startsWith("Found invalid file permissions")); + } + file.delete(); + } + + + public void testCheckPermissionsWhenNoFileExists() throws Exception { + Project project = createProject(); + + FilePermissionsTask filePermissionsTask = createTask(project); + + filePermissionsTask.checkInvalidPermissions(); + + File outputMarker = new File(project.getBuildDir(), "markers/filePermissions"); + List result = Files.readAllLines(outputMarker.toPath(), Charset.forName("UTF-8")); + assertEquals("done", result.get(0)); + } + + public void testCheckPermissionsWhenNoExecutableFileExists() throws Exception { + Project project = createProject(); + + FilePermissionsTask filePermissionsTask = createTask(project); + + File file = new File(project.getProjectDir(), "src/main/java/Code.java"); + file.getParentFile().mkdirs(); + file.createNewFile(); + + filePermissionsTask.checkInvalidPermissions(); + + File outputMarker = new File(project.getBuildDir(), "markers/filePermissions"); + List result = Files.readAllLines(outputMarker.toPath(), Charset.forName("UTF-8")); + assertEquals("done", result.get(0)); + + file.delete(); + + } + + private Project createProject() throws IOException { + Project project = ProjectBuilder.builder().withProjectDir(temporaryFolder.newFolder()).build(); + project.getPlugins().apply(JavaPlugin.class); + return project; + } + + private FilePermissionsTask createTask(Project project) { + return project.getTasks().create("filePermissionsTask", FilePermissionsTask.class); + } +}