diff --git a/build.gradle b/build.gradle index f02abefd..75c7092b 100644 --- a/build.gradle +++ b/build.gradle @@ -86,6 +86,10 @@ pluginBundle { id = 'com.palantir.docker' displayName = 'Palantir Gradle Docker' } + dockerComposePlugin { + id = 'com.palantir.docker-compose' + displayName = 'Palantir Gradle Docker-Compose' + } } } diff --git a/readme.md b/readme.md index d0cf8f9b..50b57a81 100644 --- a/readme.md +++ b/readme.md @@ -2,12 +2,19 @@ Docker Gradle Plugin ==================== [![Build Status](https://travis-ci.org/palantir/gradle-docker.svg?branch=develop)](https://travis-ci.org/palantir/gradle-docker) -Adds basic tasks for building and pushing docker images based on a simple -configuration block that specifies the container name, the Dockerfile, task -dependencies, and any additional file resources required for the Docker build. +This repository provides two Gradle plugins for working with Docker containers: +- The `com.palantir.docker` plugin add basic tasks for building and pushing +docker images based on a simple configuration block that specifies the container +name, the Dockerfile, task dependencies, and any additional file resources +required for the Docker build. +- The `com.palantir.docker-compose` plugin adds a task for populating +placeholders in a docker-compose template file with image versions resolved from +dependencies. -Usage ------ +Docker Plugin +------------- + +### Usage Apply the plugin using standard gradle convention: plugins { @@ -33,34 +40,35 @@ Configuration specifying all parameters: dockerfile 'Dockerfile' dependsOn tasks.distTar files 'file1.txt', 'file2.txt' - dockerComposeTemplate 'my-template.yml' - dockerComposeFile 'my-docker-compose.yml' } To build a docker container, run the `docker` task. To push that container to a docker repository, run the `dockerPush` task. -Generating docker-compose dependencies --------------------------------------- +Managing Docker image dependencies +---------------------------------- -The plugin provides mechanisms for managing dependencies between docker images -used in orchestrating `docker-compose` environments. Each project can declare -its dependencies on other docker images and publish an artifact advertising those -dependencies. The plugin uses standard Maven/Ivy machanism for declaring and -resolving dependencies. +The `com.palantir.docker` and `com.palantir.docker-compose` plugins provide +functionality to declare and resolve version-aware dependencies between docker +images. The primary use-case is to generate `docker-compose.yml` files whose +image versions are mutually compatible and up-to-date in cases where multiple +images depend on the existence of the same Dockerized service. -The `generateDockerCompose` task generates a `docker-compose.yml` file from -a user-defined template by replacing each version variable by the concrete version -declared by the transitive dependencies of the docker configuration. +### Specifying and publishing dependencies on Docker images -### Specifying dependencies on Docker images -The plugin adds a `docker` Gradle component and a `docker` Gradle configuration that -can be used to specify and publish dependencies on other Docker containers. -The `generateDockerCompose` task (see below) uses the transitive dependencies -of the `docker` configuration to populate a `docker-compose.yml.template` file -with the image versions specified by this project and all its transitive -dependencies. +The `docker` plugin adds a `docker` Gradle component and a `docker` Gradle +configuration that can be used to specify and publish dependencies on other +Docker containers. + +**Example** + + plugins { + id 'maven-publish' + id 'com.palantir.docker' + } + + ... dependencies { docker 'foogroup:barmodule:0.1.2' @@ -76,16 +84,28 @@ dependencies. } } -The above configuration adds a Maven publication that specifies dependencies -on `barmodule` and the `someSubProject` Gradle sub project. The resulting POM -file has two `dependency` entries, one for each dependency. +The above configuration adds a Maven publication that specifies dependencies on +`barmodule` and the `someSubProject` Gradle sub project. The resulting POM file +has two `dependency` entries, one for each dependency. Each project can declare +its dependencies on other docker images and publish an artifact advertising +those dependencies. -### Generating docker-compose.yml files from templates -The `generateDockerCompose` task performs two operations: First, it generates -a mapping `group:name --> version` from the dependencies of the `docker` -configuration (see above). Second, it replaces all occurrences of version -variables of the form `{{group:name}}` in the `docker-compose.yml.template` file -by the resolved versions and writes the resulting file as `docker-compose.yml`. +### Generating docker-compose.yml files from dependencies + +The `com.palantir.docker-compose` plugin uses the transitive dependencies of the +`docker` configuration to populate a `docker-compose.yml.template` file with the +image versions specified by this project and all its transitive dependencies. +The plugin uses standard Maven/Ivy machanism for declaring and resolving +dependencies. + +The `generateDockerCompose` task generates a `docker-compose.yml` file from a +user-defined template by replacing each version variable by the concrete version +declared by the transitive dependencies of the docker configuration. The task +performs two operations: First, it generates a mapping `group:name --> version` +from the dependencies of the `docker` configuration (see above). Second, it +replaces all occurrences of version variables of the form `{{group:name}}` in +the `docker-compose.yml.template` file by the resolved versions and writes the +resulting file as `docker-compose.yml`. **Example** @@ -96,36 +116,17 @@ Assume a `docker-compose.yml.template` as follows: otherservice: image: 'repository/otherservice:{{othergroup:otherservice}}' -The `build.gradle` of this project publishes a docker Maven artifact declaring -a dependency on a docker image published as 'othergroup:otherservice' in version -0.1.2: +`build.gradle` declares a dependency on a docker image published as +'othergroup:otherservice' in version 0.1.2: plugins { - id 'maven-publish' - id 'com.palantir.docker' - } - - docker { - name 'foo' + id 'com.palantir.docker-compose' } - group 'mygroup' - name 'myservice' - version '2.3.4' - dependencies { docker 'othergroup:otherservice:0.1.2' } - publishing { - publications { - dockerPublication(MavenPublication) { - from components.docker - artifactId project.name + "-docker" - } - } - } - The `generateDockerCompose` task creates a `docker-compose.yml` as follows: myservice: @@ -133,10 +134,21 @@ The `generateDockerCompose` task creates a `docker-compose.yml` as follows: otherservice: image: 'repository/otherservice:0.1.2' -The `generateDockerCompose` task fails if the template file contains variables that -cannot get resolved using the provided `docker` dependencies. Version conflicts between -transitive dependencies of the same artifact are handled with the standard Gradle -semantics: each artifact is resolved to the highest declared version. +The `generateDockerCompose` task fails if the template file contains variables +that cannot get resolved using the provided `docker` dependencies. Version +conflicts between transitive dependencies of the same artifact are handled with +the standard Gradle semantics: each artifact is resolved to the highest declared +version. + +**Configuring file locations** + +The template and generated file locations are customizable through the +`dockerCompose` extension: + + dockerCompose { + template 'my-template.yml' + dockerComposeFile 'my-docker-compose.yml' + } Tasks ----- @@ -153,6 +165,8 @@ Tasks + + License ------- This plugin is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/src/main/groovy/com/palantir/gradle/docker/DockerComposeExtension.groovy b/src/main/groovy/com/palantir/gradle/docker/DockerComposeExtension.groovy new file mode 100644 index 00000000..af9fdada --- /dev/null +++ b/src/main/groovy/com/palantir/gradle/docker/DockerComposeExtension.groovy @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Palantir Technologies + * + * 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 + * + * + * + * 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.palantir.gradle.docker + +import com.google.common.base.Preconditions +import org.gradle.api.Project + +class DockerComposeExtension { + Project project + + private String template = 'docker-compose.yml.template' + private String dockerComposeFile = 'docker-compose.yml' + + private File resolvedDockerComposeTemplate = null + private File resolvedDockerComposeFile = null + + public DockerComposeExtension(Project project) { + this.project = project + } + + public void setTemplate(String dockerComposeTemplate) { + this.template = dockerComposeTemplate + Preconditions.checkArgument(project.file(dockerComposeTemplate).exists(), + "Could not find specified template file: %s", project.file(dockerComposeTemplate)) + } + + public void setDockerComposeFile(String dockerComposeFile) { + this.dockerComposeFile = dockerComposeFile + } + + File getResolvedDockerComposeTemplate() { + return resolvedDockerComposeTemplate + } + + File getResolvedDockerComposeFile() { + return resolvedDockerComposeFile + } + + public void resolvePathsAndValidate() { + resolvedDockerComposeFile = project.file(dockerComposeFile) + resolvedDockerComposeTemplate = project.file(template) + } +} diff --git a/src/main/groovy/com/palantir/gradle/docker/DockerComposePlugin.groovy b/src/main/groovy/com/palantir/gradle/docker/DockerComposePlugin.groovy new file mode 100644 index 00000000..fa726d1e --- /dev/null +++ b/src/main/groovy/com/palantir/gradle/docker/DockerComposePlugin.groovy @@ -0,0 +1,51 @@ +package com.palantir.gradle.docker + +import com.google.common.base.Preconditions +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.Copy + +class DockerComposePlugin implements Plugin { + @Override + void apply(Project project) { + DockerComposeExtension ext = + project.extensions.create('dockerCompose', DockerComposeExtension, project) + project.configurations.create("docker") + + Copy generateDockerCompose = project.tasks.create('generateDockerCompose', Copy, { + description = 'Populates docker-compose.yml.template file with image versions specified by "docker" ' + + 'dependencies' + }) + + project.afterEvaluate { + ext.resolvePathsAndValidate() + if (ext.resolvedDockerComposeTemplate.exists()) { + def dockerDependencies = project.configurations.docker.resolvedConfiguration.resolvedArtifacts + def templateTokens = dockerDependencies.collectEntries { + def version = it.moduleVersion.id + [("{{${version.group}:${version.name}}}"): version.version] + } + + generateDockerCompose.with { + from(ext.resolvedDockerComposeTemplate) + into(ext.resolvedDockerComposeFile.parentFile) + rename { fileName -> + fileName.replace( + ext.resolvedDockerComposeTemplate.name, ext.resolvedDockerComposeFile.name) + } + filter { String line -> replaceAll(line, templateTokens, ext) } + } + } + } + } + + /** Replaces all occurrences of templatesTokens's keys by their corresponding values in the given line. */ + static def replaceAll(String line, Map templateTokens, DockerComposeExtension ext) { + templateTokens.each { mapping -> line = line.replace(mapping.key, mapping.value) } + def unmatchedTokens = line.findAll(/\{\{.*\}\}/) + Preconditions.checkState(unmatchedTokens.size() == 0, + "Failed to resolve Docker dependencies declared in %s: %s. Known dependencies: %s", + ext.resolvedDockerComposeTemplate, unmatchedTokens, templateTokens) + return line + } +} diff --git a/src/main/groovy/com/palantir/gradle/docker/DockerExtension.groovy b/src/main/groovy/com/palantir/gradle/docker/DockerExtension.groovy index a056614a..ad5c41e9 100644 --- a/src/main/groovy/com/palantir/gradle/docker/DockerExtension.groovy +++ b/src/main/groovy/com/palantir/gradle/docker/DockerExtension.groovy @@ -56,7 +56,7 @@ class DockerExtension { public void setDockerComposeTemplate(String dockerComposeTemplate) { this.dockerComposeTemplate = dockerComposeTemplate Preconditions.checkArgument(project.file(dockerComposeTemplate).exists(), - "Could not find specified dockerComposeTemplate file: %s", project.file(dockerComposeTemplate)) + "Could not find specified template file: %s", project.file(dockerComposeTemplate)) } public void setDockerComposeFile(String dockerComposeFile) { diff --git a/src/main/groovy/com/palantir/gradle/docker/PalantirDockerPlugin.groovy b/src/main/groovy/com/palantir/gradle/docker/PalantirDockerPlugin.groovy index 2a1faa0c..1bb83609 100644 --- a/src/main/groovy/com/palantir/gradle/docker/PalantirDockerPlugin.groovy +++ b/src/main/groovy/com/palantir/gradle/docker/PalantirDockerPlugin.groovy @@ -15,7 +15,6 @@ */ package com.palantir.gradle.docker -import com.google.common.base.Preconditions import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -27,6 +26,7 @@ import org.gradle.api.tasks.Exec import org.gradle.api.tasks.bundling.Zip class PalantirDockerPlugin implements Plugin { + @Override void apply(Project project) { DockerExtension ext = project.extensions.create('docker', DockerExtension, project) project.configurations.create("docker") @@ -52,12 +52,6 @@ class PalantirDockerPlugin implements Plugin { Zip dockerfileZip = project.tasks.create('dockerfileZip', Zip, { description = "Bundles the configured Dockerfile in a ZIP file" - - }) - - Copy generateDockerCompose = project.tasks.create('generateDockerCompose', Copy, { - description = 'Populates docker-compose.yml.template file with image versions specified by "docker" ' + - 'dependencies' }) PublishArtifact dockerArtifact = new ArchivePublishArtifact(dockerfileZip) @@ -96,33 +90,6 @@ class PalantirDockerPlugin implements Plugin { dockerfileZip.with { from(ext.resolvedDockerfile) } - - // Configure docker-compose templating - if (ext.resolvedDockerComposeTemplate.exists()) { - def dockerDependencies = project.configurations.docker.resolvedConfiguration.resolvedArtifacts - def templateTokens = dockerDependencies.collectEntries { - def version = it.moduleVersion.id - [("{{${version.group}:${version.name}}}"): version.version] - } - - generateDockerCompose.with { - from(ext.resolvedDockerComposeTemplate) - into(ext.resolvedDockerComposeFile.parentFile) - rename { fileName -> fileName.replace( - ext.resolvedDockerComposeTemplate.name, ext.resolvedDockerComposeFile.name) } - filter { String line -> replaceAll(line, templateTokens, ext) } - } - } } } - - /** Replaces all occurrences of templatesTokens's keys by their corresponding values in the given line. */ - static def replaceAll(String line, Map templateTokens, DockerExtension ext) { - templateTokens.each { mapping -> line = line.replace(mapping.key, mapping.value) } - def unmatchedTokens = line.findAll(/\{\{.*\}\}/) - Preconditions.checkState(unmatchedTokens.size() == 0, - "Failed to resolve Docker dependencies mention in %s: %s", - ext.resolvedDockerComposeTemplate, unmatchedTokens) - line - } } diff --git a/src/main/resources/META-INF/gradle-plugins/com.palantir.docker-compose.properties b/src/main/resources/META-INF/gradle-plugins/com.palantir.docker-compose.properties new file mode 100644 index 00000000..aec23ae8 --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/com.palantir.docker-compose.properties @@ -0,0 +1 @@ +implementation-class=com.palantir.gradle.docker.DockerComposePlugin diff --git a/src/test/groovy/com/palantir/gradle/docker/AbstractPluginTest.groovy b/src/test/groovy/com/palantir/gradle/docker/AbstractPluginTest.groovy new file mode 100644 index 00000000..439c2b5a --- /dev/null +++ b/src/test/groovy/com/palantir/gradle/docker/AbstractPluginTest.groovy @@ -0,0 +1,45 @@ +package com.palantir.gradle.docker + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class AbstractPluginTest extends Specification { + + @Rule + TemporaryFolder temporaryFolder = new TemporaryFolder() + + File projectDir + File buildFile + List pluginClasspath + + GradleRunner with(String... tasks) { + GradleRunner.create() + .withPluginClasspath(pluginClasspath) + .withProjectDir(projectDir) + .withArguments(tasks) + } + + String exec(String task) { + StringBuffer sout = new StringBuffer(), serr = new StringBuffer() + Process proc = task.execute() + proc.consumeProcessOutput(sout, serr) + proc.waitFor() + return sout.toString() + } + + def setup() { + projectDir = temporaryFolder.root + buildFile = temporaryFolder.newFile('build.gradle') + + def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") + if (pluginClasspathResource == null) { + throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") + } + + pluginClasspath = pluginClasspathResource.readLines() + .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths + .collect { new File(it) } + } +} diff --git a/src/test/groovy/com/palantir/gradle/docker/DockerComposePluginTests.groovy b/src/test/groovy/com/palantir/gradle/docker/DockerComposePluginTests.groovy new file mode 100644 index 00000000..2df90692 --- /dev/null +++ b/src/test/groovy/com/palantir/gradle/docker/DockerComposePluginTests.groovy @@ -0,0 +1,118 @@ +package com.palantir.gradle.docker + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome + +class DockerComposePluginTests extends AbstractPluginTest { + + def 'Generates docker-compose.yml from template with version strings replaced'() { + given: + temporaryFolder.newFile('Dockerfile') << "Foo" + temporaryFolder.newFile("docker-compose.yml.template") << ''' + service1: + image: 'repository/service1:{{com.google.guava:guava}}' + service2: + image: 'repository/service2:{{org.slf4j:slf4j-api}}' + '''.stripIndent() + buildFile << ''' + plugins { + id 'com.palantir.docker-compose' + } + + repositories { + jcenter() + } + + dependencies { + docker 'io.dropwizard:dropwizard-jackson:0.8.2' + // transitive dependencies: com.google.guava:guava:18.0, org.slf4j:slf4j-api:1.7.10 + docker 'com.google.guava:guava:17.0' // should bump to 18.0 via the above + } + + '''.stripIndent() + + when: + BuildResult buildResult = with('generateDockerCompose', "--stacktrace").build() + then: + buildResult.task(':generateDockerCompose').outcome == TaskOutcome.SUCCESS + def dockerComposeText = temporaryFolder.root.toPath().resolve("docker-compose.yml").text + dockerComposeText.contains("repository/service1:18.0") + dockerComposeText.contains("repository/service2:1.7.10") + } + + def 'Fails if docker-compose.yml.template has unmatched version tokens'() { + given: + temporaryFolder.newFile('Dockerfile') << "Foo" + temporaryFolder.newFile("docker-compose.yml.template") << ''' + service1: + image: 'repository/service1:{{foo:bar}}' + '''.stripIndent() + buildFile << ''' + plugins { + id 'com.palantir.docker-compose' + } + + repositories { + jcenter() + } + + dependencies { + docker 'com.google.guava:guava:17.0' // should bump to 18.0 via the above + } + + '''.stripIndent() + + when: + BuildResult buildResult = with('generateDockerCompose', '--stacktrace').buildAndFail() + then: + buildResult.standardError.contains("Failed to resolve Docker dependencies declared in") + buildResult.standardError.contains("{{foo:bar}}") + } + + def 'docker-compose template and file can have custom locations'() { + given: + temporaryFolder.newFile('Dockerfile') << "Foo" + new File(temporaryFolder.newFolder("templates"), "customTemplate.yml") << ''' + nothing + '''.stripIndent() + buildFile << ''' + plugins { + id 'com.palantir.docker-compose' + } + + repositories { + jcenter() + } + + dockerCompose { + template 'templates/customTemplate.yml' + dockerComposeFile 'compose-files/customDockerCompose.yml' + } + '''.stripIndent() + + when: + BuildResult buildResult = with('generateDockerCompose', "--stacktrace").build() + then: + buildResult.task(':generateDockerCompose').outcome == TaskOutcome.SUCCESS + temporaryFolder.root.toPath().resolve("compose-files").resolve("customDockerCompose.yml").toFile().exists() + } + + def 'Fails if template is configured but does not exist'() { + given: + temporaryFolder.newFile('Dockerfile') << "Foo" + buildFile << ''' + plugins { + id 'com.palantir.docker-compose' + } + + dockerCompose { + template 'templates/customTemplate.yml' + } + '''.stripIndent() + + when: + BuildResult buildResult = with('generateDockerCompose').buildAndFail() + then: + buildResult.standardError.contains("Could not find specified template file") + } +} diff --git a/src/test/groovy/com/palantir/gradle/docker/PalantirDockerPluginTests.groovy b/src/test/groovy/com/palantir/gradle/docker/PalantirDockerPluginTests.groovy index bca99aeb..2681edd3 100644 --- a/src/test/groovy/com/palantir/gradle/docker/PalantirDockerPluginTests.groovy +++ b/src/test/groovy/com/palantir/gradle/docker/PalantirDockerPluginTests.groovy @@ -19,22 +19,9 @@ import org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepository import org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenFileLocations import org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenSettingsProvider import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder - -import spock.lang.Specification - -class PalantirDockerPluginTests extends Specification { - - @Rule - TemporaryFolder temporaryFolder = new TemporaryFolder() - - File projectDir - File buildFile - List pluginClasspath +class PalantirDockerPluginTests extends AbstractPluginTest { def 'fail when missing docker configuration'() { given: @@ -238,155 +225,4 @@ class PalantirDockerPluginTests extends Specification { ["foogroup", "barmodule", "0.1.2", temporaryFolder.root.name, "2.3.4"].each { dockerPom.contains(it) } !dockerPom.contains("guava") } - - def 'Generates docker-compose.yml from template with version strings replaced'() { - given: - temporaryFolder.newFile('Dockerfile') << "Foo" - temporaryFolder.newFile("docker-compose.yml.template") << ''' - service1: - image: 'repository/service1:{{com.google.guava:guava}}' - service2: - image: 'repository/service2:{{org.slf4j:slf4j-api}}' - '''.stripIndent() - buildFile << ''' - plugins { - id 'com.palantir.docker' - } - - repositories { - jcenter() - } - - docker { - name 'foo' - } - - dependencies { - docker 'io.dropwizard:dropwizard-jackson:0.8.2' - // transitive dependencies: com.google.guava:guava:18.0, org.slf4j:slf4j-api:1.7.10 - docker 'com.google.guava:guava:17.0' // should bump to 18.0 via the above - } - - '''.stripIndent() - - when: - BuildResult buildResult = with('generateDockerCompose', "--stacktrace").build() - then: - buildResult.task(':generateDockerCompose').outcome == TaskOutcome.SUCCESS - def dockerComposeText = temporaryFolder.root.toPath().resolve("docker-compose.yml").text - dockerComposeText.contains("repository/service1:18.0") - dockerComposeText.contains("repository/service2:1.7.10") - } - - def 'Fails if docker-compose.yml.template has unmatched version tokens'() { - given: - temporaryFolder.newFile('Dockerfile') << "Foo" - temporaryFolder.newFile("docker-compose.yml.template") << ''' - service1: - image: 'repository/service1:{{foo:bar}}' - '''.stripIndent() - buildFile << ''' - plugins { - id 'com.palantir.docker' - } - - repositories { - jcenter() - } - - docker { - name 'foo' - } - - dependencies { - docker 'com.google.guava:guava:17.0' // should bump to 18.0 via the above - } - - '''.stripIndent() - - when: - BuildResult buildResult = with('generateDockerCompose', '--stacktrace').buildAndFail() - then: - buildResult.standardError.contains("Failed to resolve Docker dependencies mention in") - buildResult.standardError.contains("{{foo:bar}}") - } - - def 'docker-compose template and file can have custom locations'() { - given: - temporaryFolder.newFile('Dockerfile') << "Foo" - new File(temporaryFolder.newFolder("templates"), "customTemplate.yml") << ''' - nothing - '''.stripIndent() - buildFile << ''' - plugins { - id 'com.palantir.docker' - } - - repositories { - jcenter() - } - - docker { - name 'foo' - dockerComposeTemplate 'templates/customTemplate.yml' - dockerComposeFile 'compose-files/customDockerCompose.yml' - } - '''.stripIndent() - - when: - BuildResult buildResult = with('generateDockerCompose', "--stacktrace").build() - then: - buildResult.task(':generateDockerCompose').outcome == TaskOutcome.SUCCESS - temporaryFolder.root.toPath().resolve("compose-files").resolve("customDockerCompose.yml").toFile().exists() - } - - def 'Fails if dockerComposeTemplate is configured but does not exist'() { - given: - temporaryFolder.newFile('Dockerfile') << "Foo" - buildFile << ''' - plugins { - id 'com.palantir.docker' - } - - docker { - name 'foo' - dockerComposeTemplate 'templates/customTemplate.yml' - } - '''.stripIndent() - - when: - BuildResult buildResult = with('generateDockerCompose').buildAndFail() - then: - buildResult.standardError.contains("Could not find specified dockerComposeTemplate file") - } - - private GradleRunner with(String... tasks) { - GradleRunner.create() - .withPluginClasspath(pluginClasspath) - .withProjectDir(projectDir) - .withArguments(tasks) - } - - private String exec(String task) { - StringBuffer sout = new StringBuffer(), serr = new StringBuffer() - Process proc = task.execute() - proc.consumeProcessOutput(sout, serr) - proc.waitFor() - return sout.toString() - } - - def setup() { - projectDir = temporaryFolder.root - buildFile = temporaryFolder.newFile('build.gradle') - - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines() - .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths - .collect { new File(it) } - } - }