avast/gradle-docker-compose-plugin

repository·main·Indexed 19 days ago

https://github.com/avast/gradle-docker-compose-plugin

A Gradle plugin that simplifies using Docker Compose for local development and integration testing. It manages container health, random port assignments, and provides service connection details via environment variables and Java system properties. It includes tasks for lifecycle management (composeUp, composeDown, composePull, etc.) and supports nested configurations, Kotlin DSL, and Docker Compose V2.

Tokens
3K
Snippets
9
Records
14
Agent score
15%

What's inside gradle-docker-compose-plugin

  1. Understand Docker Compose file precedence

    main
    The plugin automatically honors a docker-compose.override.yml file, following standard Docker Compose behavior. However, this automatic discovery only occurs if you have not explicitly specified files using the useComposeFiles configuration option.
  2. Create nested Docker Compose configurations

    main

    You can define multiple independent Docker Compose setups within a single project using nested configurations. This allows you to have different sets of tasks (e.g., myNestedComposeUp, myNestedComposeDown) for different environments or test types.

    Groovy DSL Syntax:

    dockerCompose {
        myNested {
            useComposeFiles = ['docker-compose-for-integration-tests.yml']
            isRequiredBy(project.tasks.myTask)
        }
    }

    Kotlin DSL Syntax:

    configure<ComposeExtension> {
        createNested("local").apply {
            setProjectName("foo")
            // ... other settings
        }
    }

    When using nested configurations, the nested settings inherit from the main dockerCompose block, except for: projectName, startedServices, useComposeFiles, scale, captureContainersOutputToFile, captureContainersOutputToFiles, composeLogToFile, containerLogToDir, and pushServices.

  3. Access information about running containers

    main

    The dockerCompose.servicesInfos property contains metadata and information about the running containers.

    Important: Because containers are started during the composeUp task, you must access servicesInfos after that task has completed. The best practice is to access this property within the doFirst block of your test or execution task.

    test {
        doFirst {
            // Access container info after composeUp has run
            def info = dockerCompose.servicesInfos
            println info
        }
    }
  4. Install the gradle-docker-compose-plugin

    main

    You can install the plugin using the Gradle Plugin Portal or via Maven Central.

    Option 1: Gradle Plugin Portal (Recommended) Add the plugin to your plugins block:

    Option 2: Maven Central Use the buildscript block in your build.gradle file:

    Note: Versions prior to 0.14.2 were published to JCenter, which is now decommissioned and unavailable.

    // Option 1: Gradle Plugin Portal
    plugins {
      id "com.avast.gradle.docker-compose" version "$versionHere"
    }
    
    // Option 2: Maven Central
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath "com.avast.gradle:gradle-docker-compose-plugin:$versionHere"
        }
    }
    
    apply plugin: 'docker-compose'
  5. Install and apply the gradle-docker-compose-plugin

    main

    To use the plugin, you must have Docker Engine and Docker Compose installed and available in your PATH. The plugin must be applied to a project that contains a docker-compose.yml file.

    Version Requirements:

    • Gradle 9.0+: Required for plugin version 0.17.13 and newer.
    • Gradle 6.1+: Required for plugin version 0.17.6 and newer.
    • Gradle 4.9+: Required for plugin version 0.10.0 and newer.

    Note: Starting from version 0.17.0, useDockerComposeV2 defaults to true, meaning the plugin uses the docker compose command instead of the deprecated docker-compose.

    Apply the plugin in your build.gradle file as follows:

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath "com.avast.gradle:gradle-docker-compose-plugin:$versionHere"
        }
    }
    
    apply plugin: 'docker-compose'
  6. Configure task dependencies for Docker Compose

    main

    You can integrate Docker Compose with your existing Gradle task graph using several methods:

    1. Requirement Check: Use dockerCompose.isRequiredBy(anyTask) to indicate that a specific task (like a custom integrationTest task) requires the Docker Compose environment to be running.
    2. Artifact Dependencies: If a Dockerfile requires an artifact generated by a Gradle task, declare the dependency using standard Gradle syntax. For example, to ensure a distribution task runs before the containers are started, use composeUp.dependsOn project(':my-app').distTar.
    // Example: making a custom task depend on the compose environment
    dockerCompose.isRequiredBy(integrationTest)
    
    // Example: ensuring an artifact is built before composeUp
    composeUp.dependsOn project(':my-app').distTar
  7. Configure Docker Compose to run before tasks

    main

    To ensure Docker Compose services are started before a specific Gradle task (like test), use the isRequiredBy method. This ensures docker-compose up is executed in the project directory using your docker-compose.yml file.

    When the target task executes a new process, the plugin automatically provides environment variables and Java system properties containing the host and TCP port information for the services, even if ports are assigned randomly by Docker.

    dockerCompose.isRequiredBy(test)
  8. Configure environment variables for Docker Compose

    main

    You can pass environment variables to the Docker Compose process using the environment map within the dockerCompose configuration block. This is useful for setting the Docker host or service hostnames.

    Common use cases:

    • DOCKER_HOST: Set the Docker host (e.g., for docker-machine).
    • SERVICES_HOST: Set the hostname where services are expected to be listening (useful for remote hosts like CirceCI 2.0).
    dockerCompose {
        environment.put 'DOCKER_HOST', '192.168.64.9'
        environment.put 'SERVICES_HOST', 'some-remote-host'
    }
  9. Configure the dockerCompose extension

    main

    The dockerCompose configuration block allows you to customize how Docker Compose is invoked. Key configuration options include:

    • useComposeFiles: List of compose files to use (e.g., ['docker-compose.yml', 'docker-compose.prod.yml']).
    • startedServices: List of services to execute when calling up or pull.
    • scale: Map of service names to scale factors (e.g., [web: 5]).
    • forceRecreate: Pass --force-recreate and --renew-anon-volumes during up.
    • buildBeforeUp: Whether to run docker-compose build before up (default: true).
    • waitForTcpPorts: Whether to wait for exposed TCP ports to open (default: true).
    • captureContainersOutput: If true, prints all container output to Gradle output.
    • stopContainers: If false, the plugin attempts to reconnect to existing containers instead of calling down (useful for fast iteration).
    • useDockerComposeV2: Use docker compose instead of docker-compose (default: true).
    • environment: A map of environment variables for use in the compose file.
  10. Troubleshoot failing ComposeUp tasks

    main

    By default, the plugin may forcibly delete containers if the composeUp task fails. To keep containers alive for manual inspection and troubleshooting, set retainContainersOnStartupFailure to true.

    Note: This setting does not affect the removeContainers behavior; running ComposeDown will still remove the containers.

    dockerCompose {
        retainContainersOnStartupFailure = true
    }
  11. Use the gradle-docker-compose-plugin with Kotlin DSL

    main

    The plugin supports the Gradle Kotlin DSL. Use the ComposeExtension type to configure the plugin.

    import com.avast.gradle.dockercompose.ComposeExtension
    
    apply(plugin = "docker-compose")
    
    configure<ComposeExtension> {
        includeDependencies.set(true)
        createNested("local").apply {
            setProjectName("foo")
            environment.putAll(mapOf("TAGS" to "feature-test,local"))
            startedServices.set(listOf("foo-api", "foo-integration"))
            upAdditionalArgs.set(listOf("--no-deps"))
        }
    }
    import com.avast.gradle.dockercompose.ComposeExtension
    apply(plugin = "docker-compose")
    configure<ComposeExtension> {
        includeDependencies.set(true)
        createNested("local").apply {
            setProjectName("foo")
            environment.putAll(mapOf("TAGS" to "feature-test,local"))
            startedServices.set(listOf("foo-api", "foo-integration"))
            upAdditionalArgs.set(listOf("--no-deps"))
        }
    }
  12. Expose container information as environment variables or system properties

    main

    To make container hostnames and ports available to your tests, use the exposeAsEnvironment or exposeAsSystemProperties methods on the dockerCompose object within a task configuration block (e.g., test.doFirst).

    Environment Variables: Exposes ${serviceName}_HOST and ${serviceName}_TCP_${exposedPort}. Example: For service web with port 80, it exposes WEB_HOST and WEB_TCP_80.

    System Properties: Exposes ${serviceName}.host and ${serviceName}.tcp.${exposedPort}. Example: For service web with port 80, it exposes web.host and web.tcp.80.

    Accessing Service Info: You can also access servicesInfos directly to get container details like host and ports.

    test.doFirst {
        // Expose as environment variables
        dockerCompose.exposeAsEnvironment(test)
        
        // Expose as system properties
        dockerCompose.exposeAsSystemProperties(test)
    
        // Access specific container info
        def webInfo = dockerCompose.servicesInfos.web.firstContainer
        systemProperty 'myweb.host', webInfo.host
        systemProperty 'myweb.port', webInfo.ports[80]
    }