diff --git a/jib-gradle-plugin/CHANGELOG.md b/jib-gradle-plugin/CHANGELOG.md index 13587a5ad5..e17c77365b 100644 --- a/jib-gradle-plugin/CHANGELOG.md +++ b/jib-gradle-plugin/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. ### Added - Additionally reads credentials from `~/.docker/.dockerconfigjson` and legacy Docker config (`~/.docker/.dockercfg`). Also searches for `$HOME/.docker/*` (in addition to current `System.get("user.home")/.docker/*`). This may help retrieve credentials, for example, on Kubernetes. ([#2260](https://github.com/GoogleContainerTools/jib/issues/2260)) +- New skaffold configuration options that modify how jib's build config is presented to skaffold ([#2292](https://github.com/GoogleContainerTools/jib/pull/2292)): + - `jib.skaffold.watch.buildIncludes`: a list of build files to watch + - `jib.skaffold.watch.includes`: a list of project files to watch + - `jib.skaffold.watch.excludes`: a list of files to exclude from watching + - `jib.skaffold.sync.excludes`: a list of files to exclude from sync'ing ### Changed diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/JibExtension.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/JibExtension.java index 54f03b5d51..149e1deb4e 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/JibExtension.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/JibExtension.java @@ -16,6 +16,7 @@ package com.google.cloud.tools.jib.gradle; +import com.google.cloud.tools.jib.gradle.skaffold.SkaffoldParameters; import com.google.cloud.tools.jib.plugins.common.PropertyNames; import org.gradle.api.Action; import org.gradle.api.Project; @@ -77,6 +78,7 @@ public class JibExtension { private final ExtraDirectoriesParameters extraDirectories; private final DockerClientParameters dockerClient; private final OutputPathsParameters outputPaths; + private final SkaffoldParameters skaffold; private final Property allowInsecureRegistries; private final Property containerizingMode; @@ -94,6 +96,7 @@ public JibExtension(Project project) { extraDirectories = objectFactory.newInstance(ExtraDirectoriesParameters.class, project); dockerClient = objectFactory.newInstance(DockerClientParameters.class); outputPaths = objectFactory.newInstance(OutputPathsParameters.class, project); + skaffold = objectFactory.newInstance(SkaffoldParameters.class, project); allowInsecureRegistries = objectFactory.property(Boolean.class); containerizingMode = objectFactory.property(String.class); @@ -127,6 +130,10 @@ public void outputPaths(Action action) { action.execute(outputPaths); } + public void skaffold(Action action) { + action.execute(skaffold); + } + public void setAllowInsecureRegistries(boolean allowInsecureRegistries) { this.allowInsecureRegistries.set(allowInsecureRegistries); } @@ -171,6 +178,12 @@ public OutputPathsParameters getOutputPaths() { return outputPaths; } + @Nested + @Optional + public SkaffoldParameters getSkaffold() { + return skaffold; + } + @Input boolean getAllowInsecureRegistries() { if (System.getProperty(PropertyNames.ALLOW_INSECURE_REGISTRIES) != null) { diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2.java index c4cb660a6f..69c5b99a28 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2.java @@ -109,6 +109,14 @@ public void listFiles() throws IOException { } } + // Configure other files from config + SkaffoldWatchParameters watch = jibExtension.getSkaffold().getWatch(); + watch.getBuildIncludes().forEach(skaffoldFilesOutput::addBuild); + watch.getIncludes().forEach(skaffoldFilesOutput::addInput); + // we don't do any special pre-processing for ignore (input and ignore can overlap with exact + // matches) + watch.getExcludes().forEach(skaffoldFilesOutput::addIgnore); + // Print files System.out.println(); System.out.println("BEGIN JIB JSON"); diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldParameters.java new file mode 100644 index 0000000000..58c6124a8c --- /dev/null +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.google.cloud.tools.jib.gradle.skaffold; + +import com.google.common.base.Preconditions; +import javax.inject.Inject; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.tasks.Nested; + +/** Skaffold specific JibExtension parameters. */ +public class SkaffoldParameters { + + private final SkaffoldWatchParameters watch; + private final SkaffoldSyncParameters sync; + + @Inject + public SkaffoldParameters(Project project) { + ObjectFactory objectFactory = project.getObjects(); + + watch = objectFactory.newInstance(SkaffoldWatchParameters.class, project); + sync = objectFactory.newInstance(SkaffoldSyncParameters.class, project); + + Preconditions.checkNotNull(watch); + } + + public void watch(Action action) { + action.execute(watch); + } + + public void sync(Action action) { + action.execute(sync); + } + + @Nested + public SkaffoldWatchParameters getWatch() { + return watch; + } + + @Nested + public SkaffoldSyncParameters getSync() { + return sync; + } +} diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldSyncParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldSyncParameters.java new file mode 100644 index 0000000000..d608f30317 --- /dev/null +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldSyncParameters.java @@ -0,0 +1,65 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.google.cloud.tools.jib.gradle.skaffold; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.gradle.api.Project; +import org.gradle.api.tasks.Internal; + +/** Skaffold specific JibExtension parameters for configuring files to sync. */ +public class SkaffoldSyncParameters { + private final Project project; + + private Set excludes = Collections.emptySet(); + + @Inject + public SkaffoldSyncParameters(Project project) { + this.project = project; + } + + /** + * Get the excludes directive for sync functionality in skaffold. + * + * @return a set of absolute paths + */ + @Internal + public Set getExcludes() { + return excludes; + } + + /** + * Sets excludes. {@code excludes} can be any suitable object describing file paths convertible by + * {@link Project#files} (such as {@link File}, {@code List}, or {@code List}). + * + * @param paths paths to set on excludes + */ + public void setExcludes(Object paths) { + this.excludes = + project + .files(paths) + .getFiles() + .stream() + .map(File::toPath) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + } +} diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldWatchParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldWatchParameters.java new file mode 100644 index 0000000000..a6537cf840 --- /dev/null +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SkaffoldWatchParameters.java @@ -0,0 +1,124 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.google.cloud.tools.jib.gradle.skaffold; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.gradle.api.Project; +import org.gradle.api.tasks.Internal; + +/** Skaffold specific JibExtension parameters for configuring files to watch. */ +public class SkaffoldWatchParameters { + + private final Project project; + + private Set buildIncludes = Collections.emptySet(); + private Set includes = Collections.emptySet(); + private Set excludes = Collections.emptySet(); + + @Inject + public SkaffoldWatchParameters(Project project) { + this.project = project; + } + + /** + * A set of absolute paths to include with skaffold watching. + * + * @return a set of absolute paths + */ + @Internal + public Set getBuildIncludes() { + return buildIncludes; + } + + /** + * Sets includes. {@code includes} can be any suitable object describing file paths convertible by + * {@link Project#files} (such as {@link File}, {@code List}, or {@code List}). + * + * @param paths paths to set on includes + */ + public void setBuildIncludes(Object paths) { + this.buildIncludes = + project + .files(paths) + .getFiles() + .stream() + .map(File::toPath) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + } + + /** + * A set of absolute paths to include with skaffold watching. + * + * @return a set of absolute paths + */ + @Internal + public Set getIncludes() { + return includes; + } + + /** + * Sets includes. {@code includes} can be any suitable object describing file paths convertible by + * {@link Project#files} (such as {@link File}, {@code List}, or {@code List}). + * + * @param paths paths to set on includes + */ + public void setIncludes(Object paths) { + this.includes = + project + .files(paths) + .getFiles() + .stream() + .map(File::toPath) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + } + + /** + * A set of absolute paths to exclude from skaffold watching. + * + * @return a set of absolute paths + */ + @Internal + public Set getExcludes() { + // Gradle warns about @Input annotations on File objects, so we have to expose a getter for a + // String to make them go away. + return excludes; + } + + /** + * Sets excludes. {@code excludes} can be any suitable object describing file paths convertible by + * {@link Project#files} (such as {@link File}, {@code List}, or {@code List}). + * + * @param paths paths to set on excludes + */ + public void setExcludes(Object paths) { + this.excludes = + project + .files(paths) + .getFiles() + .stream() + .map(File::toPath) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()); + } +} diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTask.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTask.java index a2f9790358..bc04cd6861 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTask.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTask.java @@ -78,7 +78,10 @@ public void listFilesAndTargets() { try { String syncMapJson = - PluginConfigurationProcessor.getSkaffoldSyncMap(configuration, projectProperties); + PluginConfigurationProcessor.getSkaffoldSyncMap( + configuration, + projectProperties, + jibExtension.getSkaffold().getSync().getExcludes()); System.out.println(); System.out.println("BEGIN JIB JSON: SYNCMAP/1"); diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java index 4720f24496..3e67035043 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import java.io.File; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; @@ -240,6 +242,35 @@ public void testOutputFiles() { testJibExtension.getOutputPaths().getTarPath()); } + @Test + public void testSkaffold() { + testJibExtension.skaffold( + skaffold -> { + skaffold.sync(sync -> sync.setExcludes(fakeProject.files("sync1", "sync2"))); + skaffold.watch( + watch -> { + watch.setBuildIncludes(ImmutableList.of("watch1", "watch2")); + watch.setIncludes("watch3"); + watch.setExcludes(ImmutableList.of(new File("watch4"))); + }); + }); + Path root = fakeProject.getRootDir().toPath(); + Assert.assertEquals( + ImmutableSet.of( + root.resolve("sync1").toAbsolutePath(), root.resolve("sync2").toAbsolutePath()), + testJibExtension.getSkaffold().getSync().getExcludes()); + Assert.assertEquals( + ImmutableSet.of( + root.resolve("watch1").toAbsolutePath(), root.resolve("watch2").toAbsolutePath()), + testJibExtension.getSkaffold().getWatch().getBuildIncludes()); + Assert.assertEquals( + ImmutableSet.of(root.resolve("watch3").toAbsolutePath()), + testJibExtension.getSkaffold().getWatch().getIncludes()); + Assert.assertEquals( + ImmutableSet.of(root.resolve("watch4").toAbsolutePath()), + testJibExtension.getSkaffold().getWatch().getExcludes()); + } + @Test public void testProperties() { System.setProperty("jib.from.image", "fromImage"); diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2Test.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2Test.java index 4595439742..9ecf0eb801 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2Test.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/FilesTaskV2Test.java @@ -38,6 +38,9 @@ public class FilesTaskV2Test { @ClassRule public static final TestProject simpleTestProject = new TestProject("simple"); + @ClassRule + public static final TestProject skaffoldTestProject = new TestProject("skaffold-config"); + @ClassRule public static final TestProject multiTestProject = new TestProject("multi-service"); @ClassRule @@ -163,4 +166,23 @@ public void testFilesTask_platformProject() throws IOException { ImmutableList.of(serviceRoot.resolve("src/main/java")), result.getInputs()); Assert.assertEquals(result.getIgnore().size(), 0); } + + @Test + public void testFilesTast_withConfigModifiers() throws IOException { + Path projectRoot = skaffoldTestProject.getProjectRoot(); + SkaffoldFilesOutput result = + new SkaffoldFilesOutput(verifyTaskSuccess(skaffoldTestProject, null)); + assertPathListsAreEqual( + ImmutableList.of(projectRoot.resolve("build.gradle"), projectRoot.resolve("script.gradle")), + result.getBuild()); + assertPathListsAreEqual( + ImmutableList.of( + projectRoot.resolve("src/main/resources"), + projectRoot.resolve("src/main/java"), + projectRoot.resolve("src/main/jib"), + projectRoot.resolve("other/file.txt")), + result.getInputs()); + assertPathListsAreEqual( + ImmutableList.of(projectRoot.resolve("src/main/jib/bar")), result.getIgnore()); + } } diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTaskTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTaskTest.java index e0c683c290..07eb935217 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTaskTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/skaffold/SyncMapTaskTest.java @@ -40,6 +40,7 @@ public class SyncMapTaskTest { @ClassRule public static final TestProject simpleTestProject = new TestProject("simple"); + @ClassRule public static final TestProject skaffoldProject = new TestProject("skaffold-config"); @ClassRule public static final TestProject multiTestProject = new TestProject("multi-service"); @ClassRule public static final TestProject warProject = new TestProject("war_servlet25"); @@ -150,7 +151,33 @@ public void testSyncMapTask_multiProjectOutput() throws IOException { } @Test - public void testSyncMapMojo_failIfWar() throws IOException { + public void testSyncMapTask_withSkaffoldConfig() throws IOException { + Path projectRoot = skaffoldProject.getProjectRoot(); + SkaffoldSyncMapTemplate parsed = generateTemplate(skaffoldProject, null, null); + + List generated = parsed.getGenerated(); + Assert.assertEquals(2, generated.size()); + assertFilePaths( + projectRoot.resolve("build/resources/main/world"), + AbsoluteUnixPath.get("/app/resources/world"), + generated.get(0)); + assertFilePaths( + projectRoot.resolve("build/classes/java/main/com/test2/GoodbyeWorld.class"), + AbsoluteUnixPath.get("/app/classes/com/test2/GoodbyeWorld.class"), + generated.get(1)); + // classes/java/main/com/test is ignored + + List direct = parsed.getDirect(); + Assert.assertEquals(1, direct.size()); + assertFilePaths( + projectRoot.resolve("src/main/jib/bar/cat"), + AbsoluteUnixPath.get("/bar/cat"), + direct.get(0)); + // src/main/custom-extra-dir/foo is ignored + } + + @Test + public void testSyncMapTask_failIfWar() throws IOException { Path projectRoot = warProject.getProjectRoot(); try { generateTemplate(warProject, null, null); @@ -166,7 +193,7 @@ public void testSyncMapMojo_failIfWar() throws IOException { } @Test - public void testSyncMapMojo_failIfJarContainerizationMode() throws IOException { + public void testSyncMapTask_failIfJarContainerizationMode() throws IOException { Path projectRoot = simpleTestProject.getProjectRoot(); try { generateTemplate( diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/simple/build.gradle b/jib-gradle-plugin/src/test/resources/gradle/projects/simple/build.gradle index 6dab9e5207..94bbe1963c 100644 --- a/jib-gradle-plugin/src/test/resources/gradle/projects/simple/build.gradle +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/simple/build.gradle @@ -22,3 +22,4 @@ jib { paths = file('src/main/custom-extra-dir') } } + diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/build.gradle b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/build.gradle new file mode 100644 index 0000000000..0b62c493c9 --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'java' + id 'com.google.cloud.tools.jib' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile files('libs/dependency-1.0.0.jar') +} + +jib { + to { + image = System.getProperty("_TARGET_IMAGE") + } + skaffold { + watch { + buildIncludes = project.file("script.gradle") + includes = project.files("other/file.txt") + excludes = "src/main/jib/bar/" + } + sync { + excludes = ["build/classes/java/main/com/test", "src/main/jib/foo"] + } + } +} + diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/libs/dependency-1.0.0.jar b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/libs/dependency-1.0.0.jar new file mode 100644 index 0000000000..333ceb39b2 Binary files /dev/null and b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/libs/dependency-1.0.0.jar differ diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/other/file.txt b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/other/file.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/script.gradle b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/script.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test/HelloWorld.java b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test/HelloWorld.java new file mode 100644 index 0000000000..cf42e84818 --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test/HelloWorld.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.test; + +public class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello world"); + } +} diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test2/GoodbyeWorld.java b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test2/GoodbyeWorld.java new file mode 100644 index 0000000000..9703d67bbc --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/java/com/test2/GoodbyeWorld.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.test2; + +public class GoodbyeWorld { + public static void goodbye() { + System.out.println("Goodbye, world!"); + } +} diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/bar/cat b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/bar/cat new file mode 100644 index 0000000000..5d5b306e57 --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/bar/cat @@ -0,0 +1 @@ +cat \ No newline at end of file diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/foo b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/foo new file mode 100644 index 0000000000..1910281566 --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/jib/foo @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/resources/world b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/resources/world new file mode 100644 index 0000000000..04fea06420 --- /dev/null +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/skaffold-config/src/main/resources/world @@ -0,0 +1 @@ +world \ No newline at end of file diff --git a/jib-maven-plugin/CHANGELOG.md b/jib-maven-plugin/CHANGELOG.md index 3c3d318832..7642757274 100644 --- a/jib-maven-plugin/CHANGELOG.md +++ b/jib-maven-plugin/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. ### Added - Additionally reads credentials from `~/.docker/.dockerconfigjson` and legacy Docker config (`~/.docker/.dockercfg`). Also searches for `$HOME/.docker/*` (in addition to current `System.get("user.home")/.docker/*`). This may help retrieve credentials, for example, on Kubernetes. ([#2260](https://github.com/GoogleContainerTools/jib/issues/2260)) +- New skaffold configuration options that modify how jib's build config is presented to skaffold ([#2292](https://github.com/GoogleContainerTools/jib/pull/2292)): + - ``: a list of build files to watch + - ``: a list of project files to watch + - ``: a list of files to exclude from watching + - ``: a list of files to exclude from sync'ing ### Changed diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2.java index da1ea59807..331f11a106 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.apache.maven.artifact.Artifact; @@ -163,6 +164,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { resolveExtraDirectories(project).forEach(skaffoldFilesOutput::addInput); } + // See above note on "extraFiles" + SkaffoldConfiguration.Watch watch = collectWatchParameters(project); + resolveFiles(watch.buildIncludes, project).forEach(skaffoldFilesOutput::addBuild); + resolveFiles(watch.includes, project).forEach(skaffoldFilesOutput::addInput); + // we don't do any special pre-processing for ignore (input and ignore can overlap with exact + // matches) + resolveFiles(watch.excludes, project).forEach(skaffoldFilesOutput::addIgnore); + // Grab non-project SNAPSHOT dependencies for this project // TODO: this whole sections relies on internal maven API, it could break. We need to explore // TODO: better ways to resolve dependencies using the public maven API. @@ -239,13 +248,10 @@ private List collectExtraDirectories(MavenProject project) { if (pluginConfiguration != null) { Xpp3Dom extraDirectoriesConfiguration = pluginConfiguration.getChild("extraDirectories"); if (extraDirectoriesConfiguration != null) { - Xpp3Dom child = extraDirectoriesConfiguration.getChild("paths"); - if (child != null) { + Xpp3Dom paths = extraDirectoriesConfiguration.getChild("paths"); + if (paths != null) { // ...... - return Arrays.stream(child.getChildren()) - .map(Xpp3Dom::getValue) - .map(Paths::get) - .collect(Collectors.toList()); + return xpp3ToList(paths, Paths::get); } } } @@ -256,4 +262,50 @@ private List collectExtraDirectories(MavenProject project) { Path srcMainJib = Paths.get("src", "main", "jib"); return Collections.singletonList(projectBase.resolve(srcMainJib)); } + + private SkaffoldConfiguration.Watch collectWatchParameters(MavenProject project) { + // Try getting extra directory from project pom + SkaffoldConfiguration.Watch watchConfig = new SkaffoldConfiguration.Watch(); + Plugin jibMavenPlugin = project.getPlugin(MavenProjectProperties.PLUGIN_KEY); + if (jibMavenPlugin != null) { + Xpp3Dom pluginConfiguration = (Xpp3Dom) jibMavenPlugin.getConfiguration(); + if (pluginConfiguration != null) { + Xpp3Dom skaffold = pluginConfiguration.getChild("skaffold"); + if (skaffold != null) { + Xpp3Dom watch = skaffold.getChild("watch"); + if (watch != null) { + Xpp3Dom buildIncludes = watch.getChild("buildIncludes"); + if (buildIncludes != null) { + watchConfig.buildIncludes = xpp3ToList(buildIncludes, File::new); + } + Xpp3Dom includes = watch.getChild("includes"); + if (includes != null) { + watchConfig.includes = xpp3ToList(includes, File::new); + } + Xpp3Dom excludes = watch.getChild("excludes"); + if (excludes != null) { + watchConfig.excludes = xpp3ToList(excludes, File::new); + } + } + } + } + } + return watchConfig; + } + + private List resolveFiles(List files, MavenProject project) { + return files + .stream() + .map(File::toPath) + .map(path -> path.isAbsolute() ? path : project.getBasedir().toPath().resolve(path)) + .collect(Collectors.toList()); + } + + private List xpp3ToList(Xpp3Dom node, Function converter) { + Preconditions.checkNotNull(node); + return Arrays.stream(node.getChildren()) + .map(Xpp3Dom::getValue) + .map(converter) + .collect(Collectors.toList()); + } } diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SkaffoldConfiguration.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SkaffoldConfiguration.java new file mode 100644 index 0000000000..f02572253f --- /dev/null +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SkaffoldConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Google LLC. + * + * Licensed 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 com.google.cloud.tools.jib.maven.skaffold; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import org.apache.maven.plugins.annotations.Parameter; + +/** Skaffold specific Jib plugin configuration options. */ +public class SkaffoldConfiguration { + + /** Skaffold specific Jib plugin configuration for files to watch. */ + public static class Watch { + @Parameter List buildIncludes = Collections.emptyList(); + @Parameter List includes = Collections.emptyList(); + @Parameter List excludes = Collections.emptyList(); + } + + /** Skaffold specific Jib plugin configuration for files to sync. */ + public static class Sync { + @Parameter List excludes = Collections.emptyList(); + } + + /** + * Watch is unused, but left here to define how to parse it. See {@link + * FilesMojoV2#collectWatchParameters} + */ + @Parameter Watch watch = new Watch(); + + @Parameter Sync sync = new Sync(); +} diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojo.java index 930ea8dd30..7fd69717c8 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojo.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojo.java @@ -25,8 +25,12 @@ import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; import com.google.cloud.tools.jib.plugins.common.PluginConfigurationProcessor; import com.google.common.annotations.VisibleForTesting; +import java.io.File; +import java.nio.file.Path; +import java.util.stream.Collectors; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; @Mojo( @@ -36,6 +40,8 @@ public class SyncMapMojo extends JibPluginConfiguration { @VisibleForTesting static final String GOAL_NAME = "_skaffold-sync-map"; + @Parameter SkaffoldConfiguration skaffold = new SkaffoldConfiguration(); + @Override public void execute() throws MojoExecutionException { checkJibVersion(); @@ -76,7 +82,16 @@ public void execute() throws MojoExecutionException { try { String syncMapJson = - PluginConfigurationProcessor.getSkaffoldSyncMap(configuration, projectProperties); + PluginConfigurationProcessor.getSkaffoldSyncMap( + configuration, + projectProperties, + skaffold + .sync + .excludes + .stream() + .map(File::toPath) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet())); System.out.println(); System.out.println("BEGIN JIB JSON: SYNCMAP/1"); diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2Test.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2Test.java index 5dc754b9a8..228dc1a9a1 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2Test.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/FilesMojoV2Test.java @@ -46,7 +46,8 @@ private static void verifyFiles( String module, List extraCliOptions, List buildFiles, - List inputFiles) + List inputFiles, + List ignoreFiles) throws VerificationException, IOException { Verifier verifier = new Verifier(projectRoot.toString()); @@ -70,7 +71,7 @@ private static void verifyFiles( SkaffoldFilesOutput output = new SkaffoldFilesOutput(log.get(begin + 1)); Assert.assertEquals(buildFiles, output.getBuild()); Assert.assertEquals(inputFiles, output.getInputs()); - Assert.assertEquals(0, output.getIgnore().size()); + Assert.assertEquals(ignoreFiles, output.getIgnore()); } @Test @@ -86,7 +87,8 @@ public void testFilesMojo_singleModule() throws VerificationException, IOExcepti Arrays.asList( projectRoot.resolve("src/main/java").toString(), projectRoot.resolve("src/main/resources").toString(), - projectRoot.resolve("src/main/jib-custom").toString())); + projectRoot.resolve("src/main/jib-custom").toString()), + Collections.emptyList()); } @Test @@ -104,7 +106,8 @@ public void testFilesMojo_singleModuleWithMultipleExtraDirectories() projectRoot.resolve("src/main/java").toString(), projectRoot.resolve("src/main/resources").toString(), projectRoot.resolve("src/main/jib-custom").toString(), - projectRoot.resolve("src/main/jib-custom-2").toString())); + projectRoot.resolve("src/main/jib-custom-2").toString()), + Collections.emptyList()); } @Test @@ -123,7 +126,8 @@ public void testFilesMojo_multiModuleSimpleService() throws VerificationExceptio Arrays.asList( simpleServiceRoot.resolve("src/main/java").toString(), simpleServiceRoot.resolve("src/main/resources").toString(), - simpleServiceRoot.resolve("src/main/jib").toString())); + simpleServiceRoot.resolve("src/main/jib").toString()), + Collections.emptyList()); } @Test @@ -154,7 +158,8 @@ public void testFilesMojo_multiModuleComplexService() throws VerificationExcepti Paths.get( System.getProperty("user.home"), ".m2/repository/com/google/cloud/tools/tiny-test-lib/0.0.1-SNAPSHOT/tiny-test-lib-0.0.1-SNAPSHOT.jar") - .toString())); + .toString()), + Collections.emptyList()); } @Test @@ -171,6 +176,29 @@ public void testFilesMojo_extraDirectoriesProperty() throws VerificationExceptio projectRoot.resolve("src/main/java").toString(), projectRoot.resolve("src/main/resources").toString(), Paths.get("/").toAbsolutePath().resolve("some/extra/dir").toString(), - Paths.get("/").toAbsolutePath().resolve("another/extra/dir").toString())); + Paths.get("/").toAbsolutePath().resolve("another/extra/dir").toString()), + Collections.emptyList()); + } + + @Test + public void testFilesMojo_skaffoldConfigProperties() throws VerificationException, IOException { + Path projectRoot = simpleTestProject.getProjectRoot(); + + verifyFiles( + projectRoot, + "pom-skaffold-config.xml", + null, + Collections.emptyList(), + Arrays.asList( + projectRoot.resolve("pom-skaffold-config.xml").toString(), + Paths.get("/abs/path/some.xml").toAbsolutePath().toString()), + Arrays.asList( + projectRoot.resolve("src/main/java").toString(), + projectRoot.resolve("src/main/resources").toString(), + projectRoot.resolve("src/main/jib-custom").toString(), + projectRoot.resolve("file/in/project").toString()), + Arrays.asList( + projectRoot.resolve("file/to/exclude").toString(), + projectRoot.resolve("file/to/also/exclude").toString())); } } diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojoTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojoTest.java index 68e9b545ad..9fd1368136 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojoTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/skaffold/SyncMapMojoTest.java @@ -61,9 +61,9 @@ private static Path runBuild(Path projectRoot, String module, String pomXml) return Paths.get(verifier.getBasedir()).resolve(verifier.getLogFileName()); } - private static String getSyncMapJson(Path projectRoot, String module) + private static String getSyncMapJson(Path projectRoot, String module, String pomXml) throws VerificationException, IOException { - Path logFile = runBuild(projectRoot, module, null); + Path logFile = runBuild(projectRoot, module, pomXml); List outputLines = Files.readAllLines(logFile, StandardCharsets.UTF_8); Assert.assertEquals(3, outputLines.size()); // we expect ["\n", "", ""] Assert.assertEquals("BEGIN JIB JSON: SYNCMAP/1", outputLines.get(1)); @@ -78,7 +78,7 @@ private static void assertFilePaths(Path src, AbsoluteUnixPath dest, FileTemplat @Test public void testSyncMapMojo_simpleTestProjectOutput() throws IOException, VerificationException { Path projectRoot = simpleTestProject.getProjectRoot(); - String json = getSyncMapJson(projectRoot, null); + String json = getSyncMapJson(projectRoot, null, null); SkaffoldSyncMapTemplate parsed = SkaffoldSyncMapTemplate.from(json); List generated = parsed.getGenerated(); @@ -108,7 +108,7 @@ public void testSyncMapMojo_simpleTestProjectOutput() throws IOException, Verifi public void testSyncMapMojo_multiProjectOutput() throws IOException, VerificationException { Path projectRoot = multiTestProject.getProjectRoot(); Path m2 = Paths.get(System.getProperty("user.home")).resolve(".m2").resolve("repository"); - String json = getSyncMapJson(projectRoot, "complex-service"); + String json = getSyncMapJson(projectRoot, "complex-service", null); SkaffoldSyncMapTemplate parsed = SkaffoldSyncMapTemplate.from(json); List generated = parsed.getGenerated(); @@ -131,6 +131,29 @@ public void testSyncMapMojo_multiProjectOutput() throws IOException, Verificatio direct.get(0)); } + @Test + public void testSyncMapMojo_skaffoldConfig() throws IOException, VerificationException { + Path projectRoot = simpleTestProject.getProjectRoot(); + String json = getSyncMapJson(projectRoot, null, "pom-skaffold-config.xml"); + SkaffoldSyncMapTemplate parsed = SkaffoldSyncMapTemplate.from(json); + + List generated = parsed.getGenerated(); + Assert.assertEquals(1, generated.size()); + assertFilePaths( + projectRoot.resolve("target/classes/world"), + AbsoluteUnixPath.get("/app/resources/world"), + generated.get(0)); + // target/classes/com/test ignored + + List direct = parsed.getDirect(); + Assert.assertEquals(1, direct.size()); + assertFilePaths( + projectRoot.resolve("src/main/jib-custom/bar/cat"), + AbsoluteUnixPath.get("/bar/cat"), + direct.get(0)); + // src/main/jib-custom/foo is ignored + } + @Test public void testSyncMapMojo_failIfPackagingNotJar() throws IOException { Path projectRoot = warProject.getProjectRoot(); diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-skaffold-config.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-skaffold-config.xml new file mode 100644 index 0000000000..605544b2dd --- /dev/null +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-skaffold-config.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + com.test + hello-world + 1 + + + UTF-8 + UTF-8 + @@PluginVersion@@ + + + + + com.test + dependency + 1.0.0 + system + ${project.basedir}/libs/dependency-1.0.0.jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + com.google.cloud.tools + jib-maven-plugin + ${jib-maven-plugin.version} + + + + src/main/jib-custom + + + + + + /abs/path/some.xml + + + file/in/project + + + file/to/exclude + file/to/also/exclude + + + + + target/classes/com/test + src/main/jib-custom/foo + + + + + + + + diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java index 1fd797209f..19341e3573 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java @@ -51,6 +51,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Stream; import javax.annotation.Nullable; /** @@ -269,6 +270,7 @@ public static JibBuildRunner createJibBuildRunnerForRegistryImage( * * @param rawConfiguration the raw configuration from the plugin * @param projectProperties an plugin specific implementation of {@link ProjectProperties} + * @param excludes a set of paths to exclude, directories include in this list will be expanded * @return new json string representation of the Sync Map * @throws InvalidImageReferenceException if the image reference is invalid * @throws MainClassInferenceException if a main class could not be found @@ -287,7 +289,7 @@ public static JibBuildRunner createJibBuildRunnerForRegistryImage( * @throws InvalidCreationTimeException if configured creation time could not be parsed */ public static String getSkaffoldSyncMap( - RawConfiguration rawConfiguration, ProjectProperties projectProperties) + RawConfiguration rawConfiguration, ProjectProperties projectProperties, Set excludes) throws IOException, InvalidCreationTimeException, InvalidImageReferenceException, IncompatibleBaseImageJavaVersionException, InvalidContainerVolumeException, MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, @@ -298,6 +300,7 @@ public static String getSkaffoldSyncMap( SkaffoldSyncMapTemplate syncMap = new SkaffoldSyncMapTemplate(); // since jib has already expanded out directories after processing everything, we just // ignore directories and provide only files to watch + Set excludesExpanded = getAllFiles(excludes); for (LayerConfiguration layer : jibContainerBuilder.describeContainer().getLayers()) { if (CONST_LAYERS.contains(layer.getName())) { continue; @@ -307,18 +310,39 @@ public static String getSkaffoldSyncMap( .getLayerEntries() .stream() .filter(layerEntry -> Files.isRegularFile(layerEntry.getSourceFile())) + .filter( + layerEntry -> + !excludesExpanded.contains(layerEntry.getSourceFile().toAbsolutePath())) .forEach(syncMap::addGenerated); } else { // this is a direct layer layer .getLayerEntries() .stream() .filter(layerEntry -> Files.isRegularFile(layerEntry.getSourceFile())) + .filter( + layerEntry -> + !excludesExpanded.contains(layerEntry.getSourceFile().toAbsolutePath())) .forEach(syncMap::addDirect); } } return syncMap.getJsonString(); } + /** Expand directories to files (excludes directory paths). */ + static Set getAllFiles(Set paths) throws IOException { + Set expanded = new HashSet<>(); + for (Path path : paths) { + if (Files.isRegularFile(path)) { + expanded.add(path); + } else if (Files.isDirectory(path)) { + try (Stream dirWalk = Files.walk(path)) { + dirWalk.filter(Files::isRegularFile).forEach(expanded::add); + } + } + } + return expanded; + } + @VisibleForTesting static JibContainerBuilder processCommonConfiguration( RawConfiguration rawConfiguration, diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java index 34f18a2dae..2c4ad9813a 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java @@ -33,9 +33,11 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; @@ -944,4 +946,23 @@ private JibContainerBuilder processCommonConfiguration() return PluginConfigurationProcessor.processCommonConfiguration( rawConfiguration, ignored -> Optional.empty(), projectProperties, containerizer); } + + @Test + public void getAllFiles_expandsDirectories() throws IOException { + File rootFile = temporaryFolder.newFile("file"); + File folder = temporaryFolder.newFolder("folder"); + File folderFile = temporaryFolder.newFile("folder/file2"); + Assert.assertEquals( + ImmutableSet.of(rootFile.toPath().toAbsolutePath(), folderFile.toPath().toAbsolutePath()), + PluginConfigurationProcessor.getAllFiles( + ImmutableSet.of(rootFile.toPath(), folder.toPath()))); + } + + @Test + public void getAllFiles_doesntBreakForNonExistentFiles() throws IOException { + Path testPath = Paths.get("/a/file/that/doesnt/exist"); + Assert.assertFalse(Files.exists(testPath)); + Assert.assertEquals( + ImmutableSet.of(), PluginConfigurationProcessor.getAllFiles(ImmutableSet.of(testPath))); + } }