Skip to content

Commit 5446d45

Browse files
authored
Add _jibSkaffoldFiles task to gradle plugin (#1021)
1 parent 84678d9 commit 5446d45

File tree

25 files changed

+565
-1
lines changed

25 files changed

+565
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright 2018 Google LLC.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.google.cloud.tools.jib.gradle;
18+
19+
import com.google.common.base.Preconditions;
20+
import java.io.File;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
import javax.annotation.Nullable;
26+
import org.gradle.api.DefaultTask;
27+
import org.gradle.api.Project;
28+
import org.gradle.api.artifacts.Configuration;
29+
import org.gradle.api.artifacts.Dependency;
30+
import org.gradle.api.artifacts.ProjectDependency;
31+
import org.gradle.api.artifacts.PublishArtifact;
32+
import org.gradle.api.initialization.Settings;
33+
import org.gradle.api.plugins.JavaPluginConvention;
34+
import org.gradle.api.tasks.SourceSet;
35+
import org.gradle.api.tasks.TaskAction;
36+
37+
/**
38+
* Print out changing source dependencies on a project.
39+
*
40+
* <p>Expected use: {@code ./gradlew _jibSkaffoldFiles -q} or {@code ./gradlew
41+
* :<subproject>:_jibSkaffoldFiles -q}
42+
*/
43+
public class FilesTask extends DefaultTask {
44+
45+
/**
46+
* Prints the locations of a project's build.gradle, settings.gradle, and gradle.properties.
47+
*
48+
* @param project the project
49+
*/
50+
private static void printGradleFiles(Project project) {
51+
Path projectPath = project.getProjectDir().toPath();
52+
53+
// Print build.gradle
54+
System.out.println(project.getBuildFile());
55+
56+
// Print settings.gradle
57+
if (project.getGradle().getStartParameter().getSettingsFile() != null) {
58+
System.out.println(project.getGradle().getStartParameter().getSettingsFile());
59+
} else if (Files.exists(projectPath.resolve(Settings.DEFAULT_SETTINGS_FILE))) {
60+
System.out.println(projectPath.resolve(Settings.DEFAULT_SETTINGS_FILE));
61+
}
62+
63+
// Print gradle.properties
64+
if (Files.exists(projectPath.resolve("gradle.properties"))) {
65+
System.out.println(projectPath.resolve("gradle.properties"));
66+
}
67+
}
68+
69+
/**
70+
* Prints build files, sources, and resources associated with a project.
71+
*
72+
* @param project the project
73+
*/
74+
private static void printProjectFiles(Project project) {
75+
JavaPluginConvention javaConvention =
76+
project.getConvention().getPlugin(JavaPluginConvention.class);
77+
SourceSet mainSourceSet =
78+
javaConvention.getSourceSets().findByName(SourceSet.MAIN_SOURCE_SET_NAME);
79+
if (mainSourceSet == null) {
80+
return;
81+
}
82+
83+
// Print build config, settings, etc.
84+
printGradleFiles(project);
85+
86+
// Print sources + resources
87+
mainSourceSet
88+
.getAllSource()
89+
.getSourceDirectories()
90+
.forEach(
91+
sourceDirectory -> {
92+
if (sourceDirectory.exists()) {
93+
System.out.println(sourceDirectory);
94+
}
95+
});
96+
}
97+
98+
/**
99+
* Recursive function for printing out a project's artifacts. Calls itself when it encounters a
100+
* project dependency.
101+
*
102+
* @param project the project to list the artifacts for
103+
* @param projectDependenciesResult the set of project dependencies encountered. When a project
104+
* dependency is encountered, it is added to this set.
105+
*/
106+
private static void findProjectDependencies(
107+
Project project, Set<ProjectDependency> projectDependenciesResult) {
108+
for (Configuration configuration :
109+
project.getConfigurations().getByName("runtime").getHierarchy()) {
110+
for (Dependency dependency : configuration.getDependencies()) {
111+
if (dependency instanceof ProjectDependency) {
112+
projectDependenciesResult.add((ProjectDependency) dependency);
113+
findProjectDependencies(
114+
((ProjectDependency) dependency).getDependencyProject(), projectDependenciesResult);
115+
}
116+
}
117+
}
118+
}
119+
120+
@Nullable private JibExtension jibExtension;
121+
122+
public FilesTask setJibExtension(JibExtension jibExtension) {
123+
this.jibExtension = jibExtension;
124+
return this;
125+
}
126+
127+
@TaskAction
128+
public void listFiles() {
129+
Preconditions.checkNotNull(jibExtension);
130+
Project project = getProject();
131+
132+
// If this is not the root project, print the root project's build.gradle and settings.gradle
133+
if (project != project.getRootProject()) {
134+
printGradleFiles(project.getRootProject());
135+
}
136+
137+
printProjectFiles(project);
138+
139+
// Print extra layer
140+
if (Files.exists(jibExtension.getExtraDirectoryPath())) {
141+
System.out.println(jibExtension.getExtraDirectoryPath());
142+
}
143+
144+
// Find project dependencies
145+
Set<ProjectDependency> projectDependencies = new HashSet<>();
146+
findProjectDependencies(project, projectDependencies);
147+
148+
Set<File> projectDependencyJars = new HashSet<>();
149+
for (ProjectDependency projectDependency : projectDependencies) {
150+
printProjectFiles(projectDependency.getDependencyProject());
151+
152+
// Keep track of project dependency jars for filtering out later
153+
String configurationName = projectDependency.getTargetConfiguration();
154+
if (configurationName == null) {
155+
configurationName = "default";
156+
}
157+
Project dependencyProject = projectDependency.getDependencyProject();
158+
for (Configuration targetConfiguration :
159+
dependencyProject.getConfigurations().getByName(configurationName).getHierarchy()) {
160+
for (PublishArtifact artifact : targetConfiguration.getArtifacts()) {
161+
projectDependencyJars.add(artifact.getFile());
162+
}
163+
}
164+
}
165+
166+
// Print out SNAPSHOT, non-project dependency jars
167+
for (File file : project.getConfigurations().getByName("runtime")) {
168+
if (!projectDependencyJars.contains(file) && file.toString().contains("SNAPSHOT")) {
169+
System.out.println(file);
170+
projectDependencyJars.add(file); // Add to set to avoid printing the same files twice
171+
}
172+
}
173+
}
174+
}

jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/JibPlugin.java

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class JibPlugin implements Plugin<Project> {
3939
@VisibleForTesting static final String BUILD_TAR_TASK_NAME = "jibBuildTar";
4040
@VisibleForTesting static final String BUILD_DOCKER_TASK_NAME = "jibDockerBuild";
4141
@VisibleForTesting static final String DOCKER_CONTEXT_TASK_NAME = "jibExportDockerContext";
42+
@VisibleForTesting static final String FILES_TASK_NAME = "_jibSkaffoldFiles";
4243

4344
/**
4445
* Collects all project dependencies of the style "compile project(':mylib')" for any kind of
@@ -102,6 +103,8 @@ public void apply(Project project) {
102103
.getTasks()
103104
.create(BUILD_TAR_TASK_NAME, BuildTarTask.class)
104105
.setJibExtension(jibExtension);
106+
Task filesTask =
107+
project.getTasks().create(FILES_TASK_NAME, FilesTask.class).setJibExtension(jibExtension);
105108

106109
project.afterEvaluate(
107110
projectAfterEvaluation -> {
@@ -112,6 +115,7 @@ public void apply(Project project) {
112115
dockerContextTask.dependsOn(classesTask);
113116
buildDockerTask.dependsOn(classesTask);
114117
buildTarTask.dependsOn(classesTask);
118+
filesTask.dependsOn(classesTask);
115119

116120
// Find project dependencies and add a dependency to their assemble task. We make sure
117121
// to only add the dependency after BasePlugin is evaluated as otherwise the assemble
@@ -129,6 +133,7 @@ public void apply(Project project) {
129133
dockerContextTask.dependsOn(assembleTask);
130134
buildDockerTask.dependsOn(assembleTask);
131135
buildTarTask.dependsOn(assembleTask);
136+
filesTask.dependsOn(assembleTask);
132137
});
133138
}
134139
} catch (UnknownTaskException ex) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2018 Google LLC.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.google.cloud.tools.jib.gradle;
18+
19+
import com.google.common.base.Splitter;
20+
import com.google.common.collect.ImmutableList;
21+
import java.io.IOException;
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
24+
import java.util.List;
25+
import java.util.stream.Collectors;
26+
import javax.annotation.Nullable;
27+
import org.gradle.testkit.runner.BuildResult;
28+
import org.gradle.testkit.runner.BuildTask;
29+
import org.gradle.testkit.runner.TaskOutcome;
30+
import org.hamcrest.CoreMatchers;
31+
import org.junit.Assert;
32+
import org.junit.ClassRule;
33+
import org.junit.Test;
34+
35+
/** Tests for {@link FilesTask}. */
36+
public class FilesTaskTest {
37+
38+
@ClassRule public static final TestProject simpleTestProject = new TestProject("simple");
39+
40+
@ClassRule public static final TestProject multiTestProject = new TestProject("multi-service");
41+
42+
/**
43+
* Verifies that the files task succeeded and returns the list of paths it prints out.
44+
*
45+
* @param project the project to run the task on
46+
* @param moduleName the name of the sub-project, or {@code null} if no sub-project
47+
* @return the list of paths printed by the task
48+
*/
49+
private static List<Path> verifyTaskSuccess(TestProject project, @Nullable String moduleName) {
50+
String taskName =
51+
":" + (moduleName == null ? "" : moduleName + ":") + JibPlugin.FILES_TASK_NAME;
52+
BuildResult buildResult = project.build(taskName, "-q");
53+
BuildTask jibTask = buildResult.task(taskName);
54+
Assert.assertNotNull(jibTask);
55+
Assert.assertEquals(TaskOutcome.SUCCESS, jibTask.getOutcome());
56+
57+
return Splitter.on(System.lineSeparator())
58+
.omitEmptyStrings()
59+
.splitToList(buildResult.getOutput())
60+
.stream()
61+
.map(Paths::get)
62+
.collect(Collectors.toList());
63+
}
64+
65+
/**
66+
* Asserts that two lists contain the same paths. Required to avoid Mac's /var/ vs. /private/var/
67+
* symlink issue.
68+
*
69+
* @param expected the expected list of paths
70+
* @param actual the actual list of paths
71+
* @throws IOException if checking if two files are the same fails
72+
*/
73+
private static void assertPathListsAreEqual(List<Path> expected, List<Path> actual)
74+
throws IOException {
75+
Assert.assertEquals(expected.size(), actual.size());
76+
for (int index = 0; index < expected.size(); index++) {
77+
Assert.assertEquals(expected.get(index).toRealPath(), actual.get(index).toRealPath());
78+
}
79+
}
80+
81+
@Test
82+
public void testFilesTask_singleProject() throws IOException {
83+
Path projectRoot = simpleTestProject.getProjectRoot();
84+
List<Path> result = verifyTaskSuccess(simpleTestProject, null);
85+
List<Path> expected =
86+
ImmutableList.of(
87+
projectRoot.resolve("build.gradle"),
88+
projectRoot.resolve("src/main/resources"),
89+
projectRoot.resolve("src/main/java"),
90+
projectRoot.resolve("src/main/custom-extra-dir"));
91+
assertPathListsAreEqual(expected, result);
92+
}
93+
94+
@Test
95+
public void testFilesTask_multiProjectSimpleService() throws IOException {
96+
Path projectRoot = multiTestProject.getProjectRoot();
97+
Path simpleServiceRoot = projectRoot.resolve("simple-service");
98+
List<Path> result = verifyTaskSuccess(multiTestProject, "simple-service");
99+
List<Path> expected =
100+
ImmutableList.of(
101+
projectRoot.resolve("build.gradle"),
102+
projectRoot.resolve("settings.gradle"),
103+
projectRoot.resolve("gradle.properties"),
104+
simpleServiceRoot.resolve("build.gradle"),
105+
simpleServiceRoot.resolve("src/main/java"));
106+
assertPathListsAreEqual(expected, result);
107+
}
108+
109+
@Test
110+
public void testFilesTask_multiProjectComplexService() throws IOException {
111+
Path projectRoot = multiTestProject.getProjectRoot();
112+
Path complexServiceRoot = projectRoot.resolve("complex-service");
113+
Path libRoot = projectRoot.resolve("lib");
114+
List<Path> result = verifyTaskSuccess(multiTestProject, "complex-service");
115+
List<Path> expected =
116+
ImmutableList.of(
117+
projectRoot.resolve("build.gradle"),
118+
projectRoot.resolve("settings.gradle"),
119+
projectRoot.resolve("gradle.properties"),
120+
complexServiceRoot.resolve("build.gradle"),
121+
complexServiceRoot.resolve("src/main/extra-resources-1"),
122+
complexServiceRoot.resolve("src/main/extra-resources-2"),
123+
complexServiceRoot.resolve("src/main/java"),
124+
complexServiceRoot.resolve("src/main/other-jib"),
125+
libRoot.resolve("build.gradle"),
126+
libRoot.resolve("src/main/resources"),
127+
libRoot.resolve("src/main/java"));
128+
assertPathListsAreEqual(expected, result.subList(0, 11));
129+
130+
// guava jar is in a temporary-looking directory, so don't do a full match here
131+
Assert.assertThat(
132+
result.get(result.size() - 1).toString(),
133+
CoreMatchers.endsWith("guava-HEAD-jre-SNAPSHOT.jar"));
134+
Assert.assertEquals(12, result.size());
135+
}
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
plugins {
2+
id 'java'
3+
id 'com.google.cloud.tools.jib'
4+
}
5+
6+
sourceCompatibility = 1.8
7+
targetCompatibility = 1.8
8+
9+
repositories {
10+
mavenCentral()
11+
}
12+
13+
jib {
14+
to {
15+
image = System.getProperty("_TARGET_IMAGE")
16+
}
17+
}

0 commit comments

Comments
 (0)