Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto build gradle lib-project dependencies #821

Merged
merged 6 commits into from
Aug 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jib-gradle-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ All notable changes to this project will be documented in this file.

### Fixed

- Gradle project dependencies have their `assemble` task run before running a jib task ([#815](https://github.com/GoogleContainerTools/jib/issues/815))

## 0.9.8

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
package com.google.cloud.tools.jib.gradle;

import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.stream.Collectors;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.DependencySet;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.util.GradleVersion;

public class JibPlugin implements Plugin<Project> {
Expand All @@ -34,6 +40,42 @@ public class JibPlugin implements Plugin<Project> {
@VisibleForTesting static final String BUILD_DOCKER_TASK_NAME = "jibDockerBuild";
@VisibleForTesting static final String DOCKER_CONTEXT_TASK_NAME = "jibExportDockerContext";

/**
* Collects all assemble tasks for project dependencies of the style "compile project(':mylib')"
* for any kind of configuration [compile, runtime, etc]. It potentially will collect common test
* libraries in configs like [test, integrationTest, etc], but it's either that or filter based on
* a configuration containing the word "test" which feels dangerous.
*
* @param project this project we are containerizing
* @return a list of "assemble" tasks associated with projects that this project depends on.
*/
@VisibleForTesting
static List<Task> getProjectDependencyAssembleTasks(Project project) {
return project
.getConfigurations()
.stream()
.map(Configuration::getDependencies)
.flatMap(DependencySet::stream)
.filter(ProjectDependency.class::isInstance)
.map(ProjectDependency.class::cast)
.map(ProjectDependency::getDependencyProject)
.map(subProject -> subProject.getTasks().getByPath(BasePlugin.ASSEMBLE_TASK_NAME))
.collect(Collectors.toList());
}

private static void checkGradleVersion() {
if (GRADLE_MIN_VERSION.compareTo(GradleVersion.current()) > 0) {
throw new GradleException(
"Detected "
+ GradleVersion.current()
+ ", but jib requires "
+ GRADLE_MIN_VERSION
+ " or higher. You can upgrade by running 'gradle wrapper --gradle-version="
+ GRADLE_MIN_VERSION.getVersion()
+ "'.");
}
}

@Override
public void apply(Project project) {
checkGradleVersion();
Expand Down Expand Up @@ -62,16 +104,23 @@ public void apply(Project project) {
.create(BUILD_TAR_TASK_NAME, BuildTarTask.class)
.setJibExtension(jibExtension);

// Has all tasks depend on the 'classes' task.
project.afterEvaluate(
projectAfterEvaluation -> {
try {
// Find project dependencies
List<Task> computedDependencies =
getProjectDependencyAssembleTasks(projectAfterEvaluation);
// Has all tasks depend on the 'classes' task.
Task classesTask = projectAfterEvaluation.getTasks().getByPath("classes");
computedDependencies.add(classesTask);

buildImageTask.dependsOn(classesTask);
dockerContextTask.dependsOn(classesTask);
buildDockerTask.dependsOn(classesTask);
buildTarTask.dependsOn(classesTask);
// dependsOn takes an Object... type
Object[] dependenciesArray = computedDependencies.toArray();

buildImageTask.dependsOn(dependenciesArray);
dockerContextTask.dependsOn(dependenciesArray);
buildDockerTask.dependsOn(dependenciesArray);
buildTarTask.dependsOn(dependenciesArray);

} catch (UnknownTaskException ex) {
throw new GradleException(
Expand All @@ -82,17 +131,4 @@ public void apply(Project project) {
}
});
}

private static void checkGradleVersion() {
if (GRADLE_MIN_VERSION.compareTo(GradleVersion.current()) > 0) {
throw new GradleException(
"Detected "
+ GradleVersion.current()
+ ", but jib requires "
+ GRADLE_MIN_VERSION
+ " or higher. You can upgrade by running 'gradle wrapper --gradle-version="
+ GRADLE_MIN_VERSION.getVersion()
+ "'.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@

package com.google.cloud.tools.jib.gradle;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.stream.Collectors;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.UnexpectedBuildFailure;
import org.junit.Assert;
Expand All @@ -30,6 +39,12 @@
/** Tests for {@link JibPlugin}. */
public class JibPluginTest {

private static final ImmutableList<String> KNOWN_JIB_TASKS =
ImmutableList.of(
JibPlugin.BUILD_IMAGE_TASK_NAME,
JibPlugin.BUILD_DOCKER_TASK_NAME,
JibPlugin.DOCKER_CONTEXT_TASK_NAME,
JibPlugin.BUILD_TAR_TASK_NAME);
@Rule public TemporaryFolder testProjectRoot = new TemporaryFolder();

@Test
Expand Down Expand Up @@ -68,4 +83,69 @@ public void testCheckGradleVersion_fail() throws IOException {
+ "'."));
}
}

@Test
public void testGetProjectDependencyAssembleTasks() {
// root project is our jib packaged service
Project rootProject =
ProjectBuilder.builder().withProjectDir(testProjectRoot.getRoot()).withName("root").build();
rootProject.getPluginManager().apply("java");

// our service DOES depend on this, and jib should trigger an assemble from this project
Project subProject =
ProjectBuilder.builder()
.withParent(rootProject)
.withProjectDir(testProjectRoot.getRoot())
.withName("sub")
.build();
subProject.getPluginManager().apply("java");

// our service doesn't depend on this, and jib should NOT trigger an assemble from this project
Project unrelatedSubProject =
ProjectBuilder.builder()
.withParent(rootProject)
.withProjectDir(testProjectRoot.getRoot())
.withName("unrelated")
.build();
unrelatedSubProject.getPluginManager().apply("java");

// equivalent of "compile project(':sub')" on the root(jib) project
rootProject
.getConfigurations()
.getByName("compile")
.getDependencies()
.add(rootProject.getDependencies().project(ImmutableMap.of("path", subProject.getPath())));

// programmatic check
Assert.assertEquals(
Collections.singletonList(":sub:assemble"),
JibPlugin.getProjectDependencyAssembleTasks(rootProject)
.stream()
.map(Task::getPath)
.collect(Collectors.toList()));

// check by applying the jib plugin and inspect the task dependencies
rootProject.getPluginManager().apply("com.google.cloud.tools.jib");

// add a custom task that our jib tasks depend on to ensure we do not overwrite this dependsOn
Task dependencyTask = rootProject.getTasks().create("myCustomTask", task -> {});
KNOWN_JIB_TASKS.forEach(
taskName -> rootProject.getTasks().getByPath(taskName).dependsOn(dependencyTask));

((ProjectInternal) rootProject).evaluate();

KNOWN_JIB_TASKS.forEach(
taskName -> {
Assert.assertEquals(
ImmutableSet.of(":sub:assemble", ":classes", ":myCustomTask"),
rootProject
.getTasks()
.getByPath(taskName)
.getDependsOn()
.stream()
.map(Task.class::cast)
.map(Task::getPath)
.collect(Collectors.toSet()));
});
}
}