├── .gitattributes
├── plugin
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── docker
│ │ │ │ ├── subdirectory
│ │ │ │ │ └── file-to-be-included.txt
│ │ │ │ └── Dockerfile
│ │ │ ├── env-files
│ │ │ │ └── env-test.properties
│ │ │ └── logback-test.xml
│ │ ├── groovy
│ │ │ └── de
│ │ │ │ └── gesellix
│ │ │ │ └── gradle
│ │ │ │ └── docker
│ │ │ │ ├── tasks
│ │ │ │ ├── TestTask.groovy
│ │ │ │ ├── DockerRmiTaskSpec.groovy
│ │ │ │ ├── DockerKillTaskSpec.groovy
│ │ │ │ ├── DockerStopTaskSpec.groovy
│ │ │ │ ├── DockerPauseTaskSpec.groovy
│ │ │ │ ├── DockerWaitTaskSpec.groovy
│ │ │ │ ├── DockerRestartTaskSpec.groovy
│ │ │ │ ├── DockerStartTaskSpec.groovy
│ │ │ │ ├── DockerUnpauseTaskSpec.groovy
│ │ │ │ ├── DockerTagTaskSpec.groovy
│ │ │ │ ├── DockerServiceRmTaskSpec.groovy
│ │ │ │ ├── DockerVolumeRmTaskSpec.groovy
│ │ │ │ ├── DockerSwarmLeaveTaskSpec.groovy
│ │ │ │ ├── DockerNetworkConnectTaskSpec.groovy
│ │ │ │ ├── DockerNetworkDisconnectTaskSpec.groovy
│ │ │ │ ├── DockerPingTaskSpec.groovy
│ │ │ │ ├── DockerPsTaskSpec.groovy
│ │ │ │ ├── DockerRenameTaskSpec.groovy
│ │ │ │ ├── DockerNetworksTaskSpec.groovy
│ │ │ │ ├── DockerInfoTaskSpec.groovy
│ │ │ │ ├── DockerCopyToContainerTaskSpec.groovy
│ │ │ │ ├── DockerRmTaskSpec.groovy
│ │ │ │ ├── DockerVersionTaskSpec.groovy
│ │ │ │ ├── DockerLogsTaskSpec.groovy
│ │ │ │ ├── DockerSwarmJoinTaskSpec.groovy
│ │ │ │ ├── DockerImagesTaskSpec.groovy
│ │ │ │ ├── DockerCopyFromContainerTaskSpec.groovy
│ │ │ │ ├── DockerInspectContainerTaskSpec.groovy
│ │ │ │ ├── DockerSwarmInitTaskSpec.groovy
│ │ │ │ ├── DockerInspectImageTaskSpec.groovy
│ │ │ │ ├── DockerCommitTaskSpec.groovy
│ │ │ │ ├── DockerPullTaskSpec.groovy
│ │ │ │ ├── DockerVolumeCreateTaskSpec.groovy
│ │ │ │ ├── DockerNetworkRmTaskSpec.groovy
│ │ │ │ ├── DockerNetworkCreateTaskSpec.groovy
│ │ │ │ ├── DockerServiceCreateTaskSpec.groovy
│ │ │ │ ├── DockerVolumesTaskSpec.groovy
│ │ │ │ ├── DockerPushTaskSpec.groovy
│ │ │ │ ├── DockerExecTaskSpec.groovy
│ │ │ │ ├── GenericDockerTaskSpec.groovy
│ │ │ │ ├── DockerCreateTaskSpec.groovy
│ │ │ │ ├── DockerRunTaskSpec.groovy
│ │ │ │ ├── DockerDisposeContainerTaskSpec.groovy
│ │ │ │ ├── DockerBuildTaskFunctionalTest.groovy
│ │ │ │ └── DockerBuildTaskSpec.groovy
│ │ │ │ └── DockerPluginSpec.groovy
│ │ └── java
│ │ │ └── de
│ │ │ └── gesellix
│ │ │ └── gradle
│ │ │ └── docker
│ │ │ └── testutil
│ │ │ └── TestImage.java
│ └── main
│ │ └── java
│ │ └── de
│ │ └── gesellix
│ │ └── gradle
│ │ └── docker
│ │ ├── worker
│ │ ├── BuildcontextArchiverWorkParameters.java
│ │ └── BuildcontextArchiver.java
│ │ ├── tasks
│ │ ├── DockerPingTask.java
│ │ ├── DockerRmiTask.java
│ │ ├── DockerKillTask.java
│ │ ├── DockerStopTask.java
│ │ ├── DockerPauseTask.java
│ │ ├── DockerVolumeRmTask.java
│ │ ├── DockerStartTask.java
│ │ ├── DockerRestartTask.java
│ │ ├── DockerServiceRmTask.java
│ │ ├── DockerUnpauseTask.java
│ │ ├── DockerInfoTask.java
│ │ ├── DockerVersionTask.java
│ │ ├── DockerSwarmLeaveTask.java
│ │ ├── DockerNetworksTask.java
│ │ ├── DockerPsTask.java
│ │ ├── DockerImagesTask.java
│ │ ├── DockerSwarmJoinTask.java
│ │ ├── DockerTagTask.java
│ │ ├── DockerRenameTask.java
│ │ ├── DockerNetworkConnectTask.java
│ │ ├── DockerNetworkDisconnectTask.java
│ │ ├── DockerInspectImageTask.java
│ │ ├── DockerSwarmInitTask.java
│ │ ├── DockerInspectContainerTask.java
│ │ ├── DockerRmTask.java
│ │ ├── DockerVolumesTask.java
│ │ ├── DockerServiceCreateTask.java
│ │ ├── DockerVolumeCreateTask.java
│ │ ├── DockerCopyToContainerTask.java
│ │ ├── DockerCopyFromContainerTask.java
│ │ ├── DockerNetworkRmTask.java
│ │ ├── DockerWaitTask.java
│ │ ├── DockerExecTask.java
│ │ ├── GenericDockerTask.java
│ │ ├── DockerNetworkCreateTask.java
│ │ ├── DockerCommitTask.java
│ │ ├── DockerLogsTask.java
│ │ ├── DockerPullTask.java
│ │ ├── DockerDisposeContainerTask.java
│ │ ├── DockerPushTask.java
│ │ ├── DockerRunTask.java
│ │ └── DockerCreateTask.java
│ │ ├── DockerPlugin.java
│ │ └── DockerPluginExtension.java
└── build.gradle.kts
├── img
├── docker-logo.png
└── gradle-logo.png
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .gitignore
├── .editorconfig
├── settings.gradle.kts
├── gradle.properties
├── .github
├── workflows
│ ├── update-gradle-wrapper.yml
│ ├── publish-test-results.yml
│ ├── ci.yml
│ ├── cd.yml
│ └── release.yml
└── dependabot.yml
├── RELEASE.md
├── LICENSE
├── debug.gradle.kts
├── README.md
├── gradlew.bat
├── CODE_OF_CONDUCT.md
├── supported-api.md
└── gradlew
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | gradlew text eol=lf
--------------------------------------------------------------------------------
/plugin/src/test/resources/docker/subdirectory/file-to-be-included.txt:
--------------------------------------------------------------------------------
1 | hello world
--------------------------------------------------------------------------------
/plugin/src/test/resources/env-files/env-test.properties:
--------------------------------------------------------------------------------
1 | THE_WIND=CAUGHT_IT
2 | FOO=BAR Baz
3 |
--------------------------------------------------------------------------------
/img/docker-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gesellix/gradle-docker-plugin/HEAD/img/docker-logo.png
--------------------------------------------------------------------------------
/img/gradle-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gesellix/gradle-docker-plugin/HEAD/img/gradle-logo.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gesellix/gradle-docker-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | *.iml
4 | build/
5 | out/
6 | .DS_Store
7 | .project
8 | .classpath
9 | .settings
10 | bin
11 | local-plugins
12 |
--------------------------------------------------------------------------------
/plugin/src/test/resources/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM test:build-base
2 | LABEL de.gesellix.gradle-docker-plugin.test="1"
3 | COPY ./subdirectory/file-to-be-included.txt /file-to-be-included.txt
4 | CMD ["true"]
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 | indent_size = 2
8 | indent_style = space
9 | trim_trailing_whitespace = true
10 | continuation_indent_size = 4
11 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "gradle-docker-plugin"
2 | include("plugin")
3 |
4 | // https://docs.gradle.org/current/userguide/toolchains.html#sec:provisioning
5 | plugins {
6 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
7 | }
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
5 | networkTimeout=10000
6 | validateDistributionUrl=true
7 | zipStoreBase=GRADLE_USER_HOME
8 | zipStorePath=wrapper/dists
9 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/TestTask.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import org.gradle.api.model.ObjectFactory
4 | import org.gradle.api.tasks.TaskAction
5 |
6 | import javax.inject.Inject
7 |
8 | class TestTask extends GenericDockerTask {
9 |
10 | @Inject
11 | TestTask(ObjectFactory objectFactory) {
12 | super(objectFactory)
13 | }
14 |
15 | @TaskAction
16 | def run() {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/worker/BuildcontextArchiverWorkParameters.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.worker;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.api.file.RegularFileProperty;
5 | import org.gradle.workers.WorkParameters;
6 |
7 | public interface BuildcontextArchiverWorkParameters extends WorkParameters {
8 |
9 | DirectoryProperty getSourceDirectory();
10 |
11 | RegularFileProperty getArchivedTargetFile();
12 | }
13 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.daemon=true
2 |
3 | group=de.gesellix
4 |
5 | github.package-registry.owner=gesellix
6 | github.package-registry.repository=gradle-docker-plugin
7 | github.package-registry.username=
8 | github.package-registry.password=
9 |
10 | sonatype.snapshot.url=https://oss.sonatype.org/content/repositories/snapshots/
11 | sonatype.staging.url=https://oss.sonatype.org/service/local/staging/deploy/maven2/
12 | sonatype.staging.profile.id=
13 | sonatype.username=
14 | sonatype.password=
15 |
16 | gradle.publish.key=
17 | gradle.publish.secret=
18 |
--------------------------------------------------------------------------------
/plugin/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.github/workflows/update-gradle-wrapper.yml:
--------------------------------------------------------------------------------
1 | name: Update Gradle Wrapper
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # "weekly" https://crontab.guru/every-week
7 | - cron: "0 0 * * 0"
8 |
9 | jobs:
10 | update-gradle-wrapper:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v6
14 | - name: Update Gradle Wrapper
15 | uses: gradle-update/update-gradle-wrapper-action@v2
16 | with:
17 | repo-token: ${{ secrets.GITHUB_TOKEN }}
18 | - uses: gradle/wrapper-validation-action@v3
19 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerRmiTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerRmiTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerRmi', DockerRmiTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.imageId = "4712"
22 |
23 | when:
24 | task.rmi()
25 |
26 | then:
27 | 1 * dockerClient.rmi("4712")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerKillTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerKillTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerKill', DockerKillTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.kill()
25 |
26 | then:
27 | 1 * dockerClient.kill("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerStopTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerStopTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerStop', DockerStopTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.stop()
25 |
26 | then:
27 | 1 * dockerClient.stop("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerPauseTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerPauseTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerPause', DockerPauseTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.pause()
25 |
26 | then:
27 | 1 * dockerClient.pause("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerWaitTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerWaitTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerWait', DockerWaitTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.awaitStop()
25 |
26 | then:
27 | 1 * dockerClient.wait("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerRestartTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerRestartTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerRestart', DockerRestartTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.restart()
25 |
26 | then:
27 | 1 * dockerClient.restart("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerStartTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerStartTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerStart', DockerStartTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.start()
25 |
26 | then:
27 | 1 * dockerClient.startContainer("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerUnpauseTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerUnpauseTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerUnpause', DockerUnpauseTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4711"
22 |
23 | when:
24 | task.unpause()
25 |
26 | then:
27 | 1 * dockerClient.unpause("4711")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | ## Publishing
2 |
3 | Packages are automatically published from the `main` branch to the GitHub Package Registry.
4 | Manually cut releases will be published to both GitHub Package Registry and Maven Central.
5 |
6 | ## Release Workflow
7 |
8 | There are multiple GitHub Action Workflows for the different steps in the package's lifecycle:
9 |
10 | - CI: Builds and checks incoming changes on a pull request
11 | - triggered on every push to a non-default branch
12 | - CD: Publishes the Gradle artifacts to GitHub Package Registry
13 | - triggered only on pushes to the default branch
14 | - Release: Publishes Gradle artifacts to Sonatype and releases them to Maven Central
15 | - triggered on a published GitHub release using the underlying tag as artifact version, e.g. via `git tag -m "$MESSAGE" v$(date +"%Y-%m-%dT%H-%M-%S")`
16 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerTagTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerTagTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerTag', DockerTagTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.imageId = "4711"
22 | task.imageTag = "aTag"
23 |
24 | when:
25 | task.tag()
26 |
27 | then:
28 | 1 * dockerClient.tag("4711", "aTag")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerServiceRmTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerServiceRmTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('rmService', DockerServiceRmTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient and saves result"() {
20 | given:
21 | task.serviceName = "a-service"
22 |
23 | when:
24 | task.rmService()
25 |
26 | then:
27 | 1 * dockerClient.rmService("a-service")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerVolumeRmTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerVolumeRmTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerRmVolume', DockerVolumeRmTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient and saves result"() {
20 | given:
21 | task.configure {
22 | volumeName = "foo"
23 | }
24 |
25 | when:
26 | task.rmVolume()
27 |
28 | then:
29 | 1 * dockerClient.rmVolume("foo")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerSwarmLeaveTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.api.tasks.TaskProvider
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerSwarmLeaveTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('leaveSwarm', DockerSwarmLeaveTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates to dockerClient and saves result"() {
21 | given:
22 | task.force = true
23 |
24 | when:
25 | task.leaveSwarm()
26 |
27 | then:
28 | 1 * dockerClient.leaveSwarm(true)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerPingTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import org.gradle.api.model.ObjectFactory;
5 | import org.gradle.api.tasks.Internal;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerPingTask extends GenericDockerTask {
11 |
12 | private EngineResponseContent result;
13 |
14 | @Internal
15 | public EngineResponseContent getResult() {
16 | return result;
17 | }
18 |
19 | @Inject
20 | public DockerPingTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Ping the docker server");
23 | }
24 |
25 | @TaskAction
26 | public EngineResponseContent ping() {
27 | getLogger().info("docker ping");
28 | return result = getDockerClient().ping();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerRmiTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerRmiTask extends GenericDockerTask {
11 |
12 | private final Property imageId;
13 |
14 | @Input
15 | public Property getImageId() {
16 | return imageId;
17 | }
18 |
19 | @Inject
20 | public DockerRmiTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Remove one or more images");
23 |
24 | imageId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void rmi() {
29 | getLogger().info("docker rmi");
30 | getDockerClient().rmi(getImageId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerNetworkConnectTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerNetworkConnectTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('connectNetwork', DockerNetworkConnectTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient and saves result"() {
20 | given:
21 | task.networkName = "a-network"
22 | task.containerName = "a-container"
23 |
24 | when:
25 | task.connectNetwork()
26 |
27 | then:
28 | 1 * dockerClient.connectNetwork("a-network", "a-container")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerKillTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerKillTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerKillTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Kill a running container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void kill() {
29 | getLogger().info("docker kill");
30 | getDockerClient().kill(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerStopTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerStopTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerStopTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Stop a running container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void stop() {
29 | getLogger().info("docker stop");
30 | getDockerClient().stop(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerPauseTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerPauseTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerPauseTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Pause a running container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void pause() {
29 | getLogger().info("docker pause");
30 | getDockerClient().pause(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerVolumeRmTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerVolumeRmTask extends GenericDockerTask {
11 |
12 | private final Property volumeName;
13 |
14 | @Input
15 | public Property getVolumeName() {
16 | return volumeName;
17 | }
18 |
19 | @Inject
20 | public DockerVolumeRmTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Remove a volume");
23 |
24 | volumeName = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void rmVolume() {
29 | getLogger().info("docker volume rm");
30 | getDockerClient().rmVolume(getVolumeName().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerNetworkDisconnectTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerNetworkDisconnectTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('disconnectNetwork', DockerNetworkDisconnectTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient and saves result"() {
20 | given:
21 | task.networkName = "a-network"
22 | task.containerName = "a-container"
23 |
24 | when:
25 | task.disconnectNetwork()
26 |
27 | then:
28 | 1 * dockerClient.disconnectNetwork("a-network", "a-container")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerPingTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerPingTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('dockerPing', DockerPingTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates to dockerClient and saves result"() {
21 | given:
22 | def response = new EngineResponseContent("OK")
23 |
24 | when:
25 | task.ping()
26 |
27 | then:
28 | 1 * dockerClient.ping() >> response
29 |
30 | and:
31 | task.result == response
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerStartTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerStartTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerStartTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Start a stopped container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void start() {
29 | getLogger().info("docker start");
30 | getDockerClient().startContainer(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerPsTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerPsTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('dockerPs', DockerPsTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates to dockerClient and saves result"() {
21 | given:
22 | def expectedResult = new EngineResponseContent([])
23 |
24 | when:
25 | task.ps()
26 |
27 | then:
28 | 1 * dockerClient.ps() >> expectedResult
29 | and:
30 | task.containers == expectedResult
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerRestartTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerRestartTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerRestartTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Restart a running container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void restart() {
29 | getLogger().info("docker restart");
30 | getDockerClient().restart(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerServiceRmTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerServiceRmTask extends GenericDockerTask {
11 |
12 | private final Property serviceName;
13 |
14 | @Input
15 | public Property getServiceName() {
16 | return serviceName;
17 | }
18 |
19 | @Inject
20 | public DockerServiceRmTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Remove a service");
23 |
24 | serviceName = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void rmService() {
29 | getLogger().info("docker service rm");
30 | getDockerClient().rmService(getServiceName().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerUnpauseTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerUnpauseTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | @Inject
20 | public DockerUnpauseTask(ObjectFactory objectFactory) {
21 | super(objectFactory);
22 | setDescription("Unpause a paused container");
23 |
24 | containerId = objectFactory.property(String.class);
25 | }
26 |
27 | @TaskAction
28 | public void unpause() {
29 | getLogger().info("docker unpause");
30 | getDockerClient().unpause(getContainerId().get());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerRenameTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerRenameTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerRename', DockerRenameTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates rename command to dockerClient and saves result"() {
20 | given:
21 | def containerId = 'oldName'
22 | task.containerId = containerId
23 |
24 | def newName = 'anotherName'
25 | task.newName = newName
26 |
27 | when:
28 | task.rename()
29 |
30 | then:
31 | 1 * dockerClient.rename(containerId, newName)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerNetworksTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerNetworksTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('dockerNetworks', DockerNetworksTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates to dockerClient and saves result"() {
21 | given:
22 | def response = new EngineResponseContent([])
23 |
24 | when:
25 | task.networks()
26 |
27 | then:
28 | 1 * dockerClient.networks() >> response
29 |
30 | and:
31 | task.networks == response
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerInfoTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.SystemInfo;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.tasks.Internal;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 |
11 | public class DockerInfoTask extends GenericDockerTask {
12 |
13 | private EngineResponseContent info;
14 |
15 | @Internal
16 | public EngineResponseContent getInfo() {
17 | return info;
18 | }
19 |
20 | @Inject
21 | public DockerInfoTask(ObjectFactory objectFactory) {
22 | super(objectFactory);
23 | setDescription("Display system-wide information");
24 | }
25 |
26 | @TaskAction
27 | public EngineResponseContent info() {
28 | getLogger().info("docker info");
29 | return info = getDockerClient().info();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerVersionTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.SystemVersion;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.tasks.Internal;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 |
11 | public class DockerVersionTask extends GenericDockerTask {
12 |
13 | private EngineResponseContent version;
14 |
15 | @Internal
16 | public EngineResponseContent getVersion() {
17 | return version;
18 | }
19 |
20 | @Inject
21 | public DockerVersionTask(ObjectFactory objectFactory) {
22 | super(objectFactory);
23 | setDescription("Show the Docker version information");
24 | }
25 |
26 | @TaskAction
27 | public void version() {
28 | getLogger().info("docker version");
29 | version = getDockerClient().version();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerSwarmLeaveTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.Optional;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 |
11 | public class DockerSwarmLeaveTask extends GenericDockerTask {
12 |
13 | private final Property force;
14 |
15 | @Input
16 | @Optional
17 | public Property getForce() {
18 | return force;
19 | }
20 |
21 | @Inject
22 | public DockerSwarmLeaveTask(ObjectFactory objectFactory) {
23 | super(objectFactory);
24 | setDescription("Leave the swarm");
25 |
26 | force = objectFactory.property(Boolean.class);
27 | }
28 |
29 | @TaskAction
30 | public void leaveSwarm() {
31 | getLogger().info("docker swarm leave");
32 | getDockerClient().leaveSwarm(getForce().get());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerNetworksTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.Network;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.tasks.Internal;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 | import java.util.List;
11 |
12 | public class DockerNetworksTask extends GenericDockerTask {
13 |
14 | private EngineResponseContent> networks;
15 |
16 | @Internal
17 | public EngineResponseContent> getNetworks() {
18 | return networks;
19 | }
20 |
21 | @Inject
22 | public DockerNetworksTask(ObjectFactory objectFactory) {
23 | super(objectFactory);
24 | setDescription("Lists all networks");
25 | }
26 |
27 | @TaskAction
28 | public void networks() {
29 | getLogger().info("docker network ls");
30 | networks = getDockerClient().networks();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerPsTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ContainerSummary;
5 |
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.api.tasks.Internal;
8 | import org.gradle.api.tasks.TaskAction;
9 |
10 | import javax.inject.Inject;
11 | import java.util.List;
12 |
13 | public class DockerPsTask extends GenericDockerTask {
14 |
15 | private EngineResponseContent> containers;
16 |
17 | @Internal
18 | public EngineResponseContent> getContainers() {
19 | return containers;
20 | }
21 |
22 | @Inject
23 | public DockerPsTask(ObjectFactory objectFactory) {
24 | super(objectFactory);
25 | setDescription("List containers");
26 | }
27 |
28 | @TaskAction
29 | public void ps() {
30 | getLogger().info("docker ps");
31 | containers = getDockerClient().ps();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerInfoTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.SystemInfo
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerInfoTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerInfo', DockerInfoTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | def response = new EngineResponseContent(new SystemInfo())
24 |
25 | when:
26 | task.info()
27 |
28 | then:
29 | 1 * dockerClient.info() >> response
30 |
31 | and:
32 | task.info == response
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerCopyToContainerTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerCopyToContainerTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerCpToContainer', DockerCopyToContainerTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates archive upload to dockerClient"() {
20 | given:
21 | task.container = "4711"
22 | task.targetPath = "/tmp/."
23 |
24 | def stream = new ByteArrayInputStream('--'.bytes)
25 | task.tarInputStream = stream
26 |
27 | when:
28 | task.copyToContainer()
29 |
30 | then:
31 | 1 * dockerClient.putArchive("4711", "/tmp/.", stream)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerImagesTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ImageSummary;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.tasks.Internal;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 | import java.util.List;
11 |
12 | public class DockerImagesTask extends GenericDockerTask {
13 |
14 | private EngineResponseContent> images;
15 |
16 | @Internal
17 | public EngineResponseContent> getImages() {
18 | return images;
19 | }
20 |
21 | @Inject
22 | public DockerImagesTask(ObjectFactory objectFactory) {
23 | super(objectFactory);
24 | setDescription("List images");
25 | }
26 |
27 | @TaskAction
28 | public EngineResponseContent> images() {
29 | getLogger().info("docker images");
30 | return images = getDockerClient().images();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerRmTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerRmTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerRm', DockerRmTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.containerId = "4712"
22 |
23 | when:
24 | task.rm()
25 |
26 | then:
27 | 1 * dockerClient.rm("4712", [v: 0])
28 | }
29 |
30 | def "allows to removeVolumes"() {
31 | given:
32 | task.containerId = "4712"
33 | task.removeVolumes = true
34 |
35 | when:
36 | task.rm()
37 |
38 | then:
39 | 1 * dockerClient.rm("4712", [v: 1])
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerVersionTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.SystemVersion
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerVersionTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerVersion', DockerVersionTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | def response = new EngineResponseContent(new SystemVersion())
24 |
25 | when:
26 | task.version()
27 |
28 | then:
29 | 1 * dockerClient.version() >> response
30 |
31 | and:
32 | task.version == response
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "github-actions"
9 | directory: "/"
10 | schedule:
11 | interval: "daily"
12 | - package-ecosystem: "gradle"
13 | directory: "/"
14 | schedule:
15 | interval: "daily"
16 | open-pull-requests-limit: 20
17 | groups:
18 | # https://github.blog/2023-08-24-a-faster-way-to-manage-version-updates-with-dependabot/
19 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups
20 | kotlin:
21 | patterns:
22 | - "org.jetbrains.kotlin:*"
23 | okio:
24 | patterns:
25 | - "com.squareup.okio:*"
26 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerLogsTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | import java.time.Duration
8 | import java.time.temporal.ChronoUnit
9 |
10 | class DockerLogsTaskSpec extends Specification {
11 |
12 | def project
13 | def task
14 | def dockerClient = Mock(DockerClient)
15 |
16 | def setup() {
17 | project = ProjectBuilder.builder().build()
18 | task = project.tasks.register('dockerLogs', DockerLogsTask).get()
19 | task.dockerClient = dockerClient
20 | task.logsTimeout = Duration.of(1, ChronoUnit.SECONDS)
21 | }
22 |
23 | def "delegates to dockerClient"() {
24 | given:
25 | task.containerId = "4711"
26 | task.logOptions.put("timestamps", true)
27 |
28 | when:
29 | task.logs()
30 |
31 | then:
32 | 1 * dockerClient.logs("4711",
33 | ["follow": false, "timestamps": true],
34 | _, _)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerSwarmJoinTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.SwarmJoinRequest;
4 | import org.gradle.api.model.ObjectFactory;
5 | import org.gradle.api.provider.Property;
6 | import org.gradle.api.tasks.Input;
7 | import org.gradle.api.tasks.Optional;
8 | import org.gradle.api.tasks.TaskAction;
9 |
10 | import javax.inject.Inject;
11 |
12 | public class DockerSwarmJoinTask extends GenericDockerTask {
13 |
14 | private final Property config;
15 |
16 | @Input
17 | @Optional
18 | public Property getConfig() {
19 | return config;
20 | }
21 |
22 | @Inject
23 | public DockerSwarmJoinTask(ObjectFactory objectFactory) {
24 | super(objectFactory);
25 | setDescription("Join a swarm as a node and/or manager");
26 |
27 | config = objectFactory.property(SwarmJoinRequest.class);
28 | }
29 |
30 | @TaskAction
31 | public void joinSwarm() {
32 | getLogger().info("docker swarm join");
33 | getDockerClient().joinSwarm(getConfig().get());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Tobias Gesellchen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerSwarmJoinTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.remote.api.SwarmJoinRequest
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerSwarmJoinTaskSpec extends Specification {
9 |
10 | def project
11 | DockerSwarmJoinTask task
12 | def dockerClient = Mock(DockerClient)
13 | private SwarmJoinRequest swarmJoinRequest
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('joinSwarm', DockerSwarmJoinTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | swarmJoinRequest = new SwarmJoinRequest(
24 | "0.0.0.0:4500",
25 | null,
26 | null,
27 | ["node1:4500"],
28 | null)
29 | task.config.set(swarmJoinRequest)
30 |
31 | when:
32 | task.joinSwarm()
33 |
34 | then:
35 | 1 * dockerClient.joinSwarm(swarmJoinRequest)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerImagesTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ImageSummary
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerImagesTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerImages', DockerImagesTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | def summary = new ImageSummary(
24 | "image", "parent",
25 | -1, 1, 10, 0,
26 | null, null, null
27 | )
28 |
29 | when:
30 | task.images()
31 |
32 | then:
33 | 1 * dockerClient.images() >> new EngineResponseContent([summary])
34 |
35 | and:
36 | task.images.content.id == ["image"]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerTagTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerTagTask extends GenericDockerTask {
11 |
12 | private final Property imageId;
13 |
14 | @Input
15 | public Property getImageId() {
16 | return imageId;
17 | }
18 |
19 | private final Property imageTag;
20 |
21 | @Input
22 | public Property getImageTag() {
23 | return imageTag;
24 | }
25 |
26 | @Inject
27 | public DockerTagTask(ObjectFactory objectFactory) {
28 | super(objectFactory);
29 | setDescription("Tag an image into a repository");
30 |
31 | imageId = objectFactory.property(String.class);
32 | imageTag = objectFactory.property(String.class);
33 | }
34 |
35 | @TaskAction
36 | public void tag() {
37 | getLogger().info("docker tag");
38 | getDockerClient().tag(getImageId().get(), getImageTag().get());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerCopyFromContainerTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | class DockerCopyFromContainerTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('dockerCpFromContainer', DockerCopyFromContainerTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates archive download from dockerClient and saves result"() {
21 | given:
22 | task.container = "4711"
23 | task.sourcePath = "/file.txt"
24 | def expectedResponse = new EngineResponseContent("file-content")
25 |
26 | when:
27 | task.copyFromContainer()
28 |
29 | then:
30 | 1 * dockerClient.getArchive("4711", "/file.txt") >> expectedResponse
31 | and:
32 | task.content == expectedResponse
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/worker/BuildcontextArchiver.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.worker;
2 |
3 | import de.gesellix.docker.builder.BuildContextBuilder;
4 | import org.gradle.workers.WorkAction;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.io.File;
9 | import java.io.IOException;
10 |
11 | public abstract class BuildcontextArchiver implements WorkAction {
12 |
13 | private static final Logger log = LoggerFactory.getLogger(BuildcontextArchiver.class);
14 |
15 | @Override
16 | public void execute() {
17 | final File sourceDirectory = getParameters().getSourceDirectory().getAsFile().get();
18 | final File targetFile = getParameters().getArchivedTargetFile().getAsFile().get();
19 | log.info("archiving " + sourceDirectory + " into " + targetFile + "...");
20 | targetFile.getParentFile().mkdirs();
21 | try {
22 | BuildContextBuilder.archiveTarFilesRecursively(sourceDirectory, targetFile);
23 | }
24 | catch (IOException e) {
25 | throw new RuntimeException("Archiving failed", e);
26 | }
27 | log.info("archiving finished");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerRenameTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerRenameTask extends GenericDockerTask {
11 |
12 | private final Property containerId;
13 |
14 | @Input
15 | public Property getContainerId() {
16 | return containerId;
17 | }
18 |
19 | private final Property newName;
20 |
21 | @Input
22 | public Property getNewName() {
23 | return newName;
24 | }
25 |
26 | @Inject
27 | public DockerRenameTask(ObjectFactory objectFactory) {
28 | super(objectFactory);
29 | setDescription("Rename an existing container");
30 |
31 | containerId = objectFactory.property(String.class);
32 | newName = objectFactory.property(String.class);
33 | }
34 |
35 | @TaskAction
36 | public void rename() {
37 | getLogger().info("docker rename");
38 | getDockerClient().rename(getContainerId().get(), getNewName().get());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerInspectContainerTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ContainerInspectResponse
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerInspectContainerTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerInspect', DockerInspectContainerTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and returns result"() {
22 | given:
23 | task.containerId = "4711"
24 | def expectedResponse = new EngineResponseContent(new ContainerInspectResponse().tap { id = "123" })
25 |
26 | when:
27 | task.inspect()
28 |
29 | then:
30 | 1 * dockerClient.inspectContainer("4711") >> expectedResponse
31 | and:
32 | task.containerInfo == expectedResponse
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerSwarmInitTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.SwarmInitRequest
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerSwarmInitTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('initSwarm', DockerSwarmInitTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | def swarmConfig = new SwarmInitRequest().tap {
24 | listenAddr = "0.0.0.0:80"
25 | }
26 | task.swarmconfig.set(swarmConfig)
27 |
28 | when:
29 | task.initSwarm()
30 |
31 | then:
32 | 1 * dockerClient.initSwarm(swarmConfig) >> new EngineResponseContent("swarm-result")
33 |
34 | and:
35 | task.response.content == "swarm-result"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerInspectImageTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ImageInspect
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerInspectImageTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerInspect', DockerInspectImageTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and returns result"() {
22 | given:
23 | task.imageId = "my.image:dev"
24 | def inspect = new ImageInspect().tap {
25 | it.id = "sha256:1234"
26 | it.parent = "parent"
27 | }
28 | def expectedResponse = new EngineResponseContent(inspect)
29 |
30 | when:
31 | task.inspect()
32 |
33 | then:
34 | 1 * dockerClient.inspectImage("my.image:dev") >> expectedResponse
35 | and:
36 | task.imageInfo == expectedResponse
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/plugin/src/test/java/de/gesellix/gradle/docker/testutil/TestImage.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.testutil;
2 |
3 | import de.gesellix.docker.client.DockerClient;
4 |
5 | import java.util.Objects;
6 |
7 | public class TestImage {
8 |
9 | private final DockerClient dockerClient;
10 | private final String repository;
11 | private final String tag;
12 | private final boolean isWindows;
13 |
14 | public TestImage(DockerClient dockerClient) {
15 | this.dockerClient = dockerClient;
16 |
17 | this.isWindows = Objects.requireNonNull(dockerClient.version().getContent().getOs()).equalsIgnoreCase("windows");
18 | this.repository = "gesellix/echo-server";
19 | this.tag = "2025-07-27T22-12-00";
20 |
21 | // TODO consider NOT calling prepare inside the constructor
22 | prepare();
23 | }
24 |
25 | public void prepare() {
26 | dockerClient.pull(null, null, getImageName(), getImageTag());
27 | }
28 |
29 | public boolean isWindows() {
30 | return isWindows;
31 | }
32 |
33 | public String getImageWithTag() {
34 | return getImageName() + ":" + getImageTag();
35 | }
36 |
37 | public String getImageName() {
38 | return repository;
39 | }
40 |
41 | public String getImageTag() {
42 | return tag;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerNetworkConnectTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerNetworkConnectTask extends GenericDockerTask {
11 |
12 | private final Property networkName;
13 |
14 | @Input
15 | public Property getNetworkName() {
16 | return networkName;
17 | }
18 |
19 | private final Property containerName;
20 |
21 | @Input
22 | public Property getContainerName() {
23 | return containerName;
24 | }
25 |
26 | @Inject
27 | public DockerNetworkConnectTask(ObjectFactory objectFactory) {
28 | super(objectFactory);
29 | setDescription("Connects a container to a network");
30 |
31 | networkName = objectFactory.property(String.class);
32 | containerName = objectFactory.property(String.class);
33 | }
34 |
35 | @TaskAction
36 | public void connectNetwork() {
37 | getLogger().info("docker network connect");
38 | getDockerClient().connectNetwork(getNetworkName().get(), getContainerName().get());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerNetworkDisconnectTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class DockerNetworkDisconnectTask extends GenericDockerTask {
11 |
12 | private final Property networkName;
13 |
14 | @Input
15 | public Property getNetworkName() {
16 | return networkName;
17 | }
18 |
19 | private final Property containerName;
20 |
21 | @Input
22 | public Property getContainerName() {
23 | return containerName;
24 | }
25 |
26 | @Inject
27 | public DockerNetworkDisconnectTask(ObjectFactory objectFactory) {
28 | super(objectFactory);
29 | setDescription("Disconnects container from a network");
30 |
31 | networkName = objectFactory.property(String.class);
32 | containerName = objectFactory.property(String.class);
33 | }
34 |
35 | @TaskAction
36 | public void disconnectNetwork() {
37 | getLogger().info("docker network disconnect");
38 | getDockerClient().disconnectNetwork(getNetworkName().get(), getContainerName().get());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerInspectImageTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ImageInspect;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Internal;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import javax.inject.Inject;
12 |
13 | public class DockerInspectImageTask extends GenericDockerTask {
14 |
15 | private final Property imageId;
16 |
17 | @Input
18 | public Property getImageId() {
19 | return imageId;
20 | }
21 |
22 | private EngineResponseContent imageInfo;
23 |
24 | @Internal
25 | public EngineResponseContent getImageInfo() {
26 | return imageInfo;
27 | }
28 |
29 | @Inject
30 | public DockerInspectImageTask(ObjectFactory objectFactory) {
31 | super(objectFactory);
32 | setDescription("Return low-level information on image");
33 |
34 | imageId = objectFactory.property(String.class);
35 | }
36 |
37 | @TaskAction
38 | public void inspect() {
39 | getLogger().info("docker inspect");
40 | imageInfo = getDockerClient().inspectImage(getImageId().get());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerCommitTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.Specification
6 |
7 | class DockerCommitTaskSpec extends Specification {
8 |
9 | def project
10 | def task
11 | def dockerClient = Mock(DockerClient)
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | task = project.tasks.register('dockerCommit', DockerCommitTask).get()
16 | task.dockerClient = dockerClient
17 | }
18 |
19 | def "delegates to dockerClient"() {
20 | given:
21 | task.repo = "your.local.repo"
22 | task.tag = "container-changed:1.0"
23 | task.containerId = "a-container"
24 | task.author = "Tue Dissing "
25 | task.comment = "a test"
26 | task.changes.set("change description")
27 | task.pauseContainer = true
28 |
29 | when:
30 | task.commit()
31 |
32 | then:
33 | 1 * dockerClient.commit("a-container", [
34 | repo : 'your.local.repo',
35 | tag : 'container-changed:1.0',
36 | comment: 'a test',
37 | author : 'Tue Dissing ',
38 | changes: "change description",
39 | pause : true
40 | ])
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerPullTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.docker.client.DockerClient
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 |
8 | import java.time.Duration
9 | import java.time.temporal.ChronoUnit
10 |
11 | class DockerPullTaskSpec extends Specification {
12 |
13 | def project
14 | def task
15 | def dockerClient = Mock(DockerClient)
16 |
17 | def setup() {
18 | project = ProjectBuilder.builder().build()
19 | task = project.tasks.register('dockerPull', DockerPullTask).get()
20 | task.dockerClient = dockerClient
21 | task.pullTimeout = Duration.of(1, ChronoUnit.SECONDS)
22 | }
23 |
24 | def "delegates to dockerClient"() {
25 | given:
26 | task.authConfig = new AuthConfig(username: "user", password: "pass")
27 | task.imageName = "imageName"
28 | task.imageTag = "latest"
29 | task.registry = "registry.example.com:4711"
30 |
31 | when:
32 | task.pull()
33 |
34 | then:
35 | 1 * dockerClient.encodeAuthConfig(new AuthConfig(username: "user", password: "pass")) >> "-foo-"
36 | then:
37 | 1 * dockerClient.pull(_, _, "registry.example.com:4711/imageName", "latest", "-foo-")
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerVolumeCreateTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.Volume
6 | import de.gesellix.docker.remote.api.VolumeCreateOptions
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import spock.lang.Specification
9 |
10 | class DockerVolumeCreateTaskSpec extends Specification {
11 |
12 | def project
13 | def task
14 | def dockerClient = Mock(DockerClient)
15 |
16 | def setup() {
17 | project = ProjectBuilder.builder().build()
18 | task = project.tasks.register('dockerCreateVolume', DockerVolumeCreateTask).get()
19 | task.dockerClient = dockerClient
20 | }
21 |
22 | def "delegates to dockerClient and saves result"() {
23 | given:
24 | def config = new VolumeCreateOptions().tap {
25 | it.name = "foo"
26 | }
27 | task.configure {
28 | volumeConfig = config
29 | }
30 | def expectedResult = new EngineResponseContent(new Volume(
31 | "foo", "overlay", "", null, null, null, null, null, null, null
32 | ))
33 |
34 | when:
35 | task.createVolume()
36 |
37 | then:
38 | 1 * dockerClient.createVolume(config) >> expectedResult
39 |
40 | and:
41 | task.response == expectedResult
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerSwarmInitTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.SwarmInitRequest;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Internal;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import javax.inject.Inject;
12 |
13 | public class DockerSwarmInitTask extends GenericDockerTask {
14 |
15 | private final Property swarmconfig;
16 |
17 | @Input
18 | public Property getSwarmconfig() {
19 | return swarmconfig;
20 | }
21 |
22 | private EngineResponseContent response;
23 |
24 | @Internal
25 | public EngineResponseContent getResponse() {
26 | return response;
27 | }
28 |
29 | @Inject
30 | public DockerSwarmInitTask(ObjectFactory objectFactory) {
31 | super(objectFactory);
32 | setDescription("Initialize a swarm");
33 |
34 | swarmconfig = objectFactory.property(SwarmInitRequest.class);
35 | }
36 |
37 | @TaskAction
38 | public EngineResponseContent initSwarm() {
39 | getLogger().info("docker swarm init");
40 | response = getDockerClient().initSwarm(getSwarmconfig().get());
41 | return response;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerInspectContainerTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ContainerInspectResponse;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Internal;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import javax.inject.Inject;
12 |
13 | public class DockerInspectContainerTask extends GenericDockerTask {
14 |
15 | private final Property containerId;
16 |
17 | @Input
18 | public Property getContainerId() {
19 | return containerId;
20 | }
21 |
22 | private EngineResponseContent containerInfo;
23 |
24 | @Internal
25 | public EngineResponseContent getContainerInfo() {
26 | return containerInfo;
27 | }
28 |
29 | @Inject
30 | public DockerInspectContainerTask(ObjectFactory objectFactory) {
31 | super(objectFactory);
32 | setDescription("Return low-level information on a container");
33 |
34 | containerId = objectFactory.property(String.class);
35 | }
36 |
37 | @TaskAction
38 | public void inspect() {
39 | getLogger().info("docker inspect");
40 | containerInfo = getDockerClient().inspectContainer(getContainerId().get());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerRmTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.Optional;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | public class DockerRmTask extends GenericDockerTask {
14 |
15 | private final Property containerId;
16 |
17 | @Input
18 | public Property getContainerId() {
19 | return containerId;
20 | }
21 |
22 | private final Property removeVolumes;
23 |
24 | @Input
25 | @Optional
26 | public Property getRemoveVolumes() {
27 | return removeVolumes;
28 | }
29 |
30 | @Inject
31 | public DockerRmTask(ObjectFactory objectFactory) {
32 | super(objectFactory);
33 | setDescription("Remove one or more containers");
34 |
35 | containerId = objectFactory.property(String.class);
36 | removeVolumes = objectFactory.property(Boolean.class);
37 | removeVolumes.convention(false);
38 | }
39 |
40 | @TaskAction
41 | public void rm() {
42 | getLogger().info("docker rm");
43 | Map query = new HashMap<>(1);
44 | query.put("v", getRemoveVolumes().get() ? 1 : 0);
45 | getDockerClient().rm(getContainerId().get(), query);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/debug.gradle.kts:
--------------------------------------------------------------------------------
1 | import de.gesellix.docker.client.DockerClientImpl
2 |
3 | buildscript {
4 | repositories {
5 | // mavenLocal()
6 | // fun findProperty(s: String) = project.findProperty(s) as String?
7 | // listOf(
8 | // "docker-client/*",
9 | // "gesellix/*"
10 | // ).forEach { repo ->
11 | // maven {
12 | // name = "github"
13 | // setUrl("https://maven.pkg.github.com/$repo")
14 | // credentials {
15 | // username = System.getenv("PACKAGE_REGISTRY_USER") ?: findProperty("github.package-registry.username")
16 | // password = System.getenv("PACKAGE_REGISTRY_TOKEN") ?: findProperty("github.package-registry.password")
17 | // }
18 | // }
19 | // }
20 | mavenCentral()
21 | }
22 |
23 | dependencies {
24 | classpath("de.gesellix:docker-client:[2025-01-01T01-01-01,)")
25 | }
26 | }
27 |
28 | tasks.register("checkDockerAvailability") {
29 | group = "docker"
30 | val client = DockerClientImpl()
31 |
32 | doFirst {
33 | logger.lifecycle("Docker Host:\n|> ${client.env.dockerHost} <|")
34 | }
35 | doLast {
36 | try {
37 | logger.lifecycle("Docker Ping:\n|> ${client.ping().content} <|")
38 | logger.lifecycle("Docker Version:\n|> ${client.version().content} <|")
39 | logger.lifecycle("Docker Info:\n|> ${client.info().content} <|")
40 | } catch (e: Exception) {
41 | logger.warn("Docker Engine not available?", e)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerVolumesTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.VolumeListResponse;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.MapProperty;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Internal;
9 | import org.gradle.api.tasks.Optional;
10 | import org.gradle.api.tasks.TaskAction;
11 |
12 | import javax.inject.Inject;
13 | import java.util.HashMap;
14 |
15 | public class DockerVolumesTask extends GenericDockerTask {
16 |
17 | private final MapProperty query;
18 |
19 | @Input
20 | @Optional
21 | public MapProperty getQuery() {
22 | return query;
23 | }
24 |
25 | private EngineResponseContent volumes;
26 |
27 | @Internal
28 | public EngineResponseContent getVolumes() {
29 | return volumes;
30 | }
31 |
32 | @Inject
33 | public DockerVolumesTask(ObjectFactory objectFactory) {
34 | super(objectFactory);
35 | setDescription("List volumes from all volume drivers");
36 |
37 | query = objectFactory.mapProperty(String.class, Object.class);
38 | }
39 |
40 | @TaskAction
41 | public void volumes() {
42 | getLogger().info("docker volume ls");
43 | volumes = getDockerClient().volumes(new HashMap<>(getQuery().get()));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerServiceCreateTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ServiceCreateRequest;
5 | import de.gesellix.docker.remote.api.ServiceCreateResponse;
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.api.provider.Property;
8 | import org.gradle.api.tasks.Input;
9 | import org.gradle.api.tasks.Internal;
10 | import org.gradle.api.tasks.TaskAction;
11 |
12 | import javax.inject.Inject;
13 |
14 | public class DockerServiceCreateTask extends GenericDockerTask {
15 |
16 | private final Property serviceConfig;
17 |
18 | @Input
19 | public Property getServiceConfig() {
20 | return serviceConfig;
21 | }
22 |
23 | private EngineResponseContent response;
24 |
25 | @Internal
26 | public EngineResponseContent getResponse() {
27 | return response;
28 | }
29 |
30 | @Inject
31 | public DockerServiceCreateTask(ObjectFactory objectFactory) {
32 | super(objectFactory);
33 | setDescription("Create a service");
34 |
35 | serviceConfig = objectFactory.property(ServiceCreateRequest.class);
36 | }
37 |
38 | @TaskAction
39 | public void createService() {
40 | getLogger().info("docker service create");
41 | response = getDockerClient().createService(getServiceConfig().get());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerNetworkRmTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import org.gradle.testfixtures.ProjectBuilder
5 | import spock.lang.FailsWith
6 | import spock.lang.Specification
7 |
8 | class DockerNetworkRmTaskSpec extends Specification {
9 |
10 | def project
11 | def task
12 | def dockerClient = Mock(DockerClient)
13 |
14 | def setup() {
15 | project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('rmNetwork', DockerNetworkRmTask).get()
17 | task.dockerClient = dockerClient
18 | }
19 |
20 | def "delegates to dockerClient and saves result"() {
21 | given:
22 | task.networkName = "a-network"
23 |
24 | when:
25 | task.rmNetwork()
26 |
27 | then:
28 | 1 * dockerClient.rmNetwork("a-network")
29 | }
30 |
31 | @FailsWith(RuntimeException)
32 | def "fails on error"() {
33 | given:
34 | task.networkName = "a-network"
35 |
36 | when:
37 | task.rmNetwork()
38 |
39 | then:
40 | 1 * dockerClient.rmNetwork("a-network") >> { throw new RuntimeException("expected error") }
41 | }
42 |
43 | def "can ignore errors"() {
44 | given:
45 | task.networkName = "a-network"
46 | task.ignoreError = true
47 |
48 | when:
49 | task.rmNetwork()
50 |
51 | then:
52 | 1 * dockerClient.rmNetwork("a-network") >> { throw new RuntimeException("expected error") }
53 | notThrown(Exception)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerVolumeCreateTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.Volume;
5 | import de.gesellix.docker.remote.api.VolumeCreateOptions;
6 |
7 | import org.gradle.api.model.ObjectFactory;
8 | import org.gradle.api.provider.Property;
9 | import org.gradle.api.tasks.Input;
10 | import org.gradle.api.tasks.Internal;
11 | import org.gradle.api.tasks.Optional;
12 | import org.gradle.api.tasks.TaskAction;
13 |
14 | import javax.inject.Inject;
15 |
16 | public class DockerVolumeCreateTask extends GenericDockerTask {
17 |
18 | private final Property volumeConfig;
19 |
20 | @Input
21 | @Optional
22 | public Property getVolumeConfig() {
23 | return volumeConfig;
24 | }
25 |
26 | private EngineResponseContent response;
27 |
28 | @Internal
29 | public EngineResponseContent getResponse() {
30 | return response;
31 | }
32 |
33 | @Inject
34 | public DockerVolumeCreateTask(ObjectFactory objectFactory) {
35 | super(objectFactory);
36 | setDescription("Create a volume");
37 |
38 | volumeConfig = objectFactory.property(VolumeCreateOptions.class);
39 | }
40 |
41 | @TaskAction
42 | public EngineResponseContent createVolume() {
43 | getLogger().info("docker volume create");
44 |
45 | response = getDockerClient().createVolume(volumeConfig.get());
46 | return response;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerCopyToContainerTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import javax.inject.Inject;
9 | import java.io.InputStream;
10 |
11 | public class DockerCopyToContainerTask extends GenericDockerTask {
12 |
13 | private final Property container;
14 |
15 | @Input
16 | public Property getContainer() {
17 | return container;
18 | }
19 |
20 | private final Property targetPath;
21 |
22 | @Input
23 | public Property getTargetPath() {
24 | return targetPath;
25 | }
26 |
27 | private final Property tarInputStream;
28 |
29 | @Input
30 | public Property getTarInputStream() {
31 | return tarInputStream;
32 | }
33 |
34 | @Inject
35 | public DockerCopyToContainerTask(ObjectFactory objectFactory) {
36 | super(objectFactory);
37 | setDescription("Copy files/folders from your host to a container.");
38 |
39 | container = objectFactory.property(String.class);
40 | targetPath = objectFactory.property(String.class);
41 | tarInputStream = objectFactory.property(InputStream.class);
42 | }
43 |
44 | @TaskAction
45 | public void copyToContainer() {
46 | getLogger().info("docker cp to container");
47 | getDockerClient().putArchive(getContainer().get(), getTargetPath().get(), getTarInputStream().get());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerNetworkCreateTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.IPAM
6 | import de.gesellix.docker.remote.api.NetworkCreateRequest
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import spock.lang.Specification
9 |
10 | class DockerNetworkCreateTaskSpec extends Specification {
11 |
12 | def project
13 | def task
14 | def dockerClient = Mock(DockerClient)
15 |
16 | def setup() {
17 | project = ProjectBuilder.builder().build()
18 | task = project.tasks.register('createNetwork', DockerNetworkCreateTask).get()
19 | task.dockerClient = dockerClient
20 | }
21 |
22 | def "delegates to dockerClient and saves result"() {
23 | given:
24 | task.networkName = "a-network"
25 | task.networkConfig = new NetworkCreateRequest(
26 | "a-network", null,
27 | "overlay", null, null, null, null, null, null,
28 | new IPAM("default", null, null),
29 | null, null, null)
30 | def expectedResult = new EngineResponseContent("result")
31 |
32 | when:
33 | task.createNetwork()
34 |
35 | then:
36 | 1 * dockerClient.createNetwork(new NetworkCreateRequest(
37 | "a-network", null,
38 | "overlay", null, null, null, null, null, null,
39 | new IPAM("default", null, null),
40 | null, null, null)) >> expectedResult
41 |
42 | and:
43 | task.response == expectedResult
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerServiceCreateTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ServiceCreateRequest
6 | import de.gesellix.docker.remote.api.ServiceCreateResponse
7 | import de.gesellix.docker.remote.api.TaskSpec
8 | import de.gesellix.docker.remote.api.TaskSpecContainerSpec
9 | import org.gradle.testfixtures.ProjectBuilder
10 | import spock.lang.Specification
11 |
12 | class DockerServiceCreateTaskSpec extends Specification {
13 |
14 | def project
15 | def task
16 | def dockerClient = Mock(DockerClient)
17 |
18 | def setup() {
19 | project = ProjectBuilder.builder().build()
20 | task = project.tasks.register('createService', DockerServiceCreateTask).get()
21 | task.dockerClient = dockerClient
22 | }
23 |
24 | def "delegates to dockerClient and saves result"() {
25 | given:
26 | def response = new EngineResponseContent(new ServiceCreateResponse())
27 | def serviceSpec = new ServiceCreateRequest().tap {
28 | name = "a-service"
29 | taskTemplate = new TaskSpec().tap {
30 | containerSpec = new TaskSpecContainerSpec().tap {
31 | image = "nginx"
32 | }
33 | }
34 | }
35 | task.serviceConfig = serviceSpec
36 |
37 | when:
38 | task.createService()
39 |
40 | then:
41 | 1 * dockerClient.createService(serviceSpec) >> response
42 |
43 | and:
44 | task.response == response
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerVolumesTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.VolumeListResponse
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerVolumesTaskSpec extends Specification {
10 |
11 | def project
12 | def task
13 | def dockerClient = Mock(DockerClient)
14 |
15 | def setup() {
16 | project = ProjectBuilder.builder().build()
17 | task = project.tasks.register('dockerVolumes', DockerVolumesTask).get()
18 | task.dockerClient = dockerClient
19 | }
20 |
21 | def "delegates to dockerClient and saves result"() {
22 | given:
23 | def expectedResult = new EngineResponseContent(new VolumeListResponse(
24 | [], []
25 | ))
26 |
27 | when:
28 | task.volumes()
29 |
30 | then:
31 | 1 * dockerClient.volumes([:]) >> expectedResult
32 |
33 | and:
34 | task.volumes == expectedResult
35 | }
36 |
37 | def "delegates with query to dockerClient and saves result"() {
38 | given:
39 | def expectedResult = new EngineResponseContent(new VolumeListResponse(
40 | [], []
41 | ))
42 |
43 | when:
44 | task.configure {
45 | query = [filters: [dangling: ["true"]]]
46 | }
47 | task.volumes()
48 |
49 | then:
50 | 1 * dockerClient.volumes([filters: [dangling: ["true"]]]) >> expectedResult
51 |
52 | and:
53 | task.volumes == expectedResult
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/DockerPluginSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.gradle.docker.tasks.TestTask
5 | import org.gradle.api.Project
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import spock.lang.Specification
8 |
9 | class DockerPluginSpec extends Specification {
10 |
11 | private Project project
12 |
13 | def setup() {
14 | project = ProjectBuilder.builder().build()
15 | }
16 |
17 | def "DockerPluginExtension is added to project"() {
18 | when:
19 | project.apply plugin: 'de.gesellix.docker'
20 | then:
21 | project["docker"] instanceof DockerPluginExtension
22 | }
23 |
24 | def "configuration is passed to tasks"() {
25 | given:
26 | project.apply plugin: 'de.gesellix.docker'
27 | project.docker.dockerHost = "https://example.org:2376"
28 | project.docker.certPath = 'foo'
29 | project.docker.authConfig = new AuthConfig(username: "plain example")
30 |
31 | when:
32 | def task = project.tasks.register("testTask", TestTask).get()
33 |
34 | then:
35 | task.dockerHost.get() == "https://example.org:2376"
36 | task.certPath.get() == project.file('foo').absolutePath
37 | task.authConfig.get().username == "plain example"
38 | }
39 |
40 | def "returns the absolute certification path"() {
41 | given:
42 | project.apply plugin: 'de.gesellix.docker'
43 |
44 | when:
45 | project.docker.certPath = 'foo'
46 |
47 | then:
48 | project.docker.certPath == project.file('foo').absolutePath
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/DockerPlugin.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker;
2 |
3 | import de.gesellix.gradle.docker.tasks.GenericDockerTask;
4 | import org.gradle.api.Plugin;
5 | import org.gradle.api.Project;
6 |
7 | public class DockerPlugin implements Plugin {
8 |
9 | public static final String EXTENSION_NAME = "docker";
10 |
11 | @Override
12 | public void apply(Project project) {
13 | // project.plugins.apply(BasePlugin)
14 | DockerPluginExtension extension = project.getExtensions().create(EXTENSION_NAME, DockerPluginExtension.class, project);
15 | // ProviderFactory providers = project.getProviders();
16 | // extension.getDockerHost().convention(providers.systemProperty("docker.host")
17 | // .orElse(providers.environmentVariable("DOCKER_HOST"))
18 | // .orElse(new DockerEnv().getDockerHost()));
19 | // extension.getCertPath().convention(providers.systemProperty("docker.cert.path")
20 | // .orElse(providers.environmentVariable("DOCKER_CERT_PATH"))
21 | // .getOrNull());
22 | // extension.getProxy().convention(Proxy.NO_PROXY);
23 | project.getTasks().withType(GenericDockerTask.class).configureEach(task -> {
24 | task.getDockerHost().convention(extension.getDockerHost());
25 | task.getCertPath().convention(extension.getCertPath());
26 | task.getProxy().convention(extension.getProxy());
27 | task.getAuthConfig().convention(extension.getAuthConfig());
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerCopyFromContainerTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import org.gradle.api.model.ObjectFactory;
5 | import org.gradle.api.provider.Property;
6 | import org.gradle.api.tasks.Input;
7 | import org.gradle.api.tasks.Internal;
8 | import org.gradle.api.tasks.TaskAction;
9 |
10 | import javax.inject.Inject;
11 | import java.io.InputStream;
12 |
13 | public class DockerCopyFromContainerTask extends GenericDockerTask {
14 |
15 | private final Property container;
16 |
17 | @Input
18 | public Property getContainer() {
19 | return container;
20 | }
21 |
22 | private final Property sourcePath;
23 |
24 | @Input
25 | public Property getSourcePath() {
26 | return sourcePath;
27 | }
28 |
29 | private EngineResponseContent content;
30 |
31 | @Internal
32 | public EngineResponseContent getContent() {
33 | return content;
34 | }
35 |
36 | @Inject
37 | public DockerCopyFromContainerTask(ObjectFactory objectFactory) {
38 | super(objectFactory);
39 | setDescription("Copy files/folders from a container to your host.");
40 |
41 | container = objectFactory.property(String.class);
42 | sourcePath = objectFactory.property(String.class);
43 | }
44 |
45 | @TaskAction
46 | public EngineResponseContent copyFromContainer() {
47 | getLogger().info("docker cp from container");
48 | return content = getDockerClient().getArchive(getContainer().get(), getSourcePath().get());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerPushTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.docker.client.DockerClient
5 | import org.gradle.testfixtures.ProjectBuilder
6 | import spock.lang.Specification
7 | import spock.lang.Unroll
8 |
9 | import java.time.Duration
10 | import java.time.temporal.ChronoUnit
11 |
12 | class DockerPushTaskSpec extends Specification {
13 |
14 | def project
15 | def task
16 | def dockerClient = Mock(DockerClient)
17 |
18 | def setup() {
19 | project = ProjectBuilder.builder().build()
20 | task = project.tasks.register('dockerPush', DockerPushTask).get()
21 | task.dockerClient = dockerClient
22 | task.pushTimeout = Duration.of(1, ChronoUnit.SECONDS)
23 | }
24 |
25 | @Unroll
26 | def "delegates to dockerClient with registry=#registry"() {
27 | given:
28 | def authDetails = new AuthConfig("username": "gesellix",
29 | "password": "-yet-another-password-",
30 | "email": "tobias@gesellix.de",
31 | "serveraddress": "https://index.docker.io/v1/")
32 | task.repositoryName = "repositoryName"
33 | task.registry = registry
34 | task.authConfig = authDetails
35 | // task.authConfigEncoded = "--auth.base64--"
36 |
37 | when:
38 | task.push()
39 |
40 | then:
41 | 1 * dockerClient.encodeAuthConfig(authDetails) >> "--auth.base64--"
42 |
43 | then:
44 | 1 * dockerClient.push(_, _, "repositoryName", "--auth.base64--", registry)
45 |
46 | where:
47 | registry << [null, "registry.docker.io"]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerNetworkRmTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import org.gradle.api.model.ObjectFactory;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.api.tasks.Input;
6 | import org.gradle.api.tasks.Optional;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import javax.inject.Inject;
10 |
11 | public class DockerNetworkRmTask extends GenericDockerTask {
12 |
13 | private final Property networkName;
14 |
15 | @Input
16 | public Property getNetworkName() {
17 | return networkName;
18 | }
19 |
20 | private final Property ignoreError;
21 |
22 | @Input
23 | @Optional
24 | public Property getIgnoreError() {
25 | return ignoreError;
26 | }
27 |
28 | @Inject
29 | public DockerNetworkRmTask(ObjectFactory objectFactory) {
30 | super(objectFactory);
31 | setDescription("Remove a network");
32 |
33 | networkName = objectFactory.property(String.class);
34 | ignoreError = objectFactory.property(Boolean.class);
35 | ignoreError.convention(false);
36 | }
37 |
38 | @TaskAction
39 | public void rmNetwork() {
40 | getLogger().info("docker network rm");
41 |
42 | try {
43 | getDockerClient().rmNetwork(getNetworkName().get());
44 | }
45 | catch (Exception e) {
46 | if (!ignoreError.get()) {
47 | throw new RuntimeException(e);
48 | }
49 | else {
50 | if (getLogger().isInfoEnabled()) {
51 | getLogger().warn("docker network rm " + getNetworkName().get() + " failed", e);
52 | }
53 | else {
54 | getLogger().warn("docker network rm " + getNetworkName().get() + " failed");
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.github/workflows/publish-test-results.yml:
--------------------------------------------------------------------------------
1 | name: Publish Test results
2 |
3 | # WARNING:
4 | # workflow_run provides read-write repo token and access to secrets.
5 | # Do *not* merge changes to this file without the proper review.
6 | # We should only be running trusted code here.
7 | # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
8 | # Docs: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
9 | on:
10 | workflow_run:
11 | workflows:
12 | - CI
13 | - Publish
14 | - Release
15 | types:
16 | - completed
17 | permissions: {}
18 |
19 | jobs:
20 | # Job based on
21 | # - https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
22 | # - https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
23 | publish-test-results:
24 | runs-on: ubuntu-latest
25 | if: github.event.workflow_run.conclusion != 'skipped'
26 |
27 | permissions:
28 | checks: write
29 | # needed unless run with comment_mode: off
30 | pull-requests: write
31 | # only needed for private repository
32 | #contents: read
33 | # only needed for private repository
34 | #issues: read
35 | # required by download step to access artifacts API
36 | actions: read
37 |
38 | steps:
39 | - name: Download and Extract Artifacts
40 | uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5
41 | with:
42 | run_id: ${{ github.event.workflow_run.id }}
43 | path: artifacts
44 | - name: Publish Test Results
45 | uses: EnricoMi/publish-unit-test-result-action@v2
46 | with:
47 | commit: ${{ github.event.workflow_run.head_sha }}
48 | event_file: artifacts/event-file/event.json
49 | event_name: ${{ github.event.workflow_run.event }}
50 | files: "artifacts/**/build/test-results/test/TEST-*.xml"
51 | ...
52 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: CI
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches-ignore:
7 | - main
8 | jobs:
9 | event-file:
10 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
11 | name: "Event File"
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Upload
15 | uses: actions/upload-artifact@v6
16 | with:
17 | name: event-file
18 | path: ${{ github.event_path }}
19 | ci-build:
20 | strategy:
21 | matrix:
22 | os:
23 | - ubuntu-latest
24 | - windows-latest
25 | - macos-15-intel
26 | java:
27 | - '21'
28 | runs-on: ${{ matrix.os }}
29 | timeout-minutes: 20
30 | steps:
31 | - uses: actions/checkout@v6
32 | with:
33 | fetch-depth: 1
34 | - name: Set up JDK
35 | uses: actions/setup-java@v5.1.0
36 | with:
37 | distribution: 'zulu'
38 | java-version: ${{ matrix.java }}
39 | - name: Setup Gradle
40 | uses: gradle/actions/setup-gradle@v5
41 | - name: Install Docker on macOS
42 | if: matrix.os == 'macos-15-intel'
43 | uses: douglascamata/setup-docker-macos-action@v1.0.2
44 | - name: Login to Docker Hub
45 | uses: docker/login-action@v3
46 | with:
47 | username: ${{ secrets.DOCKERHUB_USERNAME }}
48 | password: ${{ secrets.DOCKERHUB_TOKEN }}
49 | # - name: Debug
50 | # run: ./gradlew checkDockerAvailability --info --stacktrace
51 | - name: clean build
52 | run: ./gradlew clean build --no-daemon --info --stacktrace
53 | - name: Upload Test Results
54 | # see publish-test-results.yml for workflow that publishes test results without security issues for forks
55 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
56 | if: always()
57 | uses: actions/upload-artifact@v6
58 | with:
59 | name: Test Results (Java ${{ matrix.java }} on ${{ matrix.os }})
60 | path: '**/build/test-results/test/TEST-*.xml'
61 | ...
62 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerWaitTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.ContainerWaitResponse;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Internal;
9 | import org.gradle.api.tasks.Optional;
10 | import org.gradle.api.tasks.TaskAction;
11 |
12 | import javax.inject.Inject;
13 |
14 | public class DockerWaitTask extends GenericDockerTask {
15 |
16 | private final Property containerId;
17 |
18 | @Input
19 | public Property getContainerId() {
20 | return containerId;
21 | }
22 |
23 | private final Property ignoreError;
24 |
25 | @Input
26 | @Optional
27 | public Property getIgnoreError() {
28 | return ignoreError;
29 | }
30 |
31 | private EngineResponseContent result;
32 |
33 | @Internal
34 | public EngineResponseContent getResult() {
35 | return result;
36 | }
37 |
38 | @Inject
39 | public DockerWaitTask(ObjectFactory objectFactory) {
40 | super(objectFactory);
41 | setDescription("Block until a container stops, then print its exit code.");
42 |
43 | containerId = objectFactory.property(String.class);
44 | ignoreError = objectFactory.property(Boolean.class);
45 | ignoreError.convention(false);
46 | }
47 |
48 | @TaskAction
49 | public EngineResponseContent awaitStop() {
50 | getLogger().info("docker wait");
51 |
52 | try {
53 | result = getDockerClient().wait(getContainerId().get());
54 | }
55 | catch (Exception e) {
56 | if (!ignoreError.get()) {
57 | throw new RuntimeException(e);
58 | }
59 | else {
60 | if (getLogger().isInfoEnabled()) {
61 | getLogger().warn("docker container wait " + getContainerId().get() + " failed", e);
62 | }
63 | else {
64 | getLogger().warn("docker container wait " + getContainerId().get() + " failed");
65 | }
66 | }
67 | }
68 | return result;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerExecTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.ExecConfig;
4 | import de.gesellix.docker.remote.api.ExecStartConfig;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.ListProperty;
7 | import org.gradle.api.provider.Property;
8 | import org.gradle.api.tasks.Input;
9 | import org.gradle.api.tasks.Optional;
10 | import org.gradle.api.tasks.TaskAction;
11 |
12 | import javax.inject.Inject;
13 | import java.util.Arrays;
14 | import java.util.List;
15 |
16 | public class DockerExecTask extends GenericDockerTask {
17 |
18 | private final Property containerId;
19 |
20 | @Input
21 | public Property getContainerId() {
22 | return containerId;
23 | }
24 |
25 | private final ListProperty cmds;
26 |
27 | @Input
28 | @Optional
29 | public ListProperty getCmds() {
30 | return cmds;
31 | }
32 |
33 | private final Property cmd;
34 |
35 | @Input
36 | @Optional
37 | public Property getCmd() {
38 | return cmd;
39 | }
40 |
41 | @Inject
42 | public DockerExecTask(ObjectFactory objectFactory) {
43 | super(objectFactory);
44 | setDescription("Run a command in a running container");
45 |
46 | containerId = objectFactory.property(String.class);
47 | cmds = objectFactory.listProperty(String.class);
48 | cmd = objectFactory.property(String.class);
49 | }
50 |
51 | @TaskAction
52 | public void exec() {
53 | getLogger().info("docker exec");
54 |
55 | List commandline = (!cmds.get().isEmpty()) ? cmds.get() : Arrays.asList("sh", "-c", cmd.getOrNull());
56 | ExecConfig execCreateConfig = new ExecConfig();
57 | execCreateConfig.setAttachStdin(false);
58 | execCreateConfig.setAttachStdout(true);
59 | execCreateConfig.setAttachStderr(true);
60 | execCreateConfig.setTty(false);
61 | execCreateConfig.setCmd(commandline);
62 | getLogger().debug("exec cmd: '" + execCreateConfig.getCmd() + "'");
63 | String execId = getDockerClient().createExec(containerId.get(), execCreateConfig).getContent().getId();
64 |
65 | ExecStartConfig execStartConfig = new ExecStartConfig(false, false, null);
66 | getDockerClient().startExec(execId, execStartConfig, null, null);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Publish
3 | on:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | event-file:
9 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
10 | name: "Event File"
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Upload
14 | uses: actions/upload-artifact@v6
15 | with:
16 | name: event-file
17 | path: ${{ github.event_path }}
18 | publish:
19 | strategy:
20 | matrix:
21 | os:
22 | - ubuntu-latest
23 | java:
24 | - '21'
25 | runs-on: ${{ matrix.os }}
26 | timeout-minutes: 20
27 | steps:
28 | - uses: actions/checkout@v6
29 | with:
30 | fetch-depth: 1
31 | - name: Set up JDK
32 | uses: actions/setup-java@v5.1.0
33 | with:
34 | distribution: 'zulu'
35 | java-version: ${{ matrix.java }}
36 | - name: Setup Gradle
37 | uses: gradle/actions/setup-gradle@v5
38 | with:
39 | dependency-graph: generate-and-submit
40 | dependency-graph-continue-on-failure: false
41 | # - name: Install Docker on macOS
42 | # uses: douglascamata/setup-docker-macos-action@v1-alpha
43 | # - name: Login to Docker Hub
44 | # uses: docker/login-action@v3
45 | # with:
46 | # username: ${{ secrets.DOCKERHUB_USERNAME }}
47 | # password: ${{ secrets.DOCKERHUB_TOKEN }}
48 | - name: build publish
49 | run: ./gradlew clean build publishToGitHubPackages --info --stacktrace
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }}
53 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
54 | - name: Upload Test Results
55 | # see publish-test-results.yml for workflow that publishes test results without security issues for forks
56 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
57 | if: always()
58 | uses: actions/upload-artifact@v6
59 | with:
60 | name: Test Results (Java ${{ matrix.java }} on ${{ matrix.os }})
61 | path: '**/build/test-results/test/TEST-*.xml'
62 | ...
63 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerExecTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ExecConfig
6 | import de.gesellix.docker.remote.api.ExecStartConfig
7 | import de.gesellix.docker.remote.api.IdResponse
8 | import org.gradle.testfixtures.ProjectBuilder
9 | import spock.lang.Specification
10 |
11 | class DockerExecTaskSpec extends Specification {
12 |
13 | def project
14 | def task
15 | def dockerClient = Mock(DockerClient)
16 |
17 | def setup() {
18 | project = ProjectBuilder.builder().build()
19 | task = project.tasks.register('dockerExec', DockerExecTask).get()
20 | task.dockerClient = dockerClient
21 | }
22 |
23 | def "delegates plain exec command via 'sh -c' to dockerClient and saves result"() {
24 | given:
25 | def containerId = 'foo'
26 | task.containerId = containerId
27 |
28 | def commandLine = 'echo "foo" > /bar.txt && cat /bar.txt'
29 | task.cmd = commandLine
30 |
31 | def execConfig = new ExecConfig().tap {
32 | attachStdin = false
33 | attachStdout = true
34 | attachStderr = true
35 | tty = false
36 | cmd = ["sh", "-c", commandLine]
37 | }
38 |
39 | when:
40 | task.exec()
41 |
42 | then:
43 | 1 * dockerClient.createExec(containerId, execConfig) >> new EngineResponseContent(new IdResponse("exec-id"))
44 | 1 * dockerClient.startExec("exec-id", new ExecStartConfig(false, false, null), null, null)
45 | }
46 |
47 | def "delegates exec commands to dockerClient and saves result"() {
48 | given:
49 | def containerId = 'foo'
50 | task.containerId = containerId
51 |
52 | def commands = ['sh', '-c', 'echo "foo" > /baz.txt && cat /baz.txt']
53 | task.cmds = commands
54 |
55 | def execConfig = new ExecConfig().tap {
56 | attachStdin = false
57 | attachStdout = true
58 | attachStderr = true
59 | tty = false
60 | cmd = commands
61 | }
62 |
63 | when:
64 | task.exec()
65 |
66 | then:
67 | 1 * dockerClient.createExec(containerId, execConfig) >> new EngineResponseContent(new IdResponse("exec-id"))
68 | 1 * dockerClient.startExec("exec-id", new ExecStartConfig(false, false, null), null, null)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/DockerPluginExtension.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker;
2 |
3 | import de.gesellix.docker.authentication.AuthConfig;
4 | import org.gradle.api.Project;
5 |
6 | import java.net.Proxy;
7 |
8 | /**
9 | * Provides project extension class for configuring default values for all Docker tasks.
10 | */
11 | public class DockerPluginExtension {
12 |
13 | private final Project project;
14 |
15 | /**
16 | * Docker host url. Will default to the system property {@code docker.host} if that is provided,
17 | * otherwise it will try to use the environment variable {@code DOCKER_HOST}. Failing that it will be
18 | * null.
19 | */
20 | private String dockerHost;
21 |
22 | private String certPath;
23 |
24 | private Proxy proxy;
25 | private AuthConfig authConfig;
26 |
27 | public DockerPluginExtension(Project project) {
28 | this.project = project;
29 | dockerHost = System.getProperty("docker.host", System.getenv("DOCKER_HOST"));
30 | certPath = System.getProperty("docker.cert.path", System.getenv("DOCKER_CERT_PATH"));
31 | }
32 |
33 | /**
34 | * Allows for the setting of the path where certificates can be found.
35 | *
36 | * @param path Any path object that can be resolved via {@code project.file ( )}
37 | */
38 | public void setCertPath(String path) {
39 | this.certPath = path;
40 | }
41 |
42 | /**
43 | * Returns the certificate path as an absolute path to the certificate.
44 | * If {@code certPath} was not configured it will attempt to check for system property
45 | * {@code docker.cert.path} first and then for the {@code DOCKER_CERT_PATH} environment variable.
46 | *
47 | * @return Absolute path as a string or null.
48 | */
49 | public String getCertPath() {
50 | if (certPath != null && !certPath.isEmpty()) {
51 | return project.file(certPath).getAbsolutePath();
52 | }
53 | return null;
54 | }
55 |
56 | public String getDockerHost() {
57 | return dockerHost;
58 | }
59 |
60 | public void setDockerHost(String dockerHost) {
61 | this.dockerHost = dockerHost;
62 | }
63 |
64 | public Proxy getProxy() {
65 | return proxy;
66 | }
67 |
68 | public void setProxy(Proxy proxy) {
69 | this.proxy = proxy;
70 | }
71 |
72 | public AuthConfig getAuthConfig() {
73 | return authConfig;
74 | }
75 |
76 | public void setAuthConfig(AuthConfig authConfig) {
77 | this.authConfig = authConfig;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/gesellix/gradle-docker-plugin/actions)
2 | [](https://search.maven.org/search?q=g:de.gesellix%20AND%20a:gradle-docker-plugin)
3 | [](https://plugins.gradle.org/plugin/de.gesellix.docker)
4 |
5 | # Gradle-Docker-Plugin
6 |
7 | [](https://gradle.org/)
8 | [](https://www.docker.com/)
9 |
10 | Yet another Gradle plugin making it easy for your build scripts to talk to a Docker daemon.
11 | Each task delegates to the [Docker-Client](https://github.com/gesellix/docker-client), which connects
12 | to the Docker remote API via HTTP.
13 |
14 | For basic usage please have a look at the tests or the [example project](https://github.com/gesellix/gradle-docker-plugin-example).
15 |
16 | ## Publishing/Release Workflow
17 |
18 | See RELEASE.md
19 |
20 | ## License
21 |
22 | MIT License
23 |
24 | Copyright 2015-2021 [Tobias Gesellchen](https://www.gesellix.net/) ([@gesellix](https://twitter.com/gesellix))
25 |
26 | Permission is hereby granted, free of charge, to any person obtaining a copy
27 | of this software and associated documentation files (the "Software"), to deal
28 | in the Software without restriction, including without limitation the rights
29 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 | copies of the Software, and to permit persons to whom the Software is
31 | furnished to do so, subject to the following conditions:
32 |
33 | The above copyright notice and this permission notice shall be included in all
34 | copies or substantial portions of the Software.
35 |
36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42 | SOFTWARE.
43 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | groovy3 = "3.0.25"
3 | groovy3Versionrange = "[3,4)"
4 | groovy4 = "4.0.28"
5 | groovy4Versionrange = "[4,)"
6 | junitJupiter = "5.11.4"
7 | junitPlatform = "1.11.4"
8 | kotlin = "2.1.0"
9 | kotlinVersionrange = "[1.6,3)"
10 | logback = "1.3.16"
11 | logbackVersionrange = "[1.2,2)"
12 | moshi = "1.15.2"
13 | moshiVersionrange = "[1.12.0,2)"
14 | okhttp = "5.3.2"
15 | okhttpVersionrange = "[4,5)"
16 | okio = "3.16.2"
17 | okioVersionrange = "[3,4)"
18 | slf4j = "2.0.17"
19 | slf4jVersionrange = "[1.7,3)"
20 |
21 | [libraries]
22 | groovy3 = { module = "org.codehaus.groovy:groovy", version.ref = "groovy3" }
23 | groovy3json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy3" }
24 | groovy4 = { module = "org.apache.groovy:groovy", version.ref = "groovy4" }
25 | groovy4json = { module = "org.apache.groovy:groovy-json", version.ref = "groovy4" }
26 | kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
27 | kotlinCommon = { module = "org.jetbrains.kotlin:kotlin-stdlib-common", version.ref = "kotlin" }
28 | kotlinJdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
29 | kotlinJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
30 | kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
31 | kotlinScriptingJvm = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm", version.ref = "kotlin" }
32 | kotlinStdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
33 | kotlinTest = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
34 | logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
35 | moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
36 | moshiKotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
37 | okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
38 | okhttpMockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
39 | okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
40 | okioJvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" }
41 | slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
42 |
43 | [bundles]
44 | groovy3 = ["groovy3", "groovy3json"]
45 | groovy4 = ["groovy4", "groovy4json"]
46 | kotlin = ["kotlin", "kotlinCommon", "kotlinJdk7", "kotlinJdk8", "kotlinReflect", "kotlinScriptingJvm", "kotlinStdlib", "kotlinTest"]
47 | moshi = ["moshi", "moshiKotlin"]
48 | okhttp = ["okhttp", "okhttpMockwebserver"]
49 | okio = ["okio", "okioJvm"]
50 |
51 | [plugins]
52 | kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
53 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/GenericDockerTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.docker.client.DockerClient
5 | import de.gesellix.docker.client.DockerClientImpl
6 | import de.gesellix.docker.engine.DockerEnv
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import spock.lang.Specification
9 |
10 | class GenericDockerTaskSpec extends Specification {
11 |
12 | def task
13 |
14 | def setup() {
15 | def project = ProjectBuilder.builder().build()
16 | task = project.tasks.register('dockerTask', TestTask).get()
17 | }
18 |
19 | def "creates dockerClient only once"() {
20 | def clientMock = Mock(DockerClient)
21 | given:
22 | task.dockerClient = clientMock
23 |
24 | when:
25 | def dockerClient = task.dockerClient
26 |
27 | then:
28 | dockerClient == clientMock
29 | }
30 |
31 | def "delegates to dockerClient with default dockerHost"() {
32 | when:
33 | DockerClientImpl dockerClient = task.dockerClient
34 |
35 | then:
36 | dockerClient.env.dockerHost in [
37 | // well-known default
38 | DockerEnv.getDefaultDockerHost(),
39 | // context-aware default
40 | new DockerEnv().dockerHost,
41 | // 'DockerEnv.getDefaultDockerHost()' should respect the docker context
42 | "npipe:////./pipe/dockerDesktopLinuxEngine",
43 | // for GitHub, where integration tests use a Colima based Docker engine
44 | "unix:///Users/runner/.colima/default/docker.sock",
45 | // pattern for context-aware default on unix
46 | // "unix://${System.getProperty("user.home")}/.docker/run/docker.sock".toString()
47 | ]
48 | }
49 |
50 | def "delegates to dockerClient with configured dockerHost"() {
51 | when:
52 | task.dockerHost.set("http://example.org:4243")
53 | def dockerClient = task.dockerClient
54 |
55 | then:
56 | dockerClient.env.dockerHost == "http://example.org:4243"
57 | }
58 |
59 | def "delegates to dockerClient with configured certPath"() {
60 | when:
61 | task.certPath.set("/path/to/certs")
62 | def dockerClient = task.dockerClient
63 |
64 | then:
65 | dockerClient.env.certPath.endsWith "/path/to/certs".replaceAll('/', "\\${File.separator}")
66 | }
67 |
68 | def "getAuthConfig with plain AuthConfig"() {
69 | when:
70 | task.authConfig = new AuthConfig(identitytoken: "foo")
71 |
72 | then:
73 | task.getEncodedAuthConfig() == "eyJpZGVudGl0eXRva2VuIjoiZm9vIn0="
74 | }
75 |
76 | def "getAuthConfig without AuthConfig"() {
77 | when:
78 | task.authConfig = null
79 |
80 | then:
81 | task.getEncodedAuthConfig() == ''
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/GenericDockerTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.authentication.AuthConfig;
4 | import de.gesellix.docker.client.DockerClient;
5 | import de.gesellix.docker.client.DockerClientImpl;
6 | import de.gesellix.docker.engine.DockerEnv;
7 | import org.gradle.api.DefaultTask;
8 | import org.gradle.api.model.ObjectFactory;
9 | import org.gradle.api.provider.Property;
10 | import org.gradle.api.tasks.Input;
11 | import org.gradle.api.tasks.Internal;
12 | import org.gradle.api.tasks.Optional;
13 |
14 | import javax.inject.Inject;
15 | import java.net.Proxy;
16 |
17 | import static java.net.Proxy.NO_PROXY;
18 |
19 | public class GenericDockerTask extends DefaultTask {
20 |
21 | Property dockerHost;
22 |
23 | @Input
24 | @Optional
25 | public Property getDockerHost() {
26 | return dockerHost;
27 | }
28 |
29 | Property certPath;
30 |
31 | @Input
32 | @Optional
33 | public Property getCertPath() {
34 | return certPath;
35 | }
36 |
37 | Property proxy;
38 |
39 | @Input
40 | @Optional
41 | public Property getProxy() {
42 | return proxy;
43 | }
44 |
45 | Property authConfig;
46 |
47 | @Input
48 | @Optional
49 | public Property getAuthConfig() {
50 | return authConfig;
51 | }
52 |
53 | @Internal
54 | public String getEncodedAuthConfig() {
55 | return authConfig.map((AuthConfig a) -> getDockerClient().encodeAuthConfig(a)).getOrElse("");
56 | }
57 |
58 | private DockerClient dockerClient;
59 |
60 | void setDockerClient(DockerClient dockerClient) {
61 | this.dockerClient = dockerClient;
62 | }
63 |
64 | @Inject
65 | public GenericDockerTask(ObjectFactory objectFactory) {
66 | dockerHost = objectFactory.property(String.class);
67 | certPath = objectFactory.property(String.class);
68 | proxy = objectFactory.property(Proxy.class);
69 | authConfig = objectFactory.property(AuthConfig.class);
70 | setGroup("Docker");
71 | }
72 |
73 | @Internal
74 | public DockerClient getDockerClient() {
75 | if (dockerClient == null) {
76 | if (dockerHost.isPresent() || certPath.isPresent()) {
77 | DockerEnv dockerEnv = new DockerEnv();
78 | if (dockerHost.isPresent()) {
79 | dockerEnv.setDockerHost(dockerHost.get());
80 | }
81 |
82 | if (certPath.isPresent()) {
83 | dockerEnv.setCertPath(getProject().file(certPath.get()).getAbsolutePath());
84 | }
85 |
86 | dockerClient = new DockerClientImpl(dockerEnv, proxy.getOrElse(NO_PROXY));
87 | }
88 | else {
89 | dockerClient = new DockerClientImpl();
90 | }
91 | }
92 |
93 | return dockerClient;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Release
3 | on:
4 | release:
5 | types:
6 | - released
7 | # - published
8 |
9 | jobs:
10 | event-file:
11 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
12 | name: "Event File"
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Upload
16 | uses: actions/upload-artifact@v6
17 | with:
18 | name: event-file
19 | path: ${{ github.event_path }}
20 | release:
21 | strategy:
22 | matrix:
23 | os:
24 | - ubuntu-latest
25 | java:
26 | - '21'
27 | runs-on: ${{ matrix.os }}
28 | timeout-minutes: 20
29 | steps:
30 | - uses: actions/checkout@v6
31 | with:
32 | fetch-depth: 1
33 | - name: Set up JDK
34 | uses: actions/setup-java@v5.1.0
35 | with:
36 | distribution: 'zulu'
37 | java-version: ${{ matrix.java }}
38 | - name: Setup Gradle
39 | uses: gradle/actions/setup-gradle@v5
40 | # - name: Install Docker on macOS
41 | # uses: douglascamata/setup-docker-macos-action@v1-alpha
42 | # - name: Login to Docker Hub
43 | # uses: docker/login-action@v3
44 | # with:
45 | # username: ${{ secrets.DOCKERHUB_USERNAME }}
46 | # password: ${{ secrets.DOCKERHUB_TOKEN }}
47 | - name: Set artifact version
48 | run: |
49 | echo "RELEASE_VERSION=$(echo '${{ github.event.release.tag_name }}' | sed -e s/^v//)" >> $GITHUB_ENV
50 | - name: build publish
51 | run: ./gradlew -Pgradle.publish.key="${{ secrets.GRADLE_PUBLISH_KEY }}" -Pgradle.publish.secret="${{ secrets.GRADLE_PUBLISH_SECRET }}" clean build publish publishPlugins closeAndReleaseStagingRepositories --info --stacktrace -Pversion="${{ env.RELEASE_VERSION }}"
52 | env:
53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }}
55 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
56 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
57 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
58 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
59 | - name: Upload Test Results
60 | # see publish-test-results.yml for workflow that publishes test results without security issues for forks
61 | # https://github.com/marketplace/actions/publish-test-results#support-fork-repositories-and-dependabot-branches
62 | if: always()
63 | uses: actions/upload-artifact@v6
64 | with:
65 | name: Test Results (Java ${{ matrix.java }} on ${{ matrix.os }})
66 | path: '**/build/test-results/test/TEST-*.xml'
67 | ...
68 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerNetworkCreateTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import java.util.Objects;
4 |
5 | import de.gesellix.docker.client.EngineResponseContent;
6 | import de.gesellix.docker.remote.api.NetworkCreateRequest;
7 | import de.gesellix.docker.remote.api.NetworkCreateResponse;
8 |
9 | import org.gradle.api.model.ObjectFactory;
10 | import org.gradle.api.provider.Property;
11 | import org.gradle.api.tasks.Input;
12 | import org.gradle.api.tasks.Internal;
13 | import org.gradle.api.tasks.Optional;
14 | import org.gradle.api.tasks.TaskAction;
15 |
16 | import javax.inject.Inject;
17 |
18 | public class DockerNetworkCreateTask extends GenericDockerTask {
19 |
20 | private final Property networkName;
21 |
22 | @Input
23 | @Optional
24 | public Property getNetworkName() {
25 | return networkName;
26 | }
27 |
28 | private final Property networkConfig;
29 |
30 | @Input
31 | @Optional
32 | public Property getNetworkConfig() {
33 | return networkConfig;
34 | }
35 |
36 | private EngineResponseContent response;
37 |
38 | @Internal
39 | public EngineResponseContent getResponse() {
40 | return response;
41 | }
42 |
43 | @Inject
44 | public DockerNetworkCreateTask(ObjectFactory objectFactory) {
45 | super(objectFactory);
46 | setDescription("Create a new network");
47 |
48 | networkName = objectFactory.property(String.class);
49 | networkConfig = objectFactory.property(NetworkCreateRequest.class);
50 | }
51 |
52 | @TaskAction
53 | public void createNetwork() {
54 | getLogger().info("docker network create");
55 |
56 | if (networkName.isPresent() && !networkConfig.isPresent()) {
57 | response = getDockerClient().createNetwork(networkName.get());
58 | return;
59 | }
60 |
61 | if (networkName.isPresent() && networkConfig.isPresent()) {
62 | NetworkCreateRequest networkCreateRequest = networkConfig.get();
63 | if (networkCreateRequest.getName() == null) {
64 | networkCreateRequest.setName(networkName.get());
65 | } else {
66 | if (!Objects.equals(networkCreateRequest.getName(), networkName.get())) {
67 | throw new IllegalArgumentException("NetworkName and NetworkConfig are mutually exclusive. Please specify only one of them or keep the network name consistent.");
68 | }
69 | }
70 | response = getDockerClient().createNetwork(networkCreateRequest);
71 | }
72 |
73 | if (!networkName.isPresent() && networkConfig.isPresent()) {
74 | response = getDockerClient().createNetwork(networkConfig.get());
75 | }
76 |
77 | if (!networkName.isPresent() && !networkConfig.isPresent()) {
78 | throw new IllegalArgumentException("Either NetworkName or NetworkConfig must be specified.");
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerCommitTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.remote.api.IdResponse;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Optional;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import javax.inject.Inject;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class DockerCommitTask extends GenericDockerTask {
16 |
17 | private final Property containerId;
18 |
19 | @Input
20 | public Property getContainerId() {
21 | return containerId;
22 | }
23 |
24 | private final Property repo;
25 |
26 | @Input
27 | public Property getRepo() {
28 | return repo;
29 | }
30 |
31 | private final Property tag;
32 |
33 | @Optional
34 | @Input
35 | public Property getTag() {
36 | return tag;
37 | }
38 |
39 | private final Property author;
40 |
41 | @Optional
42 | @Input
43 | public Property getAuthor() {
44 | return author;
45 | }
46 |
47 | private final Property comment;
48 |
49 | @Optional
50 | @Input
51 | public Property getComment() {
52 | return comment;
53 | }
54 |
55 | private final Property pauseContainer;
56 |
57 | @Optional
58 | @Input
59 | public Property getPauseContainer() {
60 | return pauseContainer;
61 | }
62 |
63 | private final Property changes;
64 |
65 | @Optional
66 | @Input
67 | public Property getChanges() {
68 | return changes;
69 | }
70 |
71 | @Inject
72 | public DockerCommitTask(ObjectFactory objectFactory) {
73 | super(objectFactory);
74 | setDescription("Commit changes to a container");
75 |
76 | containerId = objectFactory.property(String.class);
77 | repo = objectFactory.property(String.class);
78 | tag = objectFactory.property(String.class);
79 | author = objectFactory.property(String.class);
80 | comment = objectFactory.property(String.class);
81 | changes = objectFactory.property(String.class);
82 | pauseContainer = objectFactory.property(Boolean.class);
83 | pauseContainer.convention(true);
84 | }
85 |
86 | @TaskAction
87 | public EngineResponseContent commit() {
88 | getLogger().info("docker commit");
89 | Map map = new HashMap<>(6);
90 | map.put("repo", getRepo().get());
91 | map.put("tag", getTag().getOrNull());
92 | map.put("comment", getComment().getOrNull());
93 | map.put("author", getAuthor().getOrNull());
94 | map.put("changes", getChanges().getOrNull());
95 | map.put("pause", getPauseContainer().getOrElse(true));
96 | return getDockerClient().commit(getContainerId().get(), map);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerLogsTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.core.Cancellable;
4 | import de.gesellix.docker.remote.api.core.Frame;
5 | import de.gesellix.docker.remote.api.core.StreamCallback;
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.api.provider.MapProperty;
8 | import org.gradle.api.provider.Property;
9 | import org.gradle.api.tasks.Input;
10 | import org.gradle.api.tasks.Internal;
11 | import org.gradle.api.tasks.Optional;
12 | import org.gradle.api.tasks.TaskAction;
13 |
14 | import javax.inject.Inject;
15 | import java.time.Duration;
16 | import java.time.temporal.ChronoUnit;
17 | import java.util.HashMap;
18 | import java.util.concurrent.CountDownLatch;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | public class DockerLogsTask extends GenericDockerTask {
22 |
23 | private final Property containerId;
24 |
25 | @Input
26 | public Property getContainerId() {
27 | return containerId;
28 | }
29 |
30 | public Duration logsTimeout = Duration.of(10, ChronoUnit.MINUTES);
31 |
32 | @Internal
33 | public Duration getLogsTimeout() {
34 | return logsTimeout;
35 | }
36 |
37 | private final MapProperty logOptions;
38 |
39 | @Input
40 | @Optional
41 | public MapProperty getLogOptions() {
42 | return logOptions;
43 | }
44 |
45 | @Inject
46 | public DockerLogsTask(ObjectFactory objectFactory) {
47 | super(objectFactory);
48 |
49 | setDescription("Fetch the logs of a container");
50 |
51 | containerId = objectFactory.property(String.class);
52 | logOptions = objectFactory.mapProperty(String.class, Object.class);
53 | logOptions.convention(new HashMap<>());
54 | logOptions.put("follow", false);
55 | }
56 |
57 | @TaskAction
58 | public void logs() {
59 | getLogger().info("docker logs {}", containerId.get());
60 |
61 | CountDownLatch logsFinished = new CountDownLatch(1);
62 | StreamCallback callback = new StreamCallback() {
63 | Cancellable cancellable;
64 |
65 | @Override
66 | public void onStarting(Cancellable cancellable) {
67 | this.cancellable = cancellable;
68 | }
69 |
70 | @Override
71 | public void onNext(Frame frame) {
72 | if (frame != null) {
73 | getLogger().info(frame.toString());
74 | }
75 | }
76 |
77 | @Override
78 | public void onFailed(Exception e) {
79 | getLogger().error("failed", e);
80 | logsFinished.countDown();
81 | cancellable.cancel();
82 | }
83 |
84 | @Override
85 | public void onFinished() {
86 | getLogger().info("finished");
87 | logsFinished.countDown();
88 | }
89 | };
90 |
91 | getDockerClient().logs(
92 | containerId.get(),
93 | logOptions.getOrNull(),
94 | callback,
95 | logsTimeout);
96 | try {
97 | getLogger().debug("Following the logs for " + logsTimeout + "...");
98 | logsFinished.await(logsTimeout.toMillis(), TimeUnit.MILLISECONDS);
99 | }
100 | catch (InterruptedException ignored) {
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at tobias@gesellix.de. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerPullTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.CreateImageInfo;
4 | import de.gesellix.docker.remote.api.core.StreamCallback;
5 | import org.gradle.api.GradleException;
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.api.provider.Property;
8 | import org.gradle.api.tasks.Input;
9 | import org.gradle.api.tasks.Internal;
10 | import org.gradle.api.tasks.Optional;
11 | import org.gradle.api.tasks.TaskAction;
12 |
13 | import javax.inject.Inject;
14 | import java.time.Duration;
15 | import java.time.temporal.ChronoUnit;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 | import java.util.concurrent.CountDownLatch;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | public class DockerPullTask extends GenericDockerTask {
22 |
23 | private final Property imageName;
24 |
25 | @Input
26 | public Property getImageName() {
27 | return imageName;
28 | }
29 |
30 | private final Property imageTag;
31 |
32 | @Input
33 | @Optional
34 | public Property getImageTag() {
35 | return imageTag;
36 | }
37 |
38 | private final Property registry;
39 |
40 | @Input
41 | @Optional
42 | public Property getRegistry() {
43 | return registry;
44 | }
45 |
46 | private String imageId;
47 |
48 | @Internal
49 | public String getImageId() {
50 | return imageId;
51 | }
52 |
53 | public Duration pullTimeout = Duration.of(10, ChronoUnit.MINUTES);
54 |
55 | @Internal
56 | public Duration getPullTimeout() {
57 | return pullTimeout;
58 | }
59 |
60 | @Inject
61 | public DockerPullTask(ObjectFactory objectFactory) {
62 | super(objectFactory);
63 | setDescription("Pull an image or a repository from a Docker registry server");
64 |
65 | imageName = objectFactory.property(String.class);
66 | imageTag = objectFactory.property(String.class);
67 | registry = objectFactory.property(String.class);
68 | }
69 |
70 | @TaskAction
71 | public String pull() {
72 | getLogger().info("docker pull");
73 |
74 | String imageName = getImageName()
75 | .map(i -> getRegistry().map(r -> r + "/" + i).getOrElse(i)).get();
76 |
77 | List infos = new ArrayList<>();
78 | CountDownLatch pullFinished = new CountDownLatch(1);
79 |
80 | getDockerClient().pull(
81 | new StreamCallback<>() {
82 | @Override
83 | public void onNext(CreateImageInfo element) {
84 | if (element != null) {
85 | getLogger().info(element.toString());
86 | }
87 | infos.add(element);
88 | }
89 |
90 | @Override
91 | public void onFailed(Exception e) {
92 | pullFinished.countDown();
93 | }
94 |
95 | @Override
96 | public void onFinished() {
97 | pullFinished.countDown();
98 | }
99 | },
100 | pullTimeout,
101 | imageName,
102 | getImageTag().getOrNull(),
103 | getEncodedAuthConfig()
104 | );
105 | try {
106 | pullFinished.await(pullTimeout.toMillis(), TimeUnit.MILLISECONDS);
107 | }
108 | catch (InterruptedException e) {
109 | throw new GradleException("Pull didn't finish before " + pullTimeout, e);
110 | }
111 | imageId = imageName + getImageTag().map(t -> ":" + t).getOrElse("");
112 | return imageId;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerDisposeContainerTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.ContainerInspectResponse;
4 | import de.gesellix.docker.remote.api.core.ClientException;
5 | import org.gradle.api.model.ObjectFactory;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.Optional;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import javax.inject.Inject;
12 | import java.net.HttpURLConnection;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | public class DockerDisposeContainerTask extends GenericDockerTask {
17 |
18 | private final Property containerId;
19 |
20 | @Input
21 | public Property getContainerId() {
22 | return containerId;
23 | }
24 |
25 | private final Property rmiParentImage;
26 |
27 | @Input
28 | @Optional
29 | public Property getRmiParentImage() {
30 | return rmiParentImage;
31 | }
32 |
33 | private final Property rmiParentImageIgnoreError;
34 |
35 | @Input
36 | @Optional
37 | public Property getRmiParentImageIgnoreError() {
38 | return rmiParentImageIgnoreError;
39 | }
40 |
41 | private final Property removeVolumes;
42 |
43 | @Input
44 | @Optional
45 | public Property getRemoveVolumes() {
46 | return removeVolumes;
47 | }
48 |
49 | @Inject
50 | public DockerDisposeContainerTask(ObjectFactory objectFactory) {
51 | super(objectFactory);
52 | setDescription("Stops and removes a container and optionally its parent image");
53 |
54 | containerId = objectFactory.property(String.class);
55 | rmiParentImage = objectFactory.property(Boolean.class);
56 | rmiParentImage.convention(false);
57 | rmiParentImageIgnoreError = objectFactory.property(Boolean.class);
58 | rmiParentImageIgnoreError.convention(false);
59 | removeVolumes = objectFactory.property(Boolean.class);
60 | removeVolumes.convention(false);
61 | }
62 |
63 | @TaskAction
64 | public void dispose() {
65 | getLogger().info("docker dispose");
66 |
67 | String containerId = getContainerId().get();
68 | ContainerInspectResponse containerDetails;
69 | try {
70 | containerDetails = getDockerClient().inspectContainer(containerId).getContent();
71 | } catch (ClientException e) {
72 | if (e.getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
73 | getLogger().info("couldn't dispose container because it doesn't exist");
74 | return;
75 | }
76 | throw e;
77 | }
78 |
79 | getDockerClient().stop(containerId);
80 | getDockerClient().wait(containerId);
81 | Map query = new HashMap<>(1);
82 | query.put("v", getRemoveVolumes().getOrElse(false) ? 1 : 0);
83 | getDockerClient().rm(containerId, query);
84 | if (getRmiParentImage().getOrElse(false)) {
85 | try {
86 | getDockerClient().rmi(containerDetails.getImage());
87 | } catch (Exception e) {
88 | if (!rmiParentImageIgnoreError.get()) {
89 | throw new RuntimeException(e);
90 | } else {
91 | if (getLogger().isInfoEnabled()) {
92 | getLogger().warn("docker image rm " + containerDetails.getImage() + " failed", e);
93 | } else {
94 | getLogger().warn("docker image rm " + containerDetails.getImage() + " failed");
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerCreateTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.remote.api.ContainerCreateRequest
5 | import de.gesellix.docker.remote.api.HostConfig
6 | import de.gesellix.docker.remote.api.PortBinding
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import spock.lang.Specification
9 |
10 | class DockerCreateTaskSpec extends Specification {
11 |
12 | def project
13 | def task
14 | def dockerClient = Mock(DockerClient)
15 |
16 | def setup() {
17 | project = ProjectBuilder.builder().build()
18 | task = project.tasks.register('dockerCreate', DockerCreateTask).get()
19 | task.dockerClient = dockerClient
20 | }
21 |
22 | def "delegates to dockerClient"() {
23 | given:
24 | task.imageName = "anImage"
25 | task.imageTag = "aTag"
26 | task.containerName = "aContainerName"
27 | task.containerConfiguration = new ContainerCreateRequest().tap {
28 | exposedPorts = [
29 | "8889/tcp": [],
30 | "9300/tcp": []
31 | ]
32 | hostConfig = new HostConfig().tap {
33 | portBindings = [
34 | "8889/tcp": [new PortBinding("0.0.0.0", "8889")]
35 | ]
36 | }
37 | }
38 | def containerConfig = new ContainerCreateRequest().tap {
39 | image = "anImage:aTag"
40 | exposedPorts = [
41 | "8889/tcp": [],
42 | "9300/tcp": []
43 | ]
44 | hostConfig = new HostConfig().tap {
45 | portBindings = [
46 | "8889/tcp": [new PortBinding("0.0.0.0", "8889")]
47 | ]
48 | }
49 | }
50 |
51 | when:
52 | task.create()
53 |
54 | then:
55 | 1 * dockerClient.createContainer(containerConfig, "aContainerName", "")
56 | }
57 |
58 | def "parses env-file to containerConfig.Env"() {
59 | URL envfile = getClass().getResource('/env-files/env-test.properties')
60 |
61 | given:
62 | task.imageName = "anImage"
63 | task.containerConfiguration = new ContainerCreateRequest().tap {
64 | hostConfig = new HostConfig().tap { publishAllPorts = true }
65 | }
66 | task.environmentFiles = [new File(envfile.toURI())]
67 | def containerConfig = new ContainerCreateRequest().tap {
68 | env = ['THE_WIND=CAUGHT_IT', 'FOO=BAR Baz']
69 | image = "anImage"
70 | hostConfig = new HostConfig().tap { publishAllPorts = true }
71 | }
72 |
73 | when:
74 | task.create()
75 |
76 | then:
77 | 1 * dockerClient.createContainer(containerConfig, '', '')
78 | }
79 |
80 | def "maps env and port properties to actual containerConfig"() {
81 | given:
82 | task.imageName = "anImage"
83 | task.imageTag = "aTag"
84 | task.containerName = "aContainerName"
85 | task.env = ["foo=bar"]
86 | task.ports = ["8080:80", "8889:8889"]
87 | def containerConfig = new ContainerCreateRequest().tap {
88 | env = ["foo=bar"]
89 | image = "anImage:aTag"
90 | exposedPorts = [
91 | "80/tcp" : [:],
92 | "8889/tcp": [:]
93 | ]
94 | hostConfig = new HostConfig().tap {
95 | portBindings = [
96 | "80/tcp" : [new PortBinding("0.0.0.0", "8080")],
97 | "8889/tcp": [new PortBinding("0.0.0.0", "8889")]
98 | ]
99 | }
100 | }
101 |
102 | when:
103 | task.create()
104 |
105 | then:
106 | 1 * dockerClient.createContainer(containerConfig, "aContainerName", "")
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerPushTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.remote.api.PushImageInfo;
4 | import de.gesellix.docker.remote.api.core.Cancellable;
5 | import de.gesellix.docker.remote.api.core.StreamCallback;
6 | import org.gradle.api.GradleException;
7 | import org.gradle.api.model.ObjectFactory;
8 | import org.gradle.api.provider.Property;
9 | import org.gradle.api.tasks.Input;
10 | import org.gradle.api.tasks.Internal;
11 | import org.gradle.api.tasks.Optional;
12 | import org.gradle.api.tasks.TaskAction;
13 |
14 | import javax.inject.Inject;
15 | import java.time.Duration;
16 | import java.time.temporal.ChronoUnit;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.concurrent.CountDownLatch;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.stream.Collectors;
22 |
23 | public class DockerPushTask extends GenericDockerTask {
24 |
25 | private final Property repositoryName;
26 |
27 | @Input
28 | public Property getRepositoryName() {
29 | return repositoryName;
30 | }
31 |
32 | private final Property registry;
33 |
34 | @Input
35 | @Optional
36 | public Property getRegistry() {
37 | return registry;
38 | }
39 |
40 | public Duration pushTimeout = Duration.of(10, ChronoUnit.MINUTES);
41 |
42 | @Internal
43 | public Duration getPushTimeout() {
44 | return pushTimeout;
45 | }
46 |
47 | @Inject
48 | public DockerPushTask(ObjectFactory objectFactory) {
49 | super(objectFactory);
50 | setDescription("Push an image or a repository to a Docker registry server");
51 |
52 | repositoryName = objectFactory.property(String.class);
53 | registry = objectFactory.property(String.class);
54 | }
55 |
56 | @TaskAction
57 | public void push() {
58 | getLogger().info("docker push");
59 | List infos = new ArrayList<>();
60 | CountDownLatch pushFinished = new CountDownLatch(1);
61 | StreamCallback callback = new StreamCallback<>() {
62 |
63 | private Cancellable cancellable;
64 |
65 | @Override
66 | public void onStarting(Cancellable cancellable) {
67 | this.cancellable = cancellable;
68 | }
69 |
70 | @Override
71 | public void onNext(PushImageInfo element) {
72 | getLogger().info(element != null ? element.toString() : null);
73 | infos.add(element);
74 | }
75 |
76 | @Override
77 | public void onFailed(Exception e) {
78 | getLogger().error("Push failed", e);
79 | pushFinished.countDown();
80 | cancellable.cancel();
81 | }
82 |
83 | @Override
84 | public void onFinished() {
85 | getLogger().info("Push finished");
86 | pushFinished.countDown();
87 | }
88 | };
89 | getDockerClient().push(callback, pushTimeout, getRepositoryName().get(), getEncodedAuthConfig(), getRegistry().getOrNull());
90 | try {
91 | getLogger().debug("Waiting " + pushTimeout + " for the build to finish...");
92 | pushFinished.await(pushTimeout.toMillis(), TimeUnit.MILLISECONDS);
93 |
94 | List errors = infos.stream()
95 | .filter(i -> i.getError() != null)
96 | .collect(Collectors.toList());
97 | if (!errors.isEmpty()) {
98 | throw new GradleException("Push failed: " + errors.stream().findFirst().get());
99 | }
100 | }
101 | catch (InterruptedException e) {
102 | getLogger().error("Push didn't finish before timeout of " + pushFinished, e);
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerRunTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.docker.client.DockerClient
5 | import de.gesellix.docker.remote.api.ContainerCreateRequest
6 | import de.gesellix.docker.remote.api.HostConfig
7 | import de.gesellix.docker.remote.api.PortBinding
8 | import org.gradle.testfixtures.ProjectBuilder
9 | import spock.lang.Specification
10 |
11 | class DockerRunTaskSpec extends Specification {
12 |
13 | def project
14 | def task
15 | def dockerClient = Mock(DockerClient)
16 |
17 | def setup() {
18 | project = ProjectBuilder.builder().build()
19 | task = project.tasks.register('dockerRun', DockerRunTask).get()
20 | task.dockerClient = dockerClient
21 | }
22 |
23 | def "delegates to dockerClient"() {
24 | given:
25 | task.imageName = "anImage"
26 | task.imageTag = "aTag"
27 | task.containerName = "aContainerName"
28 | task.containerConfiguration = new ContainerCreateRequest().tap {
29 | exposedPorts = [
30 | "8889/tcp": [],
31 | "9300/tcp": []]
32 | hostConfig = new HostConfig().tap {
33 | portBindings = ["8889/tcp": [new PortBinding("0.0.0.0", "8889")]]
34 | }
35 | }
36 | def containerConfig = new ContainerCreateRequest().tap {
37 | exposedPorts = [
38 | "8889/tcp": [],
39 | "9300/tcp": []]
40 | hostConfig = new HostConfig().tap {
41 | portBindings = ["8889/tcp": [new PortBinding("0.0.0.0", "8889")]]
42 | }
43 | image = "anImage:aTag"
44 | }
45 |
46 | when:
47 | task.run()
48 |
49 | then:
50 | 1 * dockerClient.run(containerConfig, "aContainerName", "")
51 | }
52 |
53 | def "parses env-file to containerConfig.Env"() {
54 | URL envfile = getClass().getResource('/env-files/env-test.properties')
55 |
56 | given:
57 | task.imageName = "anImage"
58 | task.containerConfiguration = new ContainerCreateRequest().tap {
59 | hostConfig = new HostConfig().tap { publishAllPorts = false }
60 | }
61 | task.environmentFiles = [new File(envfile.toURI())]
62 | def containerConfig = new ContainerCreateRequest().tap {
63 | hostConfig = new HostConfig().tap { publishAllPorts = false }
64 | env = ['THE_WIND=CAUGHT_IT', 'FOO=BAR Baz']
65 | image = "anImage"
66 | }
67 |
68 | when:
69 | task.run()
70 |
71 | then:
72 | 1 * dockerClient.run(containerConfig, "", "")
73 | }
74 |
75 | def "maps env and port properties to actual containerConfig"() {
76 | given:
77 | task.imageName = "anImage"
78 | task.imageTag = "aTag"
79 | task.containerName = "aContainerName"
80 | task.env = ["foo=bar"]
81 | task.ports = ["8080:80", "8889:8889"]
82 | def containerConfig = new ContainerCreateRequest().tap {
83 | env = ["foo=bar"]
84 | image = "anImage:aTag"
85 | exposedPorts = ["80/tcp" : [:],
86 | "8889/tcp": [:]]
87 | hostConfig = new HostConfig().tap {
88 | portBindings = [
89 | "80/tcp" : [new PortBinding("0.0.0.0", "8080")],
90 | "8889/tcp": [new PortBinding("0.0.0.0", "8889")]
91 | ]
92 | }
93 | }
94 |
95 | when:
96 | task.run()
97 |
98 | then:
99 | 1 * dockerClient.run(containerConfig, "aContainerName", '')
100 | }
101 |
102 | def "passes auth config to docker client"() {
103 | given:
104 | task.imageName = "anImage"
105 | task.imageTag = "theTag"
106 | task.containerName = "anotherContainerName"
107 | task.authConfig = new AuthConfig(identitytoken: "token")
108 |
109 | when:
110 | task.run()
111 |
112 | then:
113 | 1 * dockerClient.encodeAuthConfig(new AuthConfig(identitytoken: "token")) >> "encoded-auth"
114 | 1 * dockerClient.run(_, "anotherContainerName", "encoded-auth")
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerDisposeContainerTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClient
4 | import de.gesellix.docker.client.EngineResponseContent
5 | import de.gesellix.docker.remote.api.ContainerInspectResponse
6 | import de.gesellix.docker.remote.api.core.ClientException
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import spock.lang.FailsWith
9 | import spock.lang.Specification
10 |
11 | class DockerDisposeContainerTaskSpec extends Specification {
12 |
13 | def project
14 | def task
15 | def dockerClient = Mock(DockerClient)
16 |
17 | def setup() {
18 | project = ProjectBuilder.builder().build()
19 | task = project.tasks.register('dockerDispose', DockerDisposeContainerTask).get()
20 | task.dockerClient = dockerClient
21 | }
22 |
23 | def "delegates to dockerClient (w/o removing the parent image)"() {
24 | given:
25 | task.containerId = "4712"
26 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse())
27 |
28 | when:
29 | task.dispose()
30 |
31 | then:
32 | 1 * dockerClient.stop("4712")
33 | then:
34 | 1 * dockerClient.wait("4712")
35 | then:
36 | 1 * dockerClient.rm("4712", [v: 0])
37 | and:
38 | 0 * dockerClient.rmi(_)
39 | }
40 |
41 | def "delegates to dockerClient (w/ removing the parent image)"() {
42 | given:
43 | task.containerId = "4712"
44 | task.rmiParentImage = true
45 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse().tap { image = "an-image-id" })
46 |
47 | when:
48 | task.dispose()
49 |
50 | then:
51 | 1 * dockerClient.stop("4712")
52 | then:
53 | 1 * dockerClient.wait("4712")
54 | then:
55 | 1 * dockerClient.rm("4712", [v: 0])
56 | then:
57 | 1 * dockerClient.rmi("an-image-id")
58 | }
59 |
60 | @FailsWith(RuntimeException)
61 | def "fails when removing the parent image has errors"() {
62 | given:
63 | task.containerId = "4712"
64 | task.rmiParentImage = true
65 | // default: false
66 | // task.rmiParentImageIgnoreError = false
67 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse().tap { image = "an-image-id" })
68 |
69 | when:
70 | task.dispose()
71 |
72 | then:
73 | 1 * dockerClient.stop("4712")
74 | then:
75 | 1 * dockerClient.wait("4712")
76 | then:
77 | 1 * dockerClient.rm("4712", [v: 0])
78 | then:
79 | 1 * dockerClient.rmi("an-image-id") >> { throw new RuntimeException("expected error") }
80 | }
81 |
82 | def "can ignore errors when removing the parent image"() {
83 | given:
84 | task.containerId = "4712"
85 | task.rmiParentImage = true
86 | task.rmiParentImageIgnoreError = true
87 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse().tap { image = "an-image-id" })
88 |
89 | when:
90 | task.dispose()
91 |
92 | then:
93 | 1 * dockerClient.stop("4712")
94 | then:
95 | 1 * dockerClient.wait("4712")
96 | then:
97 | 1 * dockerClient.rm("4712", [v: 0])
98 | then:
99 | 1 * dockerClient.rmi("an-image-id") >> { throw new RuntimeException("expected error") }
100 | notThrown(Exception)
101 | }
102 |
103 | def "catches ClientException when container is not present"() {
104 | given:
105 | task.containerId = "4711"
106 |
107 | when:
108 | task.dispose()
109 |
110 | then:
111 | 1 * dockerClient.inspectContainer("4711") >> {
112 | throw new ClientException("foo", 404, null)
113 | }
114 | then:
115 | 0 * dockerClient._
116 | }
117 |
118 | def "allows to removeVolumes"() {
119 | given:
120 | task.containerId = "4712"
121 | task.removeVolumes = true
122 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse())
123 |
124 | when:
125 | task.dispose()
126 |
127 | then:
128 | 1 * dockerClient.rm("4712", [v: 1])
129 | }
130 |
131 | def "does not remove Volumes by default"() {
132 | given:
133 | task.containerId = "4712"
134 | dockerClient.inspectContainer("4712") >> new EngineResponseContent<>(new ContainerInspectResponse())
135 |
136 | when:
137 | task.dispose()
138 |
139 | then:
140 | task.removeVolumes.get() == false
141 | 1 * dockerClient.rm("4712", [v: 0])
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerBuildTaskFunctionalTest.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.client.DockerClientImpl
4 | import de.gesellix.docker.client.LocalDocker
5 | import de.gesellix.gradle.docker.testutil.TestImage
6 | import org.gradle.testkit.runner.GradleRunner
7 | import org.gradle.testkit.runner.TaskOutcome
8 | import spock.lang.Requires
9 | import spock.lang.Specification
10 | import spock.lang.TempDir
11 |
12 | @Requires({ LocalDocker.available() })
13 | class DockerBuildTaskFunctionalTest extends Specification {
14 |
15 | TestImage testImage
16 |
17 | @TempDir
18 | File testProjectDir
19 |
20 | File buildFile
21 |
22 | // Also requires './gradlew :plugin:pluginUnderTestMetadata' to be run before performing the tests.
23 | def setup() {
24 | testImage = new TestImage(new DockerClientImpl())
25 | buildFile = new File(testProjectDir, 'build.gradle')
26 | buildFile << """
27 | plugins {
28 | id 'de.gesellix.docker'
29 | }
30 | """
31 | }
32 |
33 | def "can perform a build configured via config closure"() {
34 | given:
35 | new DockerClientImpl().tag(testImage.imageWithTag, "test:build-base")
36 | URL dockerfile = getClass().getResource('/docker/Dockerfile')
37 | String baseDir = new File(dockerfile.toURI()).parentFile.absolutePath.replaceAll("\\${File.separator}", "/")
38 | String imageName = "gesellix/test-build:${UUID.randomUUID()}"
39 |
40 | buildFile << """
41 | task dockerBuild(type: de.gesellix.gradle.docker.tasks.DockerBuildTask) {
42 | buildContextDirectory.set(new File('$baseDir'))
43 | imageName = '$imageName'
44 | doLast {
45 | logger.lifecycle("Resulting image id: \${imageId}")
46 | }
47 | }
48 | """
49 |
50 | when:
51 | def result = GradleRunner.create()
52 | .withProjectDir(testProjectDir)
53 | .withArguments('dockerBuild', '--info', '--debug', '--stacktrace')
54 | .withPluginClasspath()
55 | .build()
56 |
57 | then:
58 | result.output.contains("Resulting image id: sha256:")
59 | result.task(":dockerBuild").outcome == TaskOutcome.SUCCESS
60 |
61 | cleanup:
62 | new DockerClientImpl().rmi(imageName)
63 | new DockerClientImpl().rmi("test:build-base")
64 | }
65 |
66 | def "can perform a build configured via task property"() {
67 | given:
68 | new DockerClientImpl().tag(testImage.imageWithTag, "test:build-base")
69 | URL dockerfile = getClass().getResource('/docker/Dockerfile')
70 | String baseDir = new File(dockerfile.toURI()).parentFile.absolutePath.replaceAll("\\${File.separator}", "/")
71 | String imageName = "gesellix/test-build:${UUID.randomUUID()}"
72 |
73 | buildFile << """
74 | task dockerBuild(type: de.gesellix.gradle.docker.tasks.DockerBuildTask) {
75 | imageName = '$imageName'
76 | doFirst {
77 | logger.lifecycle("buildContextDirectory: \${buildContextDirectory}")
78 | }
79 | doLast {
80 | logger.lifecycle("Resulting image id: \${imageId}")
81 | }
82 | }
83 | dockerBuild.buildContextDirectory.set(new File('$baseDir'))
84 | """
85 |
86 | when:
87 | def result = GradleRunner.create()
88 | .withProjectDir(testProjectDir)
89 | .withArguments('dockerBuild', '--info', '--debug', '--stacktrace')
90 | .withPluginClasspath()
91 | .withDebug(true)
92 | .build()
93 |
94 | then:
95 | result.output.contains("Resulting image id: sha256:")
96 | result.task(":dockerBuild").outcome == TaskOutcome.SUCCESS
97 |
98 | cleanup:
99 | new DockerClientImpl().rmi(imageName)
100 | new DockerClientImpl().rmi("test:build-base")
101 | }
102 |
103 | def "accepts only task configs with at least one of buildContext or buildContextDirectory"() {
104 | given:
105 | buildFile << """
106 | task dockerBuild(type: de.gesellix.gradle.docker.tasks.DockerBuildTask) {
107 | buildContextDirectory = null
108 | buildContext = null
109 | }
110 | """
111 |
112 | when:
113 | GradleRunner.create()
114 | .withProjectDir(testProjectDir)
115 | .withArguments('dockerBuild')
116 | .withPluginClasspath()
117 | .build()
118 |
119 | then:
120 | Exception exception = thrown()
121 | exception.message.contains("Execution failed for task ':dockerBuild'.")
122 | }
123 |
124 | def "accepts exactly one of buildContext or buildContextDirectory"() {
125 | URL dockerfile = getClass().getResource('/docker/Dockerfile')
126 | String baseDir = new File(dockerfile.toURI()).parentFile.absolutePath.replaceAll("\\${File.separator}", "/")
127 |
128 | given:
129 | buildFile << """
130 | task dockerBuild(type: de.gesellix.gradle.docker.tasks.DockerBuildTask) {
131 | buildContextDirectory.set(new File('$baseDir'))
132 | buildContext = new FileInputStream(File.createTempFile("docker", "test"))
133 | }
134 | """
135 |
136 | when:
137 | GradleRunner.create()
138 | .withProjectDir(testProjectDir)
139 | .withArguments('dockerBuild')
140 | .withPluginClasspath()
141 | .build()
142 |
143 | then:
144 | Exception exception = thrown()
145 | exception.message.contains("Execution failed for task ':dockerBuild'.")
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerRunTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EnvFileParser;
4 | import de.gesellix.docker.remote.api.ContainerCreateRequest;
5 | import de.gesellix.docker.remote.api.HostConfig;
6 | import de.gesellix.docker.remote.api.PortBinding;
7 | import org.gradle.api.model.ObjectFactory;
8 | import org.gradle.api.provider.ListProperty;
9 | import org.gradle.api.provider.Property;
10 | import org.gradle.api.tasks.Input;
11 | import org.gradle.api.tasks.Internal;
12 | import org.gradle.api.tasks.Optional;
13 | import org.gradle.api.tasks.TaskAction;
14 |
15 | import javax.inject.Inject;
16 | import java.io.File;
17 | import java.util.ArrayList;
18 | import java.util.Collections;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.Map;
22 |
23 | public class DockerRunTask extends GenericDockerTask {
24 |
25 | private final Property imageName;
26 |
27 | @Input
28 | public Property getImageName() {
29 | return imageName;
30 | }
31 |
32 | private final Property imageTag;
33 |
34 | @Input
35 | @Optional
36 | public Property getImageTag() {
37 | return imageTag;
38 | }
39 |
40 | private final Property containerName;
41 |
42 | @Input
43 | @Optional
44 | public Property getContainerName() {
45 | return containerName;
46 | }
47 |
48 | private final ListProperty ports;
49 |
50 | /**
51 | * Accepts a list of port mappings with the following pattern: `hostPort:containerPort`.
52 | * More sophisticated patterns are only supported via plain containerConfig.
53 | */
54 | @Input
55 | @Optional
56 | public ListProperty getPorts() {
57 | return ports;
58 | }
59 |
60 | private final Property containerConfiguration;
61 |
62 | @Input
63 | @Optional
64 | public Property getContainerConfiguration() {
65 | return containerConfiguration;
66 | }
67 |
68 | private final ListProperty env;
69 |
70 | @Input
71 | @Optional
72 | public ListProperty getEnv() {
73 | return env;
74 | }
75 |
76 | private final ListProperty environmentFiles;
77 |
78 | @Input
79 | @Optional
80 | public ListProperty getEnvironmentFiles() {
81 | return environmentFiles;
82 | }
83 |
84 | private Object result;
85 |
86 | @Internal
87 | public Object getResult() {
88 | return result;
89 | }
90 |
91 | private final EnvFileParser envFileParser = new EnvFileParser();
92 |
93 | @Inject
94 | public DockerRunTask(ObjectFactory objectFactory) {
95 | super(objectFactory);
96 | setDescription("Run a command in a new container");
97 |
98 | imageName = objectFactory.property(String.class);
99 | imageTag = objectFactory.property(String.class);
100 | imageTag.convention("");
101 | containerName = objectFactory.property(String.class);
102 | containerName.convention("");
103 | ports = objectFactory.listProperty(String.class);
104 | containerConfiguration = objectFactory.property(ContainerCreateRequest.class);
105 | containerConfiguration.convention(new ContainerCreateRequest());
106 | env = objectFactory.listProperty(String.class);
107 | environmentFiles = objectFactory.listProperty(File.class);
108 | }
109 |
110 | @TaskAction
111 | public void run() {
112 | getLogger().info("docker run");
113 |
114 | ContainerCreateRequest containerConfig = getActualContainerConfig();
115 | result = getDockerClient().run(
116 | containerConfig,
117 | getContainerName().getOrElse(""),
118 | getEncodedAuthConfig());
119 | }
120 |
121 | private String getImageNameWithTag() {
122 | if (getImageTag().isPresent() && !getImageTag().get().isEmpty()) {
123 | return getImageName().get() + ":" + getImageTag().get();
124 | }
125 | else {
126 | return getImageName().get();
127 | }
128 | }
129 |
130 | @Internal
131 | public ContainerCreateRequest getActualContainerConfig() {
132 | ContainerCreateRequest containerCreateRequest = getContainerConfiguration().getOrElse(new ContainerCreateRequest());
133 | if (containerCreateRequest.getHostConfig() == null) {
134 | containerCreateRequest.setHostConfig(new HostConfig());
135 | }
136 |
137 | containerCreateRequest.setImage(getImageNameWithTag());
138 | if (!getEnvironmentFiles().get().isEmpty()) {
139 | if (containerCreateRequest.getEnv() == null) {
140 | containerCreateRequest.setEnv(new ArrayList<>());
141 | }
142 | List env = containerCreateRequest.getEnv();
143 | getEnvironmentFiles().get().forEach((File file) -> {
144 | List parsedEnv = envFileParser.parse(file);
145 | env.addAll(parsedEnv);
146 | });
147 | }
148 | if (!getEnv().get().isEmpty()) {
149 | if (containerCreateRequest.getEnv() == null) {
150 | containerCreateRequest.setEnv(new ArrayList<>());
151 | }
152 | List env = containerCreateRequest.getEnv();
153 | env.addAll(getEnv().get());
154 | }
155 |
156 | if (!getPorts().get().isEmpty()) {
157 | if (containerCreateRequest.getExposedPorts() == null) {
158 | containerCreateRequest.setExposedPorts(new HashMap<>());
159 | }
160 | final Map exposedPorts = containerCreateRequest.getExposedPorts();
161 | if (containerCreateRequest.getHostConfig().getPortBindings() == null) {
162 | containerCreateRequest.getHostConfig().setPortBindings(new HashMap<>());
163 | }
164 | final Map> portBindings = containerCreateRequest.getHostConfig().getPortBindings();
165 | getPorts().get().forEach((String portMapping) -> {
166 | // format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
167 | final String[] splittedPortMapping = portMapping.split(":");
168 | if (splittedPortMapping.length != 2) {
169 | throw new UnsupportedOperationException("please use the plain `containerConfig.ExposedPorts and containerConfig.HostConfig.PortBindings` properties");
170 | }
171 | String hostPort = splittedPortMapping[0];
172 | String containerPort = splittedPortMapping[1] + "/tcp";
173 | exposedPorts.put(containerPort, new HashMap<>());
174 |
175 | PortBinding hostBinding = new PortBinding("0.0.0.0", hostPort);
176 | portBindings.put(containerPort, Collections.singletonList(hostBinding));
177 | });
178 | }
179 |
180 | getLogger().info("effective container config: " + containerCreateRequest);
181 | return containerCreateRequest;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/plugin/src/main/java/de/gesellix/gradle/docker/tasks/DockerCreateTask.java:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks;
2 |
3 | import de.gesellix.docker.client.EngineResponseContent;
4 | import de.gesellix.docker.client.EnvFileParser;
5 | import de.gesellix.docker.remote.api.ContainerCreateRequest;
6 | import de.gesellix.docker.remote.api.ContainerCreateResponse;
7 | import de.gesellix.docker.remote.api.HostConfig;
8 | import de.gesellix.docker.remote.api.PortBinding;
9 | import org.gradle.api.model.ObjectFactory;
10 | import org.gradle.api.provider.ListProperty;
11 | import org.gradle.api.provider.Property;
12 | import org.gradle.api.tasks.Input;
13 | import org.gradle.api.tasks.Internal;
14 | import org.gradle.api.tasks.Optional;
15 | import org.gradle.api.tasks.TaskAction;
16 |
17 | import javax.inject.Inject;
18 | import java.io.File;
19 | import java.util.ArrayList;
20 | import java.util.Collections;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | public class DockerCreateTask extends GenericDockerTask {
26 |
27 | private final Property imageName;
28 |
29 | @Input
30 | public Property getImageName() {
31 | return imageName;
32 | }
33 |
34 | private final Property imageTag;
35 |
36 | @Input
37 | @Optional
38 | public Property getImageTag() {
39 | return imageTag;
40 | }
41 |
42 | private final Property containerName;
43 |
44 | @Input
45 | @Optional
46 | public Property getContainerName() {
47 | return containerName;
48 | }
49 |
50 | private final ListProperty ports;
51 |
52 | /**
53 | * Accepts a list of port mappings with the following pattern: `hostPort:containerPort`.
54 | * More sophisticated patterns are only supported via plain containerConfig.
55 | */
56 | @Input
57 | @Optional
58 | public ListProperty getPorts() {
59 | return ports;
60 | }
61 |
62 | private final Property containerConfiguration;
63 |
64 | @Input
65 | @Optional
66 | public Property getContainerConfiguration() {
67 | return containerConfiguration;
68 | }
69 |
70 | private final ListProperty env;
71 |
72 | @Input
73 | @Optional
74 | public ListProperty getEnv() {
75 | return env;
76 | }
77 |
78 | private final ListProperty environmentFiles;
79 |
80 | @Input
81 | @Optional
82 | public ListProperty getEnvironmentFiles() {
83 | return environmentFiles;
84 | }
85 |
86 | private EngineResponseContent result;
87 |
88 | @Internal
89 | public EngineResponseContent getResult() {
90 | return result;
91 | }
92 |
93 | private final EnvFileParser envFileParser = new EnvFileParser();
94 |
95 | @Inject
96 | public DockerCreateTask(ObjectFactory objectFactory) {
97 | super(objectFactory);
98 | setDescription("Create a new container");
99 |
100 | imageName = objectFactory.property(String.class);
101 | imageTag = objectFactory.property(String.class);
102 | imageTag.convention("");
103 | containerName = objectFactory.property(String.class);
104 | containerName.convention("");
105 | ports = objectFactory.listProperty(String.class);
106 | containerConfiguration = objectFactory.property(ContainerCreateRequest.class);
107 | containerConfiguration.convention(new ContainerCreateRequest());
108 | env = objectFactory.listProperty(String.class);
109 | environmentFiles = objectFactory.listProperty(File.class);
110 | }
111 |
112 | @TaskAction
113 | public EngineResponseContent create() {
114 | getLogger().info("docker create");
115 |
116 | ContainerCreateRequest containerConfig = getActualContainerConfig();
117 | result = getDockerClient().createContainer(containerConfig, getContainerName().getOrElse(""), getEncodedAuthConfig());
118 | return result;
119 | }
120 |
121 | private String getImageNameWithTag() {
122 | if (getImageTag().isPresent() && !getImageTag().get().isEmpty()) {
123 | return getImageName().get() + ":" + getImageTag().get();
124 | }
125 | else {
126 | return getImageName().get();
127 | }
128 | }
129 |
130 | @Internal
131 | public ContainerCreateRequest getActualContainerConfig() {
132 | ContainerCreateRequest containerCreateRequest = getContainerConfiguration().getOrElse(new ContainerCreateRequest());
133 | if (containerCreateRequest.getHostConfig() == null) {
134 | containerCreateRequest.setHostConfig(new HostConfig());
135 | }
136 |
137 | containerCreateRequest.setImage(getImageNameWithTag());
138 | if (!getEnvironmentFiles().get().isEmpty()) {
139 | if (containerCreateRequest.getEnv() == null) {
140 | containerCreateRequest.setEnv(new ArrayList<>());
141 | }
142 | List env = containerCreateRequest.getEnv();
143 | getEnvironmentFiles().get().forEach((File file) -> {
144 | List parsedEnv = envFileParser.parse(file);
145 | env.addAll(parsedEnv);
146 | });
147 | }
148 | if (!getEnv().get().isEmpty()) {
149 | if (containerCreateRequest.getEnv() == null) {
150 | containerCreateRequest.setEnv(new ArrayList<>());
151 | }
152 | List env = containerCreateRequest.getEnv();
153 | env.addAll(getEnv().get());
154 | }
155 |
156 | if (!getPorts().get().isEmpty()) {
157 | if (containerCreateRequest.getExposedPorts() == null) {
158 | containerCreateRequest.setExposedPorts(new HashMap<>());
159 | }
160 | final Map exposedPorts = containerCreateRequest.getExposedPorts();
161 | if (containerCreateRequest.getHostConfig().getPortBindings() == null) {
162 | containerCreateRequest.getHostConfig().setPortBindings(new HashMap<>());
163 | }
164 | final Map> portBindings = containerCreateRequest.getHostConfig().getPortBindings();
165 | getPorts().get().forEach((String portMapping) -> {
166 | // format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
167 | final String[] splittedPortMapping = portMapping.split(":");
168 | if (splittedPortMapping.length != 2) {
169 | throw new UnsupportedOperationException("please use the plain `containerConfig.ExposedPorts and containerConfig.HostConfig.PortBindings` properties");
170 | }
171 | String hostPort = splittedPortMapping[0];
172 | String containerPort = splittedPortMapping[1] + "/tcp";
173 | exposedPorts.put(containerPort, new HashMap<>());
174 |
175 | PortBinding hostBinding = new PortBinding("0.0.0.0", hostPort);
176 | portBindings.put(containerPort, Collections.singletonList(hostBinding));
177 | });
178 | }
179 |
180 | getLogger().info("effective container config: " + containerCreateRequest);
181 | return containerCreateRequest;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/plugin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import io.freefair.gradle.plugins.maven.central.ValidateMavenPom
2 | import java.text.SimpleDateFormat
3 | import java.util.*
4 |
5 | plugins {
6 | id("groovy")
7 | id("java-gradle-plugin")
8 | id("maven-publish")
9 | id("signing")
10 | id("com.github.ben-manes.versions")
11 | id("org.sonatype.gradle.plugins.scan")
12 | id("com.gradle.plugin-publish")
13 | id("io.freefair.maven-central.validate-poms")
14 | }
15 |
16 | repositories {
17 | // mavenLocal()
18 | // fun findProperty(s: String) = project.findProperty(s) as String?
19 | // listOf(
20 | // "docker-client/*",
21 | // "gesellix/*"
22 | // ).forEach { repo ->
23 | // maven {
24 | // name = "github"
25 | // setUrl("https://maven.pkg.github.com/$repo")
26 | // credentials {
27 | // username = System.getenv("PACKAGE_REGISTRY_USER") ?: findProperty("github.package-registry.username")
28 | // password = System.getenv("PACKAGE_REGISTRY_TOKEN") ?: findProperty("github.package-registry.password")
29 | // }
30 | // }
31 | // }
32 | mavenCentral()
33 | }
34 |
35 | dependencies {
36 | constraints {
37 | listOf(
38 | "org.apache.groovy:groovy",
39 | "org.apache.groovy:groovy-json",
40 | ).forEach {
41 | implementation(it) {
42 | version {
43 | strictly("[4,5)")
44 | }
45 | }
46 | }
47 | testImplementation("org.junit:junit-bom") {
48 | version {
49 | strictly("[5,6)")
50 | prefer("5.13.4")
51 | }
52 | }
53 | }
54 | api(gradleApi())
55 |
56 | api("de.gesellix:docker-client:2025-11-30T22-30-00-groovy-4")
57 |
58 | testImplementation(localGroovy())
59 | testImplementation("org.spockframework:spock-core:2.3-groovy-4.0")
60 | testImplementation("cglib:cglib-nodep:3.3.0")
61 | testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4")
62 |
63 | // see https://docs.gradle.org/current/userguide/test_kit.html
64 | testImplementation(gradleTestKit())
65 | }
66 |
67 | java {
68 | toolchain {
69 | languageVersion.set(JavaLanguageVersion.of(17))
70 | }
71 | }
72 |
73 | tasks {
74 | withType(Test::class.java) {
75 | useJUnitPlatform()
76 | }
77 | }
78 |
79 | val javadocJar by tasks.registering(Jar::class) {
80 | dependsOn("classes")
81 | archiveClassifier.set("javadoc")
82 | from(tasks.javadoc)
83 | }
84 |
85 | val sourcesJar by tasks.registering(Jar::class) {
86 | dependsOn("classes")
87 | archiveClassifier.set("sources")
88 | from(sourceSets.main.get().allSource)
89 | }
90 |
91 | artifacts {
92 | add("archives", sourcesJar.get())
93 | add("archives", javadocJar.get())
94 | }
95 |
96 | ossIndexAudit {
97 | username = System.getenv("SONATYPE_INDEX_USERNAME") ?: findProperty("sonatype.index.username")
98 | password = System.getenv("SONATYPE_INDEX_PASSWORD") ?: findProperty("sonatype.index.password")
99 | }
100 |
101 | fun findProperty(s: String) = project.findProperty(s) as String?
102 |
103 | val localRepositoryName = "LocalPackages"
104 | val gitHubPackagesRepositoryName = "GitHubPackages"
105 | val isSnapshot = project.version == "unspecified"
106 | val artifactVersion = if (!isSnapshot) project.version as String else SimpleDateFormat("yyyy-MM-dd\'T\'HH-mm-ss").format(Date())!!
107 | val publicationName = "gradleDockerPlugin"
108 | publishing {
109 | repositories {
110 | maven {
111 | name = localRepositoryName
112 | url = uri("../local-plugins")
113 | }
114 | maven {
115 | name = gitHubPackagesRepositoryName
116 | url = uri("https://maven.pkg.github.com/${property("github.package-registry.owner")}/${property("github.package-registry.repository")}")
117 | credentials {
118 | username = System.getenv("GITHUB_ACTOR") ?: findProperty("github.package-registry.username")
119 | password = System.getenv("GITHUB_TOKEN") ?: findProperty("github.package-registry.password")
120 | }
121 | }
122 | }
123 | publications {
124 | register(publicationName) {
125 | pom {
126 | name.set("gradle-docker-plugin")
127 | description.set("A Docker plugin for Gradle")
128 | url.set("https://github.com/gesellix/gradle-docker-plugin")
129 | licenses {
130 | license {
131 | name.set("MIT")
132 | url.set("https://opensource.org/licenses/MIT")
133 | }
134 | }
135 | developers {
136 | developer {
137 | id.set("gesellix")
138 | name.set("Tobias Gesellchen")
139 | email.set("tobias@gesellix.de")
140 | }
141 | }
142 | scm {
143 | connection.set("scm:git:github.com/gesellix/gradle-docker-plugin.git")
144 | developerConnection.set("scm:git:ssh://github.com/gesellix/gradle-docker-plugin.git")
145 | url.set("https://github.com/gesellix/gradle-docker-plugin")
146 | }
147 | }
148 | artifactId = "gradle-docker-plugin"
149 | version = artifactVersion
150 | from(components["java"])
151 | // TODO how do we ensure that these artifacts will always be added
152 | // automatically?
153 | // artifact(sourcesJar.get())
154 | // artifact(javadocJar.get())
155 | }
156 | }
157 | }
158 |
159 | signing {
160 | setRequired({ !isSnapshot })
161 | val signingKey: String? by project
162 | val signingPassword: String? by project
163 | useInMemoryPgpKeys(signingKey, signingPassword)
164 | sign(publishing.publications[publicationName])
165 | }
166 |
167 | gradlePlugin {
168 | website.set("https://github.com/gesellix/gradle-docker-plugin")
169 | vcsUrl.set("https://github.com/gesellix/gradle-docker-plugin.git")
170 |
171 | plugins {
172 | register(publicationName) {
173 | id = "de.gesellix.docker"
174 | displayName = "Gradle Docker plugin"
175 | description = "A Docker plugin for Gradle"
176 | implementationClass = "de.gesellix.gradle.docker.DockerPlugin"
177 | version = artifactVersion
178 | tags.set(listOf("docker", "remote api", "client"))
179 | }
180 | }
181 | }
182 |
183 | tasks.withType().configureEach {
184 | ignoreFailures = System.getenv()["IGNORE_INVALID_POMS"] == "true"
185 | || name.contains("For${publicationName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}PluginMarkerMaven")
186 | || name.contains("ForPluginMavenPublication")
187 | }
188 |
189 | tasks.register("publishTo${localRepositoryName}") {
190 | group = "publishing"
191 | description = "Publishes all Maven publications to the $localRepositoryName Maven repository."
192 | dependsOn(tasks.withType().matching {
193 | it.repository == publishing.repositories[localRepositoryName]
194 | })
195 | }
196 |
197 | tasks.register("publishTo${gitHubPackagesRepositoryName}") {
198 | group = "publishing"
199 | description = "Publishes all Maven publications to the $gitHubPackagesRepositoryName Maven repository."
200 | dependsOn(tasks.withType().matching {
201 | it.repository == publishing.repositories[gitHubPackagesRepositoryName]
202 | })
203 | }
204 |
205 | val isLocalRepo = { repository: MavenArtifactRepository ->
206 | repository == publishing.repositories[localRepositoryName]
207 | }
208 | val isStandardMavenPublication = { repository: MavenArtifactRepository, publication: MavenPublication ->
209 | publication == publishing.publications[publicationName]
210 | && repository.name in listOf("sonatype", localRepositoryName, gitHubPackagesRepositoryName)
211 | }
212 | val isGradlePluginPublish = { repository: MavenArtifactRepository, publication: MavenPublication ->
213 | publication == publishing.publications["pluginMaven"]
214 | && repository.name !in listOf("sonatype", localRepositoryName, gitHubPackagesRepositoryName)
215 | }
216 |
217 | tasks.withType().configureEach {
218 | onlyIf {
219 | isLocalRepo(repository)
220 | || isStandardMavenPublication(repository, publication)
221 | || isGradlePluginPublish(repository, publication)
222 | }
223 | mustRunAfter(tasks.withType())
224 | }
225 |
226 | //afterEvaluate {
227 | // publishing.publications.forEach { p ->
228 | // if (p is MavenPublication){
229 | // p.artifacts.forEach {a->
230 | // println("${p.name} -> ${a.extension}/${a.classifier} -> ${a.file}")
231 | // }
232 | // }
233 | // }
234 | //}
235 |
--------------------------------------------------------------------------------
/plugin/src/test/groovy/de/gesellix/gradle/docker/tasks/DockerBuildTaskSpec.groovy:
--------------------------------------------------------------------------------
1 | package de.gesellix.gradle.docker.tasks
2 |
3 | import de.gesellix.docker.authentication.AuthConfig
4 | import de.gesellix.docker.client.DockerClient
5 | import de.gesellix.gradle.docker.worker.BuildcontextArchiver
6 | import org.gradle.testfixtures.ProjectBuilder
7 | import org.gradle.workers.WorkQueue
8 | import org.gradle.workers.WorkerExecutor
9 | import spock.lang.Specification
10 | import spock.lang.Unroll
11 |
12 | import java.time.Duration
13 | import java.time.temporal.ChronoUnit
14 |
15 | class DockerBuildTaskSpec extends Specification {
16 |
17 | def project
18 | def task
19 | def dockerClient = Mock(DockerClient)
20 |
21 | def setup() {
22 | project = ProjectBuilder.builder().build()
23 | task = project.tasks.register('dockerBuild', DockerBuildTask).get()
24 | task.dockerClient = dockerClient
25 | task.buildTimeout = Duration.of(1, ChronoUnit.SECONDS)
26 | }
27 |
28 | def "should archive the buildcontext in a worker thread"() {
29 | URL dockerfile = getClass().getResource('/docker/Dockerfile')
30 | def baseDir = new File(dockerfile.toURI()).parentFile
31 |
32 | given:
33 | def buildTaskDependency = project.task('buildTaskDependency', type: TestTask)
34 | task.dependsOn buildTaskDependency
35 | task.buildContextDirectory = baseDir
36 | task.imageName = "busybox"
37 | def workerExecutor = Mock(WorkerExecutor)
38 | def workQueue = Mock(WorkQueue)
39 | task.workerExecutor = workerExecutor
40 |
41 | when:
42 | task.build()
43 |
44 | then:
45 | project.tasks.findByName("dockerBuild").getDependsOn().contains project.tasks.findByName("buildTaskDependency")
46 | and:
47 | 1 * workerExecutor.noIsolation() >> workQueue
48 | 1 * workQueue.submit(BuildcontextArchiver, _) >> { task.targetFile = new File(dockerfile.toURI()) }
49 | 1 * workerExecutor.await()
50 | and:
51 | 1 * dockerClient.build(*_)
52 | }
53 |
54 | def "delegates to dockerClient with buildContext"() {
55 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
56 |
57 | given:
58 | task.buildContext = inputStream
59 | task.imageName = "imageName"
60 |
61 | when:
62 | task.build()
63 |
64 | then:
65 | 1 * dockerClient.build(_, _,
66 | null, "imageName",
67 | null, null, null, true,
68 | null, null, null,
69 | null, inputStream)
70 |
71 | and:
72 | task.outputs.files.isEmpty()
73 | }
74 |
75 | def "delegates to dockerClient with buildContext and buildParams"() {
76 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
77 |
78 | given:
79 | task.buildContext = inputStream
80 | task.buildParams = [
81 | buildargs : [AN_ARGUMENT: "a value"],
82 | dockerfile: './custom.Dockerfile',
83 | nocache : true,
84 | pull : "true",
85 | quiet : true,
86 | rm : false,
87 | ]
88 | task.imageName = "imageName"
89 |
90 | when:
91 | task.build()
92 |
93 | then:
94 | 1 * dockerClient.build(_, _,
95 | "./custom.Dockerfile",
96 | "imageName",
97 | true,
98 | true,
99 | "true",
100 | false,
101 | '{"AN_ARGUMENT":"a value"}',
102 | null,
103 | null,
104 | null,
105 | inputStream)
106 |
107 | and:
108 | task.outputs.files.isEmpty()
109 | }
110 |
111 | def "delegates to dockerClient with buildContext and buildOptions"() {
112 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
113 |
114 | given:
115 | task.buildContext = inputStream
116 | task.buildOptions = [EncodedRegistryConfig: "base-64"]
117 | task.imageName = "imageName"
118 |
119 | when:
120 | task.build()
121 |
122 | then:
123 | 1 * dockerClient.build(_, _,
124 | null, "imageName",
125 | null, null, null, true,
126 | null, null, "base-64",
127 | null, inputStream)
128 |
129 | and:
130 | task.outputs.files.isEmpty()
131 | }
132 |
133 | def "does not override rm build param if given"() {
134 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
135 |
136 | given:
137 | task.buildContext = inputStream
138 | task.buildParams = [rm: false, dockerfile: './custom.Dockerfile']
139 | task.imageName = "imageName"
140 |
141 | when:
142 | task.build()
143 |
144 | then:
145 | 1 * dockerClient.build(_, _,
146 | "./custom.Dockerfile", "imageName",
147 | null, null, null, false,
148 | null, null, null,
149 | null, inputStream)
150 |
151 | and:
152 | task.outputs.files.isEmpty()
153 | }
154 |
155 | @Unroll
156 | def "should accept boolean for 'pull' build param"() {
157 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
158 |
159 | given:
160 | task.buildContext = inputStream
161 | task.buildParams = [rm: false, pull: pull, dockerfile: './custom.Dockerfile']
162 | task.imageName = "imageName"
163 |
164 | when:
165 | task.build()
166 |
167 | then:
168 | 1 * dockerClient.build(_, _,
169 | "./custom.Dockerfile", "imageName",
170 | null, null, pull.toString(), false,
171 | null, null, null,
172 | null, inputStream)
173 |
174 | and:
175 | task.outputs.files.isEmpty()
176 |
177 | where:
178 | pull << [true, false]
179 | }
180 |
181 | def "uses auth configs if not overridden via build options"() {
182 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
183 | Map authConfigs = ["host.name": new AuthConfig(username: "user-name", password: "a secret")]
184 |
185 | given:
186 | task.authConfigs = authConfigs
187 | dockerClient.encodeAuthConfigs(authConfigs) >> "encoded-auth"
188 | task.buildContext = inputStream
189 | task.imageName = "imageName"
190 |
191 | when:
192 | task.build()
193 |
194 | then:
195 | 1 * dockerClient.build(_, _,
196 | null, "imageName",
197 | null, null, null, true,
198 | null, null, "encoded-auth",
199 | null, inputStream)
200 |
201 | and:
202 | task.outputs.files.isEmpty()
203 | }
204 |
205 | def "delegates to dockerClient with buildContext (with logs)"() {
206 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
207 |
208 | given:
209 | task.buildContext = inputStream
210 | task.imageName = "imageName"
211 | task.enableBuildLog = true
212 |
213 | when:
214 | task.build()
215 |
216 | then:
217 | 1 * dockerClient.build(_, _,
218 | null, "imageName",
219 | null, null, null, true,
220 | null, null, null,
221 | null, inputStream)
222 |
223 | and:
224 | task.outputs.files.isEmpty()
225 | }
226 |
227 | def "delegates to dockerClient with buildContext and buildParams (with logs)"() {
228 | def inputStream = new FileInputStream(File.createTempFile("docker", "test"))
229 |
230 | given:
231 | task.buildContext = inputStream
232 | task.buildParams = [rm: true, dockerfile: './custom.Dockerfile']
233 | task.imageName = "imageName"
234 | task.enableBuildLog = true
235 |
236 | when:
237 | task.build()
238 |
239 | then:
240 | 1 * dockerClient.build(_, _,
241 | "./custom.Dockerfile", "imageName",
242 | null, null, null, true,
243 | null, null, null,
244 | null, inputStream)
245 |
246 | and:
247 | task.outputs.files.isEmpty()
248 | }
249 |
250 | def "normalizedImageName should match [a-z0-9-_.]"() {
251 | expect:
252 | task.getNormalizedImageName() ==~ "[a-z0-9-_.]+"
253 | }
254 |
255 | def parentDir(URL resource) {
256 | new File(resource.toURI()).parentFile
257 | }
258 |
259 | def wrapInClosure(value) {
260 | new Closure(null) {
261 |
262 | @Override
263 | Object call() {
264 | value
265 | }
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/supported-api.md:
--------------------------------------------------------------------------------
1 | # Supported Features
2 |
3 | *feature set based on the [Docker Engine API v1.25](https://docs.docker.com/engine/api/v1.25/)*
4 |
5 | Since the Docker engine api tends to be backwards compatible,
6 | the underlying Docker Client currently supports most other api versions, too.
7 |
8 | Current api coverage: 40/114 endpoints.
9 |
10 | This project tends to support most api endpoints, but only if there's an actual use case. If you're missing a feature, please file
11 | a [new issue](https://github.com/gesellix/gradle-docker-plugin/issues) or a [pull request](https://github.com/gesellix/gradle-docker-plugin/pulls)
12 | and we'll add it as soon as the time allows. This plugin relies on the [Docker Client](https://github.com/gesellix/docker-client) while
13 | there's a [similar Gradle Docker plugin](https://github.com/bmuschko/gradle-docker-plugin) based
14 | on the [Java Docker API Client](https://github.com/docker-java/docker-java) available, too.
15 |
16 | # Management Commands
17 |
18 | ## Checkpoints - Manage checkpoints (0/3)
19 |
20 | * [ ] `docker checkpoints create`: Create a checkpoint from a running container
21 | * [ ] `docker checkpoints ls`: List checkpoints for a container
22 | * [ ] `docker checkpoints rm`: Remove a checkpoint
23 |
24 | ## Container - Manage containers (18/32)
25 |
26 | * [ ] `docker container attach `: Attach to a running container (supports interactive tty)
27 | * [ ] Attach to a running container (websocket)
28 | * [ ] Resize a container TTY
29 | * [x] `docker container commit `: Create a new image from a container's changes
30 | * [x] `docker container cp : `: Get an archive of a filesystem resource in a container
31 | * [x] `docker container cp :`: Extract an archive of files or folders to a directory in a container
32 | * [ ] Retrieve information about files and folders in a container
33 | * [x] `docker container create`: Create a new container
34 | * [ ] `docker container diff `: Inspect changes on a container's filesystem
35 | * [x] `docker container exec `: Run a command in a running container
36 | * [x] Exec Start (supports interactive tty)
37 | * [x] Exec Create
38 | * [ ] Exec Resize
39 | * [ ] Exec Inspect
40 | * [ ] `docker container export `: Export a container's filesystem as a tar archive
41 | * [x] `docker container inspect `: Display detailed information on one or more containers
42 | * [x] `docker container kill `: Kill one or more running containers
43 | * [ ] `docker container logs `: Fetch the logs of a container
44 | * [x] `docker container ps`: List containers (alias for `ls`, `list`)
45 | * [x] `docker container pause `: Pause all processes within one or more containers
46 | * [ ] `docker container port`: List port mappings or a specific mapping for the container
47 | * [ ] `docker container prune`: Remove all stopped containers
48 | * [x] `docker container rename `: Rename a container
49 | * [x] `docker container restart `: Restart one or more containers
50 | * [x] `docker container rm `: Remove one or more containers
51 | * [ ] `docker container run`: Run a command in a new container
52 | * [x] `docker container start `: Start one or more stopped containers
53 | * [ ] `docker container stats `: Display a live stream of container(s) resource usage statistics
54 | * [x] `docker container stop `: Stop one or more running containers
55 | * [ ] `docker container top `: Display the running processes of a container
56 | * [x] `docker container unpause `: Unpause all processes within one or more containers
57 | * [ ] `docker container update [...]`: Update configuration of one or more containers
58 | * [x] `docker container wait `: Block until one or more containers stop, then print their exit codes
59 |
60 | ## Image - Manage images (6/14)
61 |
62 | * [x] `docker image build`: Build an image from a Dockerfile
63 | * [ ] `docker image history `: Show the history of an image
64 | * [ ] `docker image import`: Import the contents from a tarball to create a filesystem image (from stream)
65 | * [ ] `docker image import`: Import the contents from a tarball to create a filesystem image (from url)
66 | * [ ] `docker image inspect `: Display detailed information on one or more images
67 | * [ ] `docker image load`: Load a tarball with a set of images and tags into docker
68 | * [x] `docker image ls`: List Images
69 | * [ ] `docker image prune`: Remove unused images
70 | * [x] `docker image pull`: Pull an image or a repository from a registry
71 | * [x] `docker image push `: Push an image or a repository to a registry
72 | * [x] `docker image rm