diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4ef335ae..409016a9 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -7,7 +7,7 @@ env: steps: - label: ":shell: Shellcheck" plugins: - shellcheck#v1.1.2: + shellcheck#v1.3.0: files: - hooks/** - lib/** @@ -15,13 +15,14 @@ steps: - label: ":sparkles: Lint" plugins: - plugin-linter#v2.0.0: + plugin-linter#v3.2.0: id: docker-compose - label: ":bash: Tests" plugins: ${BUILDKITE_PULL_REQUEST_REPO:-$BUILDKITE_REPO}#${BUILDKITE_COMMIT}: run: tests + command: ["bats", "tests", "tests/v2"] # The rest of the steps are integration tests @@ -114,6 +115,8 @@ steps: config: tests/composefiles/docker-compose.v2.1.yml commmand: ["/hello"] + - wait: ~ + - label: prebuild with custom image-name key: prebuild-custom-image-name env: diff --git a/README.md b/README.md index bee93445..076f1294 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,12 @@ The following pipeline will run `test.sh` inside a `app` service container using steps: - command: test.sh plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app ``` +:warning: Warning: you should not use this plugin with an array of commands at the step level. Execute a script in your repository, a single command separated by `;` or the plugin's [`command` option](#command-optional-run-only-array) instead. + You can also specify a custom Docker Compose config file and what environment to pass through if you need: @@ -26,7 +28,7 @@ through if you need: steps: - command: test.sh plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app config: docker-compose.tests.yml env: @@ -39,13 +41,37 @@ or multiple config files: steps: - command: test.sh plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app config: - docker-compose.yml - docker-compose.test.yml ``` +You can also specify the Docker Compose config file with [`$COMPOSE_FILE`](https://docs.docker.com/compose/reference/envvars/#compose_file): + +```yml +env: + COMPOSE_FILE: docker-compose.yml +steps: + - command: test.sh + plugins: + - docker-compose#v4.15.0: + run: app +``` + +If you want to control how your command is passed to docker-compose, you can use the command parameter on the plugin directly: + +```yml +steps: + - plugins: + - docker-compose#v4.15.0: + run: app + command: ["custom", "command", "values"] +``` + +## Authenticated registries + You can leverage the [docker-login plugin](https://github.com/buildkite-plugins/docker-login-buildkite-plugin) in tandem for authenticating with a registry. For example, the following will build and push an image to a private repo, and pull from that private repo in subsequent run commands: ```yml @@ -53,7 +79,7 @@ steps: - plugins: - docker-login#v2.0.1: username: xyz - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: build: app image-repository: index.docker.io/myorg/myrepo - wait @@ -61,19 +87,11 @@ steps: plugins: - docker-login#v2.0.1: username: xyz - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app ``` -If you want to control how your command is passed to docker-compose, you can use the command parameter on the plugin directly: - -```yml -steps: - - plugins: - - docker-compose#v3.5.0: - run: app - command: ["custom", "command", "values"] -``` +Note, you will need to add the configuration to all steps in which you use this plugin. ## Artifacts @@ -86,7 +104,7 @@ steps: - command: generate-dist.sh artifact_paths: "dist/*" plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app ``` @@ -104,28 +122,31 @@ steps: - command: generate-dist.sh artifact_paths: "dist/*" plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app volumes: - "./dist:/app/dist" ``` +If you want to use environment variables in the `volumes` element, you will need to activate the (unsafe) option `expand-volume-vars` (and most likely escape it using `$$VARIABLE_NAME`). + ## Environment By default, docker-compose makes whatever environment variables it gets available for interpolation of docker-compose.yml, but it doesn't pass them in to your containers. -You can use the [environment key in docker-compose.yml](https://docs.docker.com/compose/environment-variables/) to either set specific environment vars or "pass through" environment -variables from outside docker-compose. +You can use the [environment key in docker-compose.yml](https://docs.docker.com/compose/environment-variables/) to either set specific environment vars or "pass through" environment variables from outside docker-compose. + +### Specific values If you want to add extra environment above what is declared in your `docker-compose.yml`, -this plugin offers a `environment` block of it's own: +this plugin offers a `environment` block of its own: ```yml steps: - command: generate-dist.sh plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app env: - BUILDKITE_BUILD_NUMBER @@ -135,34 +156,67 @@ steps: Note how the values in the list can either be just a key (so the value is sourced from the environment) or a KEY=VALUE pair. +### Pipeline variables + +Alternatively, you can have the plugin add all environment variables defined for the job by the agent as defined in [`BUILDKITE_ENV_FILE`](https://buildkite.com/docs/pipelines/environment-variables#BUILDKITE_ENV_FILE) activating the `propagate-environment` option: + +```yml +steps: + - command: use-vars.sh + plugins: + - docker-compose#v4.15.0: + run: app + propagate-environment: true +``` + +## Container Labels + +When running a command, the plugin will automatically add the following Docker labels to the container specified in the `run` option: +- `com.buildkite.pipeline_name=${BUILDKITE_PIPELINE_NAME}` +- `com.buildkite.pipeline_slug=${BUILDKITE_PIPELINE_SLUG}` +- `com.buildkite.build_number=${BUILDKITE_BUILD_NUMBER}` +- `com.buildkite.job_id=${BUILDKITE_JOB_ID}` +- `com.buildkite.job_label=${BUILDKITE_LABEL}` +- `com.buildkite.step_key=${BUILDKITE_STEP_KEY}` +- `com.buildkite.agent_name=${BUILDKITE_AGENT_NAME}` +- `com.buildkite.agent_id=${BUILDKITE_AGENT_ID}` + +These labels can make it easier to query containers on hosts using `docker ps` for example: + +```bash +docker ps --filter "label=com.buildkite.job_label=Run tests" +``` + +This behaviour can be disabled with the `run-labels: false` option. + ## Build Arguments You can use the [build args key in docker-compose.yml](https://docs.docker.com/compose/compose-file/#args) to set specific build arguments when building an image. -Alternatively, if you want to set build arguments when pre-building an image, this plugin offers an `args` block of it's own: +Alternatively, if you want to set build arguments when pre-building an image, this plugin offers an `args` block of its own: ```yml steps: - command: generate-dist.sh plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: build: app image-repository: index.docker.io/myorg/myrepo args: - MY_CUSTOM_ARG=panda ``` -Note that the values in the list must be a KEY=VALUE pair. +Note that the values in the list must be a `KEY=VALUE` pair. ## Pre-building the image -To speed up run steps that use the same service/image (such as steps that run in parallel), you can add a pre-build step to your pipeline: +If you have multiple steps that use the same service/image (such as steps that run in parallel), you can use this plugin in a specific `build` step to your pipeline. That will set specific metadata in the pipeline for this plugin to use in `run` steps afterwards: ```yml steps: - label: ":docker: Build" plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: build: app image-repository: index.docker.io/myorg/myrepo @@ -172,11 +226,11 @@ steps: command: test.sh parallelism: 25 plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: app ``` -All `run` steps for the service `app` will automatically pull and use the pre-built image. +All `run` steps for the service `app` will automatically pull and use the pre-built image. Without this, each `Test %n` job would build its own instead. ## Building multiple images @@ -188,7 +242,7 @@ steps: agents: queue: docker-builder plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: build: - app - tests @@ -200,7 +254,7 @@ steps: command: test.sh parallelism: 25 plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: run: tests ``` @@ -212,19 +266,7 @@ If you want to push your Docker images ready for deployment, you can use the `pu steps: - label: ":docker: Push" plugins: - - docker-compose#v3.5.0: - push: app -``` - -If you need to authenticate to the repository to push (e.g. when pushing to Docker Hub), use the Docker Login plugin: - -```yml -steps: - - label: ":docker: Push" - plugins: - - docker-login#v2.0.1: - username: xyz - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: push: app ``` @@ -234,9 +276,7 @@ To push multiple images, you can use a list: steps: - label: ":docker: Push" plugins: - - docker-login#v2.0.1: - username: xyz - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: push: - first-service - second-service @@ -248,12 +288,10 @@ If you want to push to a specific location (that's not defined as the `image` in steps: - label: ":docker: Push" plugins: - - docker-login#v2.0.1: - username: xyz - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: push: - - app:index.docker.io/myorg/myrepo/myapp - - app:index.docker.io/myorg/myrepo/myapp:latest + - app:index.docker.io/myorg/myrepo/myapp + - app:index.docker.io/myorg/myrepo/myapp:latest ``` ## Reusing caches from images @@ -262,44 +300,147 @@ A newly spawned agent won't contain any of the docker caches for the first run w ```yaml steps: - - label: ":docker Build an image" + - label: ":docker: Build an image" plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: build: app image-repository: index.docker.io/myorg/myrepo cache-from: app:index.docker.io/myorg/myrepo/myapp:latest - wait - label: ":docker: Push to final repository" plugins: - - docker-compose#v3.5.0: + - docker-compose#v4.15.0: + push: + - app:index.docker.io/myorg/myrepo/myapp + - app:index.docker.io/myorg/myrepo/myapp:latest +``` + +**Important**: if your registry URL contains a port, you will need to take the following into account: +* specify the `separator-cache-from` option to change the colon character to something else (like `#`) +* you will have to specify tags in the `push` elements (or the plugin will try to validate everything after the port as a tag) + +#### Multiple cache-from values + +This plugin allows for the value of `cache-from` to be a string or a list. If it's a list, as below, then the first successfully pulled image will be used. + +```yaml +steps: + - label: ":docker Build an image" + plugins: + - docker-compose#v4.15.0: + build: app + image-repository: index.docker.io/myorg/myrepo + separator-cache-from: "#" + cache-from: + - "app#myregistry:port/myrepo/myapp#my-branch" + - "app#myregistry:port/myrepo/myapp#latest" + - wait + - label: ":docker: Push to final repository" + plugins: + - docker-compose#v4.15.0: push: - - app:index.docker.io/myorg/myrepo/myapp - - app:index.docker.io/myorg/myrepo/myapp:latest + - app:myregistry:port/myrepo/myapp:my-branch + - app:myregistry:port/myrepo/myapp:latest ``` +You may actually want to build your image with multiple cache-from values, for instance, with the cached images of multiple stages in a multi-stage build. +Adding a grouping tag to the end of a cache-from list item allows this plugin to differentiate between groups within which only the first successfully downloaded image should be used (those elements that don't have a group specified will make a separate `:default:` group of its own). This way, not all images need to be downloaded and used as cache, not just the first. + +```yaml +steps: + - label: ":docker: Build Intermediate Image" + plugins: + - docker-compose#v4.15.0: + build: myservice_intermediate # docker-compose.yml is the same as myservice but has `target: intermediate` + image-name: buildkite-build-${BUILDKITE_BUILD_NUMBER} + image-repository: index.docker.io/myorg/myrepo/myservice_intermediate + cache-from: + - myservice_intermediate:index.docker.io/myorg/myrepo/myservice_intermediate:${BUILDKITE_BRANCH} + - myservice_intermediate:index.docker.io/myorg/myrepo/myservice_intermediate:latest + - wait + - label: ":docker: Build Final Image" + plugins: + - docker-compose#v4.15.0: + build: myservice + image-name: buildkite-build-${BUILDKITE_BUILD_NUMBER} + image-repository: index.docker.io/myorg/myrepo + cache-from: + - myservice:index.docker.io/myorg/myrepo/myservice_intermediate:buildkite-build-${BUILDKITE_BUILD_NUMBER}:intermediate # built in step above + - myservice:index.docker.io/myorg/myrepo/myservice:${BUILDKITE_BRANCH} + - myservice:index.docker.io/myorg/myrepo/myservice:latest + +``` + +In the example above, the `myservice_intermediate:buildkite-build-${BUILDKITE_BUILD_NUMBER}` is one group named "intermediate", and `myservice:${BUILDKITE_BRANCH}` and `myservice:latest` +are another (with a default name). The first successfully downloaded image in each group will be used as a cache. + ## Configuration -### `build` +### Main Commands -The name of a service to build and store, allowing following pipeline steps to run faster as they won't need to build the image. The step’s `command` will be ignored and does not need to be specified. +You will need to specify at least one of the following to use this extension. + +#### `build` + +The name of a service to build and store, allowing following pipeline steps to run faster as they won't need to build the image. The step's `command` will be ignored and does not need to be specified. Either a single service or multiple services can be provided as an array. -### `run` +#### `run` The name of the service the command should be run within. If the docker-compose command would usually be `docker-compose run app test.sh` then the value would be `app`. -### `push` +#### `push` + +A list of services to push in the format `service:image:tag`. If an image has been pre-built with the build step, that image will be re-tagged, otherwise docker-compose's built-in push operation will be used. + +#### Known issues + +##### Run & Push + +A basic pipeline similar to the following: + +```yaml +steps: + - label: ":docker: Run & Push" + plugins: + - docker-compose#v4.15.0: + run: myservice + push: myservice +``` -A list of services to push in the format `service:image:tag`. If an image has been pre-built with the build step, that image will be re-tagged, otherwise docker-compose's built in push operation will be used. +Will cause the image to be built twice (once before running and once before pushing) unless there was a previous `build` step that set the appropriate metadata. + +##### Build & Push + +A basic pipeline similar to the following: + +```yaml +steps: + - label: ":docker: Build & Push" + plugins: + - docker-compose#v4.15.0: + build: myservice + push: myservice +``` + +Will cause the image to be pushed twice (once by the build step and another by the push step) ### `pull` (optional, run only) -Pull down multiple pre-built images. By default only the service that is being run will be pulled down, but this allows multiple images to be specified to handle prebuilt dependent images. +Pull down multiple pre-built images. By default only the service that is being run will be pulled down, but this allows multiple images to be specified to handle prebuilt dependent images. Note that pulling will be skipped if the `skip-pull` option is activated. + +### `collapse-run-log-group` (optional, boolean, run only) + +Whether to collapse or expand the log group that is created for the output of `docker-compose run`. When this setting is `true`, the output is collected into a `---` group, when `false` the output is collected into a `+++` group. Setting this to `true` can be useful to de-emphasize plugin output if your command creates its own `+++` group. + +For more information see [Managing log output](https://buildkite.com/docs/pipelines/managing-log-output). + +Default `false` ### `config` (optional) -The file name of the Docker Compose configuration file to use. Can also be a list of filenames. +The file name of the Docker Compose configuration file to use. Can also be a list of filenames. If `$COMPOSE_FILE` is set, it will be used if `config` is not specified. Default: `docker-compose.yml` @@ -327,6 +468,16 @@ A list of KEY=VALUE that are passed through as build arguments when image is bei A list of either KEY or KEY=VALUE that are passed through as environment variables to the container. +### `env-propagation-list` (optional, string) + +If you set this to `VALUE`, and `VALUE` is an environment variable containing a space-separated list of environment variables such as `A B C D`, then A, B, C, and D will all be propagated to the container. This is helpful when you've set up an `environment` hook to export secrets as environment variables, and you'd also like to programmatically ensure that secrets get propagated to containers, instead of listing them all out. + +### `propagate-environment` (optional, boolean) + +Whether or not to automatically propagate all pipeline environment variables into the run container. Avoiding the need to be specified with environment. + +**Important**: only pipeline environment variables will be propagated (what you see in the BuildKite UI, those listed in `$BUILDKITE_ENV_FILE`). This does not include variables exported in preceeding `environment` hooks. If you wish for those to be propagated you will need to list them specifically or use `env-propagation-list`. + ### `command` (optional, run only, array) Sets the command for the Docker image, and defaults the `shell` option to `false`. Useful if the Docker image has an entrypoint, or doesn't contain a shell. @@ -343,11 +494,19 @@ Example: `[ "powershell", "-Command" ]` ### `skip-checkout` (optional, run only) -Whether to skip the repository checkout phase. This is useful for steps that use a pre-built image. This will fail if there is no pre-built image. +Whether to skip the repository checkout phase. This is useful for steps that use a pre-built image and will fail if there is no pre-built image. + +**Important**: as the code repository will not be available in the step, you need to ensure that the docker compose file(s) are present in some way (like using artifacts) + +### `skip-pull` (optional, build and run only) + +Completely avoid running any `pull` command. Images being used will need to be present in the machine from before or have been built in the same step. Could be useful to avoid hitting rate limits when you can be sure the operation is unnecessary. Note that it is possible other commands run in the plugin's lifecycle will trigger a pull of necessary images. ### `workdir` (optional, run only) -Specify the container working directory via `docker-compose run --workdir`. +Specify the container working directory via `docker-compose run --workdir`. This option is also used by [`mount-checkout`](#mount-checkout-optional-run-only-boolean) if it doesn't specify where to mount the checkout in the container. + +Example: `/app` ### `user` (optional, run only) @@ -359,6 +518,26 @@ Whether to match the user ID and group ID for the container user to the user ID Using this option ensures that any files created on shared mounts from within the container will be accessible to the host user. It is otherwise common to accidentally create root-owned files that Buildkite will be unable to remove, since containers by default run as the root user. +### `mount-ssh-agent` (optional, run-only, boolean or string) + +Whether to mount the ssh-agent socket (at `/ssh-agent`) from the host agent machine into the container or not. Instead of just `true` or `false`, you can specify absolute path in the container for the home directory of the user used to run on which the agent's `.ssh/known_hosts` will be mounted (by default, `/root`). + +Default: `false` + +### `mount-buildkite-agent` (optional, run-only, boolean) + +Whether to automatically mount the `buildkite-agent` binary and associated environment variables from the host agent machine into the container. + +Default: `false` + +### `mount-checkout` (optional, run-only, string or boolean) + +The absolute path where to mount the current working directory which contains your checked out codebase. + +If set to `true` it will mount onto `/workdir`, unless `workdir` is set, in which case that will be used. + +Default: `false` + ### `pull-retries` (optional) A number of times to retry failed docker pull. Defaults to 0. @@ -373,7 +552,22 @@ This option can also be configured on the agent machine using the environment va ### `cache-from` (optional, build only) -A list of images to pull caches from in the format `service:index.docker.io/myorg/myrepo/myapp:tag` before building, ignoring any failures. If multiple images are listed for a service, the first one to successfully pull will be used. Requires docker-compose file version `3.2+`. +A list of images to attempt pulling before building in the format `service:index.docker.io/myorg/myrepo/myapp:tag:group`, ignoring any failures, to allow docker to re-use layers. The parameters `service` and `image-repo` are mandatory, without them it won't work. For each combination of service and group, it will attempt to pull each in order until one is successful (the rest will be ignored). Those elements that don't have a group specified will use a `:default:` group. +Requires docker-compose file version `3.2+`. + +### `separator-cache-from` (optional, build only, single character) + +A single character that specifies the character to use for splitting elements in the `cache-from` option. + +By default it is `:` which should not be a problem unless your registry's URL contains a port, in which case you will have to use this option to specify a different character. + +**Important**: the tag to use is its own field, so you will have to specify elements like `service#registry:port/myrepo/myapp#tag#group` + +### `target` (optional, build only) + +Allow for intermediate builds with `--target VALUE` options. + +Note that there is a single build command run for all services so the target value will apply to all of them. ### `volumes` (optional, run only) @@ -381,6 +575,14 @@ A list of volumes to mount into the container. If a matching volume exists in th Additionally, volumes may be specified via the agent environment variable `BUILDKITE_DOCKER_DEFAULT_VOLUMES`, a `;` (semicolon) delimited list of mounts in the `-v` syntax. (Ex. `buildkite:/buildkite;./app:/app`). +### `expand-volume-vars` (optional, boolean, run only, unsafe) + +When set to true, it will activate interpolation of variables in the elements of the `volumes` configuration array. When turned off (the default), attempting to use variables will fail as the literal `$VARIABLE_NAME` string will be passed to the `-v` option. + +:warning: **Important:** this is considered an unsafe option as the most compatible way to achieve this is to run the strings through `eval` which could lead to arbitrary code execution or information leaking if you don't have complete control of the pipeline + +Note that rules regarding [environment variable interpolation](https://buildkite.com/docs/pipelines/environment-variables#runtime-variable-interpolation) apply here. That means that `$VARIABLE_NAME` is resolved at pipeline upload time, whereas `$$VARIABLE_NAME` will be at run time. All things being equal, you likely want to use `$$VARIABLE_NAME` on the variables mentioned in this option. + ### `graceful-shutdown` (optional, run only) Gracefully shuts down all containers via 'docker-compose stop`. @@ -413,10 +615,20 @@ The default is `true` on unix, `false` on windows ### `dependencies` (optional, run only) -If set to false, doesn't start linked services. +If set to false, runs with `--no-deps` and doesn't start linked services. The default is `true`. +### `pre-run-dependencies` (optional, run only) + +If `dependencies` are activated (which is the default), you can skip starting them up before the main container by setting this option to `false`. This is useful if you want compose to take care of that on its own at the expense of messier output in the run step. + +### `wait` (optional, run only) + +Whether to wait for dependencies to be up (and healthy if possible) when starting them up. It translates to using [`--wait` in the docker-compose up] command. + +Defaults to `false`. + ### `ansi` (optional, run only) If set to false, disables the ansi output from containers. @@ -435,16 +647,46 @@ Sets `docker-compose` to run with `--verbose` The default is `false`. +### `quiet-pull` (optional, run only) + +Start up dependencies with `--quiet-pull` to prevent even more logs during that portion of the execution. + +The default is `false`. + ### `rm` (optional, run only) If set to true, docker compose will remove the primary container after run. Equivalent to `--rm` in docker-compose. The default is `true`. +### `run-labels` (optional, run only) + +If set to true, adds useful Docker labels to the primary container. See [Container Labels](#container-labels) for more info. + +The default is `true`. + +### `compatibility` (optional, run only) + +If set to true, all docker compose commands will rum with compatibility mode. Equivalent to `--compatibility` in docker-compose. + +The default is `false`. + +Note that [the effect of this option changes depending on your docker compose CLI version](https://docs.docker.com/compose/cli-command-compatibility/#flags-that-will-not-be-implemented): +* in v1 it translates (composefile) v3 deploy keys to their non-swarm (composefile) v2 equivalents +* in v2 it will revert some behaviour to v1 as well, including (but not limited to): + - [Character separator for container names](https://github.com/docker/compose/blob/a0acc20d883ce22b8b0c65786e3bea1328809bbd/cmd/compose/compose.go#L181) + - [Not normalizing compose models (when running `config`)](https://github.com/docker/compose/blob/2e7644ff21f9ca0ea6fb5e8d41d4f6af32cd7e20/cmd/compose/convert.go#L69) + ### `entrypoint` (optional, run only) Sets the `--entrypoint` argument when running `docker-compose`. +### `service-ports` (optional, run only) + +If set to true, docker compose will run with the service ports enabled and mapped to the host. Equivalent to `--service-ports` in docker-compose. + +The default is `false`. + ### `upload-container-logs` (optional, run only) Select when to upload container logs. @@ -455,12 +697,30 @@ Select when to upload container logs. The default is `on-error`. +### `cli-version` (optional, string or integer) + +If set to `2`, plugin will use `docker compose` to execute commands; otherwise it will default to version `1`, using `docker-compose` instead. + +### `buildkit` (optional, build only, boolean) + +Assuming you have a compatible docker installation and configuration in the agent, activating this option would setup the environment for the `docker-compose build` call to use BuildKit. Note that if you are using `cli-version` 2, you are already using buildkit by default. + +You may want to also add `BUILDKIT_INLINE_CACHE=1` to your build arguments (`args` option in this plugin), but know that [there are known issues with it](https://github.com/moby/buildkit/issues/2274). + +### `ssh` (optional, build only, boolean or string) + +It will add the `--ssh` option to the build command with the passed value (if `true` it will use `default`). Note that it assumes you have a compatible docker installation and configuration in the agent (meaning you are using BuildKit and it is correctly setup). + +### `secrets` (optional, build only, array of strings) + +All elements in this array will be passed literally to the `build` command as parameters of the [`--secrets` option](https://docs.docker.com/engine/reference/commandline/buildx_build/#secret). Note that you must have BuildKit enabled for this option to have any effect and special `RUN` stanzas in your Dockerfile to actually make use of them. + ## Developing To run the tests: ```bash -docker-compose run --rm tests +docker-compose run --rm tests bats tests tests/v2 ``` ## License diff --git a/commands/build.sh b/commands/build.sh index cce943dd..1c0dcedd 100755 --- a/commands/build.sh +++ b/commands/build.sh @@ -4,12 +4,40 @@ set -ueo pipefail image_repository="$(plugin_read_config IMAGE_REPOSITORY)" pull_retries="$(plugin_read_config PULL_RETRIES "0")" push_retries="$(plugin_read_config PUSH_RETRIES "0")" +separator="$(plugin_read_config SEPARATOR_CACHE_FROM ":")" override_file="docker-compose.buildkite-${BUILDKITE_BUILD_NUMBER}-override.yml" build_images=() +normalize_var_name() { + local orig_value="$1" + # POSIX variable names should match [a-zA-Z_][a-zA-Z0-9_]* + # service names and the like also allow periods and dashes + no_periods="${orig_value//./_}" + no_dashes="${no_periods//-/_}" + echo "${no_dashes}" +} + service_name_cache_from_var() { local service_name="$1" - echo "cache_from__${service_name//-/_}" + echo "cache_from__$(normalize_var_name "${service_name}")" +} + +service_name_group_name_cache_from_var() { + local service_name="$1" + local group_index="$2" + echo "group_cache_from__$(normalize_var_name "${service_name}")__$(normalize_var_name "${group_index}")" +} + +count_of_named_array() { + local tmp="$1[@]" + local copy=( "${!tmp}" ) + echo "${#copy[@]}" +} + +named_array_values() { + local tmp="$1[@]" + local copy=( "${!tmp}" ) + echo "${copy[@]}" } if [[ -z "$image_repository" ]] ; then @@ -17,22 +45,57 @@ if [[ -z "$image_repository" ]] ; then echo "This build step has no image-repository set. Without an image-repository, the Docker image won't be pushed to a repository, and won't be automatically used by any run steps." fi +if [[ "$(plugin_read_config BUILDKIT "false")" == "true" ]]; then + export DOCKER_BUILDKIT=1 + export COMPOSE_DOCKER_CLI_BUILD=1 + export BUILDKIT_PROGRESS=plain +fi + # Read any cache-from parameters provided and pull down those images first # If no-cache is set skip pulling the cache-from images if [[ "$(plugin_read_config NO_CACHE "false")" == "false" ]] ; then for line in $(plugin_read_list CACHE_FROM) ; do - IFS=':' read -r -a tokens <<< "$line" + IFS="${separator}" read -r -a tokens <<< "$line" service_name=${tokens[0]} - service_image=$(IFS=':'; echo "${tokens[*]:1}") + service_image=$(IFS=':'; echo "${tokens[*]:1:2}") + if [ ${#tokens[@]} -gt 2 ]; then + service_tag=${tokens[2]} + else + service_tag="latest" + fi + + if ! validate_tag "$service_tag"; then + echo "🚨 cache-from ${service_image} has an invalid tag so it will be ignored" + continue + fi + + cache_from_group_name=$(IFS=':'; echo "${tokens[*]:3}") + if [[ -z "$cache_from_group_name" ]]; then + cache_from_group_name=":default:" + fi + # The variable with this name will hold an array of group names: cache_image_name="$(service_name_cache_from_var "$service_name")" if [[ -n ${!cache_image_name+x} ]]; then - continue # skipping since there's already a pulled cache image for this service + if [[ "$(named_array_values "${cache_image_name}")" =~ ${cache_from_group_name} ]]; then + continue # skipping since there's already a pulled cache image for this service+group + fi fi - echo "~~~ :docker: Pulling cache image for $service_name" + echo "~~~ :docker: Pulling cache image for $service_name (group ${cache_from_group_name})" if retry "$pull_retries" plugin_prompt_and_run docker pull "$service_image" ; then - printf -v "$cache_image_name" "%s" "$service_image" + if [[ -z "${!cache_image_name+x}" ]]; then + declare -a "$cache_image_name" + cache_image_length=0 + else + cache_image_length="$(count_of_named_array "${cache_image_name}")" + fi + + declare "$cache_image_name+=( $cache_from_group_name )" + # The variable with this name will hold the image for the this group + # (based on index into the array of group names): + cache_from_group_var="$(service_name_group_name_cache_from_var "$service_name" "${cache_image_length}")" + printf -v "$cache_from_group_var" "%s" "$service_image" else echo "!!! :docker: Pull failed. $service_image will not be used as a cache for $service_name" fi @@ -45,6 +108,12 @@ fi service_idx=0 for service_name in $(plugin_read_list BUILD) ; do image_name=$(build_image_name "${service_name}" "${service_idx}") + + if ! validate_tag "$image_name"; then + echo "🚨 ${image_name} is not a valid tag name" + exit 1 + fi + service_idx=$((service_idx+1)) if [[ -n "$image_repository" ]] ; then @@ -55,9 +124,15 @@ for service_name in $(plugin_read_list BUILD) ; do cache_from_var="$(service_name_cache_from_var "${service_name}")" if [[ -n "${!cache_from_var-}" ]]; then - build_images+=("${!cache_from_var}") + cache_from_length="$(count_of_named_array "${cache_from_var}")" + build_images+=("${cache_from_length}") + + for i in $(seq 0 "$((cache_from_length-1))"); do + cache_from_group_var="$(service_name_group_name_cache_from_var "$service_name" "$i")" + build_images+=("${!cache_from_group_var}") + done else - build_images+=("") + build_images+=(0) fi done @@ -73,7 +148,11 @@ while read -r line ; do [[ -n "$line" ]] && services+=("$line") done <<< "$(plugin_read_list BUILD)" -build_params=(--pull) +build_params=(build) + +if [[ ! "$(plugin_read_config SKIP_PULL "false")" == "true" ]] ; then + build_params+=(--pull) +fi if [[ "$(plugin_read_config NO_CACHE "false")" == "true" ]] ; then build_params+=(--no-cache) @@ -83,12 +162,36 @@ if [[ "$(plugin_read_config BUILD_PARALLEL "false")" == "true" ]] ; then build_params+=(--parallel) fi +# Parse the list of secrets to pass on to build command +while read -r line ; do + [[ -n "$line" ]] && build_params+=("--secret" "$line") +done <<< "$(plugin_read_list SECRETS)" + +if [[ "$(plugin_read_config SSH "false")" != "false" ]] ; then + if [[ "${DOCKER_BUILDKIT:-}" != "1" && "${BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLI_VERSION:-}" != "2" ]]; then + echo "🚨 You can not use the ssh option if you are not using buildkit" + exit 1 + fi + + SSH_CONTEXT="$(plugin_read_config SSH)" + if [[ "${SSH_CONTEXT}" == "true" ]]; then + # ssh option was a boolean + SSH_CONTEXT='default' + fi + build_params+=(--ssh "${SSH_CONTEXT}") +fi + +target="$(plugin_read_config TARGET "")" +if [[ -n "$target" ]] ; then + build_params+=(--target "$target") +fi + while read -r arg ; do [[ -n "${arg:-}" ]] && build_params+=("--build-arg" "${arg}") done <<< "$(plugin_read_list ARGS)" echo "+++ :docker: Building services ${services[*]}" -run_docker_compose -f "$override_file" build "${build_params[@]}" "${services[@]}" +run_docker_compose -f "$override_file" "${build_params[@]}" "${services[@]}" if [[ -n "$image_repository" ]] ; then echo "~~~ :docker: Pushing built images to $image_repository" @@ -104,6 +207,7 @@ if [[ -n "$image_repository" ]] ; then done # pop-off the last build image - build_images=("${build_images[@]:3}") + # 3 for service, image, num_cache_from; plus num_cache_from + build_images=("${build_images[@]:(3 + ${build_images[2]})}") done fi diff --git a/commands/push.sh b/commands/push.sh index 564418da..bc96fb63 100755 --- a/commands/push.sh +++ b/commands/push.sh @@ -21,6 +21,15 @@ for line in $(plugin_read_list PUSH) ; do service_name=${tokens[0]} service_image=$(compose_image_for_service "$service_name") + # push in the form of service:repo:tag + # if the registry contains a port this means that the tag is mandatory + if [[ ${#tokens[@]} -gt 2 ]]; then + if ! validate_tag "${tokens[-1]}"; then + echo "🚨 specified image to push ${line} has an invalid tag so it will be ignored" + continue + fi + fi + # Pull down prebuilt image if one exists if prebuilt_image=$(get_prebuilt_image "$service_name") ; then diff --git a/commands/run.sh b/commands/run.sh index 0b7b7818..e7926369 100755 --- a/commands/run.sh +++ b/commands/run.sh @@ -1,6 +1,8 @@ #!/bin/bash set -ueo pipefail +. "$DIR/../lib/log.bash" + # Run takes a service name, pulls down any pre-built image for that name # and then runs docker-compose run a generated project name @@ -8,6 +10,8 @@ run_service="$(plugin_read_config RUN)" container_name="$(docker_compose_project_name)_${run_service}_build_${BUILDKITE_BUILD_NUMBER}" override_file="docker-compose.buildkite-${BUILDKITE_BUILD_NUMBER}-override.yml" pull_retries="$(plugin_read_config PULL_RETRIES "0")" +mount_checkout="$(plugin_read_config MOUNT_CHECKOUT "false")" +workdir='' expand_headers_on_error() { echo "^^^ +++" @@ -41,7 +45,7 @@ prebuilt_services=() for service_name in "${prebuilt_candidates[@]}" ; do if prebuilt_image=$(get_prebuilt_image "$service_name") ; then echo "~~~ :docker: Found a pre-built image for $service_name" - prebuilt_service_overrides+=("$service_name" "$prebuilt_image" "") + prebuilt_service_overrides+=("$service_name" "$prebuilt_image" 0) prebuilt_services+=("$service_name") # If it's prebuilt, we need to pull it down @@ -62,28 +66,34 @@ fi # If there are multiple services to pull, run it in parallel (although this is now the default) if [[ ${#pull_services[@]} -gt 1 ]] ; then - pull_params+=("pull" "--parallel" "${pull_services[@]}") + pull_params+=("pull" "--parallel" "--include-deps" "${pull_services[@]}") elif [[ ${#pull_services[@]} -eq 1 ]] ; then - pull_params+=("pull" "${pull_services[0]}") + pull_params+=("pull" "--include-deps" "${pull_services[0]}") fi # Pull down specified services -if [[ ${#pull_services[@]} -gt 0 ]] ; then +if [[ ${#pull_services[@]} -gt 0 ]] && [[ "$(plugin_read_config SKIP_PULL "false")" != "true" ]]; then echo "~~~ :docker: Pulling services ${pull_services[0]}" retry "$pull_retries" run_docker_compose "${pull_params[@]}" - - # Sometimes docker-compose pull leaves unfinished ansi codes - echo -fi - -# Optionally disable ansi output -if [[ "$(plugin_read_config ANSI "true")" == "false" ]] ; then - run_params+=(--no-ansi) fi # We set a predictable container name so we can find it and inspect it later on run_params+=("run" "--name" "$container_name") +if [[ "$(plugin_read_config RUN_LABELS "true")" =~ ^(true|on|1)$ ]]; then + # Add useful labels to run container + run_params+=( + "--label" "com.buildkite.pipeline_name=${BUILDKITE_PIPELINE_NAME}" + "--label" "com.buildkite.pipeline_slug=${BUILDKITE_PIPELINE_SLUG}" + "--label" "com.buildkite.build_number=${BUILDKITE_BUILD_NUMBER}" + "--label" "com.buildkite.job_id=${BUILDKITE_JOB_ID}" + "--label" "com.buildkite.job_label=${BUILDKITE_LABEL}" + "--label" "com.buildkite.step_key=${BUILDKITE_STEP_KEY}" + "--label" "com.buildkite.agent_name=${BUILDKITE_AGENT_NAME}" + "--label" "com.buildkite.agent_id=${BUILDKITE_AGENT_ID}" + ) +fi + # append env vars provided in ENV or ENVIRONMENT, these are newline delimited while IFS=$'\n' read -r env ; do [[ -n "${env:-}" ]] && run_params+=("-e" "${env}") @@ -91,6 +101,32 @@ done <<< "$(printf '%s\n%s' \ "$(plugin_read_list ENV)" \ "$(plugin_read_list ENVIRONMENT)")" +# Propagate all environment variables into the container if requested +if [[ "$(plugin_read_config PROPAGATE_ENVIRONMENT "false")" =~ ^(true|on|1)$ ]] ; then + if [[ -n "${BUILDKITE_ENV_FILE:-}" ]] ; then + # Read in the env file and convert to --env params for docker + # This is because --env-file doesn't support newlines or quotes per https://docs.docker.com/compose/env-file/#syntax-rules + while read -r var; do + run_params+=("-e" "${var%%=*}") + done < "${BUILDKITE_ENV_FILE}" + else + echo -n "🚨 Not propagating environment variables to container as \$BUILDKITE_ENV_FILE is not set" + fi +fi + +# If requested, propagate a set of env vars as listed in a given env var to the +# container. +if [[ -n "$(plugin_read_config ENV_PROPAGATION_LIST)" ]]; then + env_propagation_list_var="$(plugin_read_config ENV_PROPAGATION_LIST)" + if [[ -z "${!env_propagation_list_var:-}" ]]; then + echo -n "env-propagation-list desired, but ${env_propagation_list_var} is not defined!" + exit 1 + fi + for var in ${!env_propagation_list_var}; do + run_params+=("-e" "$var") + done +fi + while IFS=$'\n' read -r vol ; do [[ -n "${vol:-}" ]] && run_params+=("-v" "$(expand_relative_volume_path "$vol")") done <<< "$(plugin_read_list VOLUMES)" @@ -103,11 +139,23 @@ for vol in "${default_volumes[@]:-}" ; do [[ -n "$trimmed_vol" ]] && run_params+=("-v" "$(expand_relative_volume_path "$trimmed_vol")") done +# If there's a git mirror, mount it so that git references can be followed. +if [[ -n "${BUILDKITE_REPO_MIRROR:-}" ]]; then + run_params+=("-v" "$BUILDKITE_REPO_MIRROR:$BUILDKITE_REPO_MIRROR:ro") +fi + tty_default='true' +workdir_default="/workdir" +pwd_default="$PWD" +run_dependencies="true" # Set operating system specific defaults if is_windows ; then tty_default='false' + workdir_default="C:\\workdir" + # escaping /C is a necessary workaround for an issue with Git for Windows 2.24.1.2 + # https://github.com/git-for-windows/git/issues/2442 + pwd_default="$(cmd.exe //C "echo %CD%")" fi # Optionally disable allocating a TTY @@ -118,10 +166,26 @@ fi # Optionally disable dependencies if [[ "$(plugin_read_config DEPENDENCIES "true")" == "false" ]] ; then run_params+=(--no-deps) + run_dependencies="false" +elif [[ "$(plugin_read_config PRE_RUN_DEPENDENCIES "true")" == "false" ]]; then + run_dependencies="false" fi -if [[ -n "$(plugin_read_config WORKDIR)" ]] ; then - run_params+=("--workdir=$(plugin_read_config WORKDIR)") +if [[ -n "$(plugin_read_config WORKDIR)" ]] || [[ "${mount_checkout}" == "true" ]]; then + workdir="$(plugin_read_config WORKDIR "$workdir_default")" +fi + +if [[ -n "${workdir}" ]] ; then + run_params+=("--workdir=${workdir}") +fi + +if [[ "${mount_checkout}" == "true" ]]; then + run_params+=("-v" "${pwd_default}:${workdir}") +elif [[ "${mount_checkout}" =~ ^/.*$ ]]; then + run_params+=("-v" "${pwd_default}:${mount_checkout}") +elif [[ "${mount_checkout}" != "false" ]]; then + echo -n "🚨 mount-checkout should be either true or an absolute path to use as a mountpoint" + exit 1 fi # Can't set both user and propagate-uid-gid @@ -156,9 +220,65 @@ if [[ -n "$(plugin_read_config ENTRYPOINT)" ]] ; then run_params+=("$(plugin_read_config ENTRYPOINT)") fi +# Mount ssh-agent socket and known_hosts +if [[ ! "${BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT:-false}" = 'false' ]] ; then + if [[ -z "${SSH_AUTH_SOCK:-}" ]] ; then + echo "+++ 🚨 \$SSH_AUTH_SOCK isn't set, has ssh-agent started?" + exit 1 + fi + if [[ ! -S "${SSH_AUTH_SOCK}" ]] ; then + echo "+++ 🚨 The file at ${SSH_AUTH_SOCK} does not exist or is not a socket, was ssh-agent started?" + exit 1 + fi + + if [[ "${BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT:-''}" =~ ^(true|on|1)$ ]]; then + MOUNT_PATH=/root + else + MOUNT_PATH="${BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT}" + fi + + run_params+=( + "-e" "SSH_AUTH_SOCK=/ssh-agent" + "-v" "${SSH_AUTH_SOCK}:/ssh-agent" + "-v" "${HOME}/.ssh/known_hosts:${MOUNT_PATH}/.ssh/known_hosts" + ) +fi + +# Optionally handle the mount-buildkite-agent option +if [[ "$(plugin_read_config MOUNT_BUILDKITE_AGENT "false")" == "true" ]]; then + if [[ -z "${BUILDKITE_AGENT_BINARY_PATH:-}" ]] ; then + if ! command -v buildkite-agent >/dev/null 2>&1 ; then + echo -n "+++ 🚨 Failed to find buildkite-agent in PATH to mount into container, " + echo "you can disable this behaviour with 'mount-buildkite-agent:false'" + else + BUILDKITE_AGENT_BINARY_PATH=$(command -v buildkite-agent) + fi + fi +fi + +# Mount buildkite-agent if we have a path for it +if [[ -n "${BUILDKITE_AGENT_BINARY_PATH:-}" ]] ; then + run_params+=( + "-e" "BUILDKITE_JOB_ID" + "-e" "BUILDKITE_BUILD_ID" + "-e" "BUILDKITE_AGENT_ACCESS_TOKEN" + "-v" "$BUILDKITE_AGENT_BINARY_PATH:/usr/bin/buildkite-agent" + ) +fi + +# Optionally expose service ports +if [[ "$(plugin_read_config SERVICE_PORTS "false")" == "true" ]]; then + run_params+=(--service-ports) +fi + run_params+=("$run_service") -build_params=(--pull) +build_params=() + +# Only pull if SKIP_PULL is not true +if [[ ! "$(plugin_read_config SKIP_PULL "false")" == "true" ]] ; then + build_params+=(--pull) +fi if [[ "$(plugin_read_config NO_CACHE "false")" == "true" ]] ; then build_params+=(--no-cache) @@ -185,22 +305,37 @@ elif [[ ! -f "$override_file" ]]; then # for when an image and a build is defined in the docker-compose.ymk file, otherwise we try and # pull an image that doesn't exist run_docker_compose build "${build_params[@]}" "$run_service" +fi - # Sometimes docker-compose pull leaves unfinished ansi codes - echo +up_params+=("up") # this ensures that the array has elements to avoid issues with bash 4.3 + +if [[ "$(plugin_read_config WAIT "false")" == "true" ]] ; then + up_params+=("--wait") +fi + +if [[ "$(plugin_read_config QUIET_PULL "false")" == "true" ]] ; then + up_params+=("--quiet-pull") fi -# Start up service dependencies in a different header to keep the main run with less noise -if [[ "$(plugin_read_config DEPENDENCIES "true")" == "true" ]] ; then +dependency_exitcode=0 +if [[ "${run_dependencies}" == "true" ]] ; then + # Start up service dependencies in a different header to keep the main run with less noise echo "~~~ :docker: Starting dependencies" - if [[ ${#up_params[@]} -gt 0 ]] ; then - run_docker_compose "${up_params[@]}" up -d --scale "${run_service}=0" "${run_service}" - else - run_docker_compose up -d --scale "${run_service}=0" "${run_service}" + run_docker_compose "${up_params[@]}" -d --scale "${run_service}=0" "${run_service}" || dependency_exitcode=$? +fi + +if [[ $dependency_exitcode -ne 0 ]] ; then + # Dependent services failed to start. + echo "^^^ +++" + echo "+++ 🚨 Failed to start dependencies" + + if [[ -n "${BUILDKITE_AGENT_ACCESS_TOKEN:-}" ]] ; then + print_failed_container_information + + upload_container_logs "$run_service" fi - # Sometimes docker-compose leaves unfinished ansi codes - echo + return $dependency_exitcode fi shell=() @@ -274,6 +409,12 @@ if [[ ${#shell[@]} -gt 0 ]] ; then fi if [[ -n "${BUILDKITE_COMMAND}" ]] ; then + if [[ $(echo "$BUILDKITE_COMMAND" | wc -l) -gt 1 ]]; then + # An array of commands in the step will be a single string with multiple lines + # This breaks a lot of things here so we will print a warning for user to be aware + echo "⚠️ Warning: The command received has multiple lines." + echo "⚠️ The Docker Compose Plugin does not correctly support step-level array commands." + fi run_params+=("${BUILDKITE_COMMAND}") display_command+=("'${BUILDKITE_COMMAND}'") elif [[ ${#command[@]} -gt 0 ]] ; then @@ -283,57 +424,42 @@ elif [[ ${#command[@]} -gt 0 ]] ; then done fi -# Disable -e outside of the subshell; since the subshell returning a failure -# would exit the parent shell (here) early. -set +e +ensure_stopped() { + log 143 + echo '+++ :warning: Signal received, stopping container' + docker stop "${container_name}" || true + # compose_cleanup ${run_service} # This works but causes the docker process time to restart before the container dies. This must be a docker setting somewhere causing the restart, but I can't find it + echo '~~~ Last log lines that may be missing above (if container was not already removed)' + docker logs "${container_name}" || true + exit 143 +} -( - echo "+++ :docker: Running ${display_command[*]:-} in service $run_service" >&2 - run_docker_compose "${run_params[@]}" -) +trap 'ensure_stopped "$?"' SIGINT SIGTERM SIGQUIT -exitcode=$? +if [[ "${BUILDKITE_PLUGIN_DOCKER_COMPOSE_COLLAPSE_RUN_LOG_GROUP:-false}" = "true" ]]; then + group_type="---" +else + group_type="+++" +fi -# Restore -e as an option. -set -e +exitcode=0 +( + echo "${group_type} :docker: Running ${display_command[*]:-} in service $run_service" >&2 + run_docker_compose "${run_params[@]}" +) || exitcode=$? if [[ $exitcode -ne 0 ]] ; then echo "^^^ +++" - echo "+++ :warning: Failed to run command, exited with $exitcode" + echo "+++ :warning: Failed to run command, exited with $exitcode, run params:" + echo "${run_params[@]}" fi if [[ -n "${BUILDKITE_AGENT_ACCESS_TOKEN:-}" ]] ; then if [[ "$(plugin_read_config CHECK_LINKED_CONTAINERS "true")" != "false" ]] ; then + print_failed_container_information - # Get list of failed containers - containers=() - while read -r container ; do - [[ -n "$container" ]] && containers+=("$container") - done <<< "$(docker_ps_by_project -q)" - - failed_containers=() - if [[ 0 != "${#containers[@]}" ]] ; then - while read -r container ; do - [[ -n "$container" ]] && failed_containers+=("$container") - done <<< "$(docker inspect -f '{{if ne 0 .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{ end }}' \ - "${containers[@]}")" - fi - - if [[ 0 != "${#failed_containers[@]}" ]] ; then - echo "+++ :warning: Some containers had non-zero exit codes" - docker_ps_by_project \ - --format 'table {{.Label "com.docker.compose.service"}}\t{{ .ID }}\t{{ .Status }}' - fi - - check_linked_containers_and_save_logs \ - "$run_service" "docker-compose-logs" \ - "$(plugin_read_config UPLOAD_CONTAINER_LOGS "on-error")" - - if [[ -d "docker-compose-logs" ]] && test -n "$(find docker-compose-logs/ -maxdepth 1 -name '*.log' -print)"; then - echo "~~~ Uploading linked container logs" - buildkite-agent artifact upload "docker-compose-logs/*.log" - fi + upload_container_logs "$run_service" fi fi -return $exitcode +return "$exitcode" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 8023abd9..7b38f95b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ version: '2' services: tests: - image: buildkite/plugin-tester + image: buildkite/plugin-tester:v4.1.0 volumes: - ".:/plugin" diff --git a/hooks/command b/hooks/command index 8e0692b1..41c857ca 100755 --- a/hooks/command +++ b/hooks/command @@ -12,14 +12,6 @@ commands=() [[ -n "$(plugin_read_list BUILD)" ]] && commands+=("BUILD") [[ -n "$(plugin_read_list RUN)" ]] && commands+=("RUN") - -# Check we've only got one of BUILD or RUN -if [[ ${#commands[@]} -gt 1 ]] ; then - echo "+++ Docker Compose plugin error" - echo "Only one of build or run is supported. More than one was used." - exit 1 -fi - [[ -n "$(plugin_read_list PUSH)" ]] && commands+=("PUSH") # Don't convert paths on gitbash on windows diff --git a/hooks/pre-exit b/hooks/pre-exit index f3bd61d4..8f1b4d82 100755 --- a/hooks/pre-exit +++ b/hooks/pre-exit @@ -15,5 +15,5 @@ if [[ -n "$(plugin_read_list RUN)" ]] && [[ "$(plugin_read_config CLEANUP "true" . "$DIR/../lib/run.bash" echo "~~~ :docker: Cleaning up after docker-compose" >&2 - compose_cleanup + compose_cleanup "" fi diff --git a/lib/log.bash b/lib/log.bash new file mode 100644 index 00000000..d66e4980 --- /dev/null +++ b/lib/log.bash @@ -0,0 +1,40 @@ +#!/bin/bash + +log() { + msg="SIG $1 received, process exiting" + echo "${msg}" + buildkite-agent meta-data set "dd_tags.job-signal-${BUILDKITE_STEP_KEY}" "$1" + buildkite-agent meta-data set "dd_tags.step-error-code-${BUILDKITE_STEP_KEY}" "$1" + buildkite-agent meta-data set "dd_tags.job-error-code-${BUILDKITE_JOB_ID}" "$1" + + echo "$(pidof buildkite-agent) is the pid of the buildkite agent" || true + + send_job_signaled_to_dd "${msg}" "${1}" +} + +send_job_signaled_to_dd() { + send_event_to_dd '{ "title": "Job '"${BUILDKITE_STEP_KEY}"' received signal", "text": "'"${1}"'", "alert_type": "error", "tags": [ "ci:job_signal", "exit_status:'"${2}"'", "job_name:'"${BUILDKITE_STEP_KEY}"'", "build_id:'"${BUILDKITE_BUILD_ID}"'", "branch:'"${BUILDKITE_BRANCH}"'", "hs_source:docker_compose_plugin", "env:ci" ] }' +} + +send_event_to_dd() { + if command -v curl >/dev/null 2>&1; then + echo "Using curl to send event to Datadog" + curl -X POST "https://api.datadoghq.com/api/v1/events" \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + -H "DD-API-KEY: ${DD_API_KEY}" \ + -d "$1" + elif command -v wget >/dev/null 2>&1; then + echo "Using wget to send event to Datadog" + wget \ + --header="Accept: application/json" \ + --header="Content-Type: application/json" \ + --header="DD-API-KEY: ${DD_API_KEY}" \ + --post-data="$1" \ + --output-document - \ + https://api.datadoghq.com/api/v1/events + else + echo "No suitable network tool found to send event to Datadog" + exit 1 + fi +} diff --git a/lib/push.bash b/lib/push.bash index 7714352f..1025c974 100644 --- a/lib/push.bash +++ b/lib/push.bash @@ -20,8 +20,13 @@ compose_image_for_service() { default_compose_image_for_service() { local service="$1" + + local separator="_" + if [[ "$(plugin_read_config CLI_VERSION "1")" == "2" ]] && [[ "$(plugin_read_config COMPATIBILITY "false")" != "true" ]] ; then + separator="-" + fi - printf '%s_%s\n' "$(docker_compose_project_name)" "$service" + printf '%s%s%s\n' "$(docker_compose_project_name)" "$separator" "$service" } docker_image_exists() { diff --git a/lib/run.bash b/lib/run.bash index bd057ab7..522ef069 100644 --- a/lib/run.bash +++ b/lib/run.bash @@ -1,14 +1,15 @@ #!/bin/bash -compose_cleanup() { - if [[ "$(plugin_read_config GRACEFUL_SHUTDOWN 'false')" == "false" ]]; then - # Send all containers a SIGKILL - run_docker_compose kill || true - else - # Send all containers a friendly SIGTERM, followed by a SIGKILL after exceeding the stop_grace_period - run_docker_compose stop || true +kill_or_wait_for_stop() { + + if [[ "$(plugin_read_config GRACEFUL_SHUTDOWN 'false')" == "true" ]]; then + # This will block until the container exits + run_docker_compose wait "$1" + container_exit_code=$? + echo "exit code was $container_exit_code" fi + # This will kill the container if it hasn't exited yet # `compose down` doesn't support force removing images if [[ "$(plugin_read_config LEAVE_VOLUMES 'false')" == "false" ]]; then run_docker_compose rm --force -v || true @@ -18,9 +19,21 @@ compose_cleanup() { # Stop and remove all the linked services and network if [[ "$(plugin_read_config LEAVE_VOLUMES 'false')" == "false" ]]; then - run_docker_compose down --volumes || true + run_docker_compose down --remove-orphans --volumes || true else - run_docker_compose down || true + run_docker_compose down --remove-orphans || true + fi +} + +compose_cleanup() { + kill_or_wait_for_stop "$1" & + sleep 1 + + # No need to call kill directly for GRACEFUL_SHUTDOWN == false since rm --force will send the same kill signal + if [[ "$(plugin_read_config GRACEFUL_SHUTDOWN 'false')" == "true" ]]; then + echo "graceful shutdown was true, stopping ${1}" + # Send all containers a friendly SIGTERM, followed by a SIGKILL after exceeding the stop_grace_period + run_docker_compose stop "$1" || true fi } @@ -72,11 +85,18 @@ check_linked_containers_and_save_logs() { } # docker-compose's -v arguments don't do local path expansion like the .yml -# versions do. So we add very simple support, for the common and basic case. +# versions do. So we add very simple support for the common and basic case. # # "./foo:/foo" => "/buildkite/builds/.../foo:/foo" expand_relative_volume_path() { - local path="$1" + local path + + if [[ "$(plugin_read_config EXPAND_VOLUME_VARS 'false')" == "true" ]]; then + path=$(eval echo "$1") + else + path="$1" + fi + local pwd="$PWD" # docker-compose's -v expects native paths on windows, so convert back. @@ -86,5 +106,46 @@ expand_relative_volume_path() { pwd="$(cygpath -w "$PWD")" fi + + echo "${path/.\//$pwd/}" } + +# Prints information about the failed containers. +function print_failed_container_information() { + # Get list of failed containers + containers=() + while read -r container ; do + [[ -n "$container" ]] && containers+=("$container") + done <<< "$(docker_ps_by_project -q)" + + failed_containers=() + if [[ 0 != "${#containers[@]}" ]] ; then + while read -r container ; do + [[ -n "$container" ]] && failed_containers+=("$container") + done <<< "$(docker inspect -f '{{if ne 0 .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{ end }}' \ + "${containers[@]}")" + fi + + if [[ 0 != "${#failed_containers[@]}" ]] ; then + echo "+++ :warning: Some containers had non-zero exit codes" + docker_ps_by_project \ + --format 'table {{.Label "com.docker.compose.service"}}\t{{ .ID }}\t{{ .Status }}' + fi +} + +# Uploads the container's logs, respecting the `UPLOAD_CONTAINER_LOGS` option +function upload_container_logs() { + run_service="$1" + + if [[ -n "${BUILDKITE_AGENT_ACCESS_TOKEN:-}" ]] ; then + check_linked_containers_and_save_logs \ + "$run_service" "docker-compose-logs" \ + "$(plugin_read_config UPLOAD_CONTAINER_LOGS "on-error")" + + if [[ -d "docker-compose-logs" ]] && test -n "$(find docker-compose-logs/ -maxdepth 1 -name '*.log' -print)"; then + echo "~~~ Uploading linked container logs" + buildkite-agent artifact upload "docker-compose-logs/*.log" + fi + fi +} diff --git a/lib/shared.bash b/lib/shared.bash index 375cae28..986eaf9a 100644 --- a/lib/shared.bash +++ b/lib/shared.bash @@ -17,8 +17,16 @@ function plugin_prompt() { # Shows the command being run, and runs it function plugin_prompt_and_run() { + local exit_code + plugin_prompt "$@" "$@" + exit_code=$? + + # Sometimes docker-compose pull leaves unfinished ansi codes + echo + + return $exit_code } # Shows the command about to be run, and exits if it fails @@ -102,7 +110,7 @@ function docker_compose_config_files() { # Use a default if there are no config files specified if [[ -z "${config_files[*]:-}" ]] ; then - echo "docker-compose.yml" + echo "${COMPOSE_FILE:-docker-compose.yml}" return fi @@ -115,7 +123,7 @@ function docker_compose_config_files() { # Returns the version from the output of docker_compose_config function docker_compose_config_version() { IFS=$'\n' read -r -a config <<< "$(docker_compose_config_files)" - awk '/^\s*version:/ { print $2; }' < "${config[0]}" | sed "s/[\"']//g" + grep 'version' < "${config[0]}" | sort -r | awk '/^\s*version:/ { print $2; exit; }' | sed "s/[\"']//g" } # Build an docker-compose file that overrides the image for a set of @@ -128,24 +136,27 @@ function build_image_override_file() { # Checks that a specific version of docker-compose supports cache_from function docker_compose_supports_cache_from() { local version="$1" - if [[ -z "$version" || "$version" == 1* || "$version" =~ ^(2|3)(\.[01])?$ ]] ; then + if [[ "$version" == 1* || "$version" =~ ^(2|3)(\.[01])?$ ]] ; then return 1 fi } # Build an docker-compose file that overrides the image for a specific -# docker-compose version and set of [ service, image, cache_from ] tuples +# docker-compose version and set of [ service, image, num_cache_from, cache_from1, cache_from2, ... ] tuples function build_image_override_file_with_version() { local version="$1" - if [[ -z "$version" ]]; then + if [[ "$version" == 1* ]] ; then echo "The 'build' option can only be used with Compose file versions 2.0 and above." echo "For more information on Docker Compose configuration file versions, see:" echo "https://docs.docker.com/compose/compose-file/compose-versioning/#versioning" exit 1 fi - printf "version: '%s'\\n" "$version" + if [[ -n "$version" ]]; then + printf "version: '%s'\\n" "$version" + fi + printf "services:\\n" shift @@ -153,7 +164,7 @@ function build_image_override_file_with_version() { printf " %s:\\n" "$1" printf " image: %s\\n" "$2" - if [[ -n "$3" ]] ; then + if [[ "$3" -gt 0 ]] ; then if ! docker_compose_supports_cache_from "$version" ; then echo "Unsupported Docker Compose config file version: $version" echo "The 'cache_from' option can only be used with Compose file versions 2.2 or 3.2 and above." @@ -164,7 +175,10 @@ function build_image_override_file_with_version() { printf " build:\\n" printf " cache_from:\\n" - printf " - %s\\n" "$3" + for cache_from_i in $(seq 4 "$((3 + $3))"); do + printf " - %s\\n" "${!cache_from_i}" + done + shift "$3" fi shift 3 @@ -174,11 +188,23 @@ function build_image_override_file_with_version() { # Runs the docker-compose command, scoped to the project, with the given arguments function run_docker_compose() { local command=(docker-compose) + if [[ "$(plugin_read_config CLI_VERSION "1")" == "2" ]] ; then + command=(docker compose) + fi if [[ "$(plugin_read_config VERBOSE "false")" == "true" ]] ; then command+=(--verbose) fi + if [[ "$(plugin_read_config ANSI "true")" == "false" ]] ; then + command+=(--no-ansi) + fi + + # Enable compatibility mode for v3 files + if [[ "$(plugin_read_config COMPATIBILITY "false")" == "true" ]]; then + command+=(--compatibility) + fi + for file in $(docker_compose_config_files) ; do command+=(-f "$file") done @@ -237,3 +263,13 @@ function is_windows() { function is_macos() { [[ "$OSTYPE" =~ ^(darwin) ]] } + +function validate_tag { + local tag=$1 + + if [[ "$tag" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then + return 0 + else + return 1 + fi +} \ No newline at end of file diff --git a/plugin.yml b/plugin.yml index 39db0041..8eb4291a 100644 --- a/plugin.yml +++ b/plugin.yml @@ -14,65 +14,113 @@ configuration: push: type: [ string, array ] minimum: 1 - pull: + ansi: + type: boolean + args: type: [ string, array ] minimum: 1 - config: + build-alias: type: [ string, array ] minimum: 1 - env: + buildkit: + type: boolean + cache-from: type: [ string, array ] minimum: 1 - environment: + compatibility: + type: boolean + cli-version: + oneOf: + - type: string + enum: [ "1", "2" ] + - type: integer + enum: [ 1, 2 ] + command: + type: array + collapse-run-log-group: + type: boolean + config: type: [ string, array ] minimum: 1 - args: + dependencies: + type: boolean + entrypoint: + type: string + env: type: [ string, array ] minimum: 1 - build-alias: + environment: type: [ string, array ] minimum: 1 + env-propagation-list: + type: string + expand-volume-vars: + type: boolean image-repository: type: string image-name: - type: string - pull-retries: - type: integer - push-retries: - type: integer - cache-from: type: [ string, array ] - minimum: 1 - volumes: - type: [ string, array ] - minimum: 1 - command: - type: array - skip-checkout: - type: boolean leave-volumes: type: boolean + mount-buildkite-agent: + type: boolean + mount-ssh-agent: + type: [ boolean, string ] + mount-checkout: + type: [ boolean, string ] no-cache: type: boolean - use-aliases: + pre-run-dependencies: type: boolean - tty: + propagate-environment: type: boolean - dependencies: + propagate-uid-gid: type: boolean - ansi: + pull: + type: [ string, array ] + minimum: 1 + pull-retries: + type: integer + push-retries: + type: integer + quiet-pull: type: boolean - verbose: + rm: type: boolean - workdir: + run-labels: + type: boolean + separator-cache-from: type: string - rm: + minLength: 1 + maxLength: 1 + service-ports: + type: boolean + skip-checkout: + type: boolean + skip-pull: + type: boolean + ssh: + type: [ boolean, string ] + secrets: + type: array + items: + type: string + target: + type: string + tty: type: boolean upload-container-logs: type: string - propagate-uid-gid: + use-aliases: type: boolean - oneOf: + verbose: + type: boolean + volumes: + type: [ string, array ] + minimum: 1 + workdir: + type: string + anyOf: - required: - run - required: @@ -81,19 +129,32 @@ configuration: - push additionalProperties: false dependencies: - pull: [ run ] - image-repository: [ build ] - image-name: [ build ] + ansi: [ run ] + buildkit: [ build ] + cache-from: [ build ] + cache-from-separator: [ cache-from ] + dependencies: [ run ] env: [ run ] environment: [ run ] - push-retries: [ push ] - cache-from: [ build ] - volumes: [ run ] + expand-volume-vars: [ volumes ] + image-repository: [ build ] + image-name: [ build ] leave-volumes: [ run ] - use-aliases: [ run ] - dependencies: [ run ] - ansi: [ run ] + mount-buildkite-agent: [ run ] + mount-checkout: [ run ] + pre-run-dependencies: [ run ] + propagate-uid-gid: [ run ] + pull: [ run ] + push-retries: [ push ] + quiet-pull: [ run ] + service-ports: [ run ] + skip-pull: [ build, run ] + secrets: [ buildkit, build ] + ssh: [ buildkit ] + target: [ build ] tty: [ run ] - workdir: [ run ] + use-aliases: [ run ] user: [ run ] - propagate-uid-gid: [ run ] + volumes: [ run ] + wait: [ run ] + workdir: [ run ] diff --git a/renovate.json b/renovate.json index da407de5..1b4560b6 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,7 @@ { "extends": [ - "config:base" + "config:base", + ":disableDependencyDashboard" ], "docker-compose": { "digest": { diff --git a/tests/build.bats b/tests/build.bats index 50a00b45..8ed91f13 100644 --- a/tests/build.bats +++ b/tests/build.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' # export DOCKER_COMPOSE_STUB_DEBUG=/dev/stdout @@ -16,7 +16,7 @@ load '../lib/shared' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command unstub docker-compose assert_success @@ -33,7 +33,7 @@ load '../lib/shared' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --no-cache myservice : echo built myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -50,7 +50,7 @@ load '../lib/shared' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --parallel myservice : echo built myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -68,7 +68,24 @@ load '../lib/shared' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --build-arg MYARG=0 --build-arg MYARG=1 myservice : echo built myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker-compose +} + +@test "Build with docker-compose and v1 is set explicitly " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLI_VERSION=1 + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" + + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -89,7 +106,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -117,7 +134,7 @@ load '../lib/shared' "meta-data set docker-compose-plugin-built-image-tag-myservice-1 my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice-1" \ "meta-data set docker-compose-plugin-built-image-tag-myservice-2 my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice-2" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -146,7 +163,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -171,7 +188,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -197,7 +214,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -223,7 +240,7 @@ load '../lib/shared' "meta-data set docker-compose-plugin-built-image-tag-myservice1 my.repository/llamas:test-myservice1-build-1 : echo set image metadata for myservice1" \ "meta-data set docker-compose-plugin-built-image-tag-myservice2 my.repository/llamas:test-myservice2-build-1 : echo set image metadata for myservice2" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built all services" @@ -241,7 +258,7 @@ load '../lib/shared' export BUILDKITE_PIPELINE_SLUG=test export BUILDKITE_BUILD_NUMBER=1 - run $PWD/hooks/command + run "$PWD"/hooks/command assert_failure assert_output --partial "Compose file versions 2.0 and above" @@ -261,7 +278,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled cache image" @@ -271,6 +288,32 @@ load '../lib/shared' unstub docker-compose } +@test "Build with a cache-from image and custom separator" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0='helloworld#my.repository:port/myservice_cache#latest' + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SEPARATOR_CACHE_FROM='#' + + stub docker \ + "pull my.repository:port/myservice_cache:latest : echo pulled cache image" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository:port/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker + unstub docker-compose +} + @test "Build with a cache-from image with no-cache also set" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" export BUILDKITE_JOB_ID=1111 @@ -283,7 +326,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --no-cache helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success refute_output --partial "pulled cache image" @@ -292,6 +335,52 @@ load '../lib/shared' unstub docker-compose } +@test "Build with an invalid cache-from tag" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:-latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + refute_output --partial "pulled cache image" + refute_output --partial "- my.repository/myservice_cache:-latest" + assert_output --partial "invalid tag so it will be ignored" + assert_output --partial "built helloworld" + unstub docker-compose +} + +@test "Build with a cache-from image with no tag" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + + stub docker \ + "pull my.repository/myservice_cache : echo pulled cache image" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/myservice_cache" + assert_output --partial "built helloworld" + unstub docker + unstub docker-compose +} + @test "Build with several cache-from images for one service" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" export BUILDKITE_JOB_ID=1111 @@ -307,7 +396,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled cache image" @@ -318,6 +407,158 @@ load '../lib/shared' unstub docker-compose } +@test "Build with several cache-from image groups for one service" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:build-target-build-1:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld:my.repository/myservice_cache:build-target-latest:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_2=helloworld:my.repository/myservice_cache:install-target-build-1:target2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_3=helloworld:my.repository/myservice_cache:branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_4=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:build-target-build-1 : echo pulled cache image build-target" \ + "pull my.repository/myservice_cache:install-target-build-1 : echo pulled cache image install-target" \ + "pull my.repository/myservice_cache:branch-name : echo pulled cache image branch-name" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image build-target" + assert_output --partial "pulled cache image install-target" + assert_output --partial "pulled cache image branch-name" + assert_output --partial "- my.repository/myservice_cache:build-target-build-1" + refute_output --partial "- my.repository/myservice_cache:build-target-latest" + assert_output --partial "- my.repository/myservice_cache:install-target-build-1" + assert_output --partial "- my.repository/myservice_cache:branch-name" + refute_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker + unstub docker-compose +} + +@test "Build with several cache-from image groups for one service with failures" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:build-target-build-1:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld:my.repository/myservice_cache:build-target-latest:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_2=helloworld:my.repository/myservice_cache:install-target-build-1:target2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_3=helloworld:my.repository/myservice_cache:branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_4=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:build-target-build-1 : exit 1" \ + "pull my.repository/myservice_cache:build-target-latest : echo pulled cache image build-target-latest" \ + "pull my.repository/myservice_cache:install-target-build-1 : echo pulled cache image install-target" \ + "pull my.repository/myservice_cache:branch-name : echo pulled cache image branch-name" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image build-target-latest" + assert_output --partial "pulled cache image install-target" + assert_output --partial "pulled cache image branch-name" + assert_output --partial "- my.repository/myservice_cache:build-target-latest" + assert_output --partial "- my.repository/myservice_cache:build-target-latest" + assert_output --partial "- my.repository/myservice_cache:install-target-build-1" + assert_output --partial "- my.repository/myservice_cache:branch-name" + refute_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker + unstub docker-compose +} + +@test "Build with several cache-from image groups for one service with failures and separator" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld#my.repository:port/myservice_cache#build-target-build-1#target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld#my.repository:port/myservice_cache#build-target-latest#target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_2=helloworld#my.repository:port/myservice_cache#install-target-build-1#target2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_3=helloworld#my.repository:port/myservice_cache#branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_4=helloworld#my.repository:port/myservice_cache#latest + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SEPARATOR_CACHE_FROM='#' + + stub docker \ + "pull my.repository:port/myservice_cache:build-target-build-1 : exit 1" \ + "pull my.repository:port/myservice_cache:build-target-latest : echo pulled cache image build-target-latest" \ + "pull my.repository:port/myservice_cache:install-target-build-1 : echo pulled cache image install-target" \ + "pull my.repository:port/myservice_cache:branch-name : echo pulled cache image branch-name" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image build-target-latest" + assert_output --partial "pulled cache image install-target" + assert_output --partial "pulled cache image branch-name" + assert_output --partial "- my.repository:port/myservice_cache:build-target-latest" + assert_output --partial "- my.repository:port/myservice_cache:build-target-latest" + assert_output --partial "- my.repository:port/myservice_cache:install-target-build-1" + assert_output --partial "- my.repository:port/myservice_cache:branch-name" + refute_output --partial "- my.repository:port/myservice_cache:latest" + assert_output --partial "built helloworld" + + unstub docker + unstub docker-compose +} + +@test "Build with several cache-from image groups out of order" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld:my.repository/myservice_cache:build-target-build-1:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_2=helloworld:my.repository/myservice_cache:install-target-build-1:target2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_3=helloworld:my.repository/myservice_cache:build-target-latest:target1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_4=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:branch-name : exit 1" \ + "pull my.repository/myservice_cache:build-target-build-1 : exit 1" \ + "pull my.repository/myservice_cache:install-target-build-1 : echo pulled cache image install-target" \ + "pull my.repository/myservice_cache:build-target-latest : echo pulled cache image build-target-latest" \ + "pull my.repository/myservice_cache:latest : echo pulled cache image branch-name-latest" + + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image build-target-latest" + assert_output --partial "pulled cache image install-target" + assert_output --partial "pulled cache image branch-name" + assert_output --partial "- my.repository/myservice_cache:build-target-latest" + assert_output --partial "- my.repository/myservice_cache:build-target-latest" + assert_output --partial "- my.repository/myservice_cache:install-target-build-1" + refute_output --partial "- my.repository/myservice_cache:branch-name" + assert_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker + unstub docker-compose +} + @test "Build with several cache-from images for one service with first image being not available" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" export BUILDKITE_JOB_ID=1111 @@ -334,7 +575,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled cache image" @@ -359,7 +600,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "my.repository/myservice_cache:latest will not be used as a cache for helloworld" @@ -383,7 +624,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull hello-world : echo built hello-world" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled cache image" @@ -393,6 +634,31 @@ load '../lib/shared' unstub docker-compose } +@test "Build with a service name and cache-from with period" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=hello.world + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=hello.world:my.repository/my-service_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/my-service_cache:latest : echo pulled cache image" + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull \* : echo built \$9" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/my-service_cache:latest" + assert_output --partial "built hello.world" + unstub docker + unstub docker-compose +} + + @test "Build with a cache-from image retry on failing pull" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" export BUILDKITE_JOB_ID=1111 @@ -410,7 +676,7 @@ load '../lib/shared' stub docker-compose \ "-f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled cache image" @@ -435,7 +701,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:my-llamas-image : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -445,6 +711,46 @@ load '../lib/shared' unstub buildkite-agent } +@test "Build with an invalid image-name (start with hyphen) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=-llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "-llamas-image is not a valid tag name" +} + +@test "Build with an invalid image-name (start with period) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=.llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial ".llamas-image is not a valid tag name" +} + +@test "Build with an invalid image-name (too long) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + # shellcheck disable=SC2155 # numbers from 1 to 69 result in 129 characters + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME="$(seq 69 | tr -d "\n")" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "is not a valid tag name" +} + @test "Build with a custom image-name and a config" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" export BUILDKITE_JOB_ID=1111 @@ -461,7 +767,7 @@ load '../lib/shared' stub buildkite-agent \ "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v3.2.yml my.repository/llamas:my-llamas-image : echo set image metadata for myservice" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -489,7 +795,7 @@ load '../lib/shared' "meta-data set docker-compose-plugin-built-image-tag-myservice1 my.repository/llamas:my-llamas-image-1 : echo set image metadata for myservice1" \ "meta-data set docker-compose-plugin-built-image-tag-myservice2 my.repository/llamas:my-llamas-image-2 : echo set image metadata for myservice2" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built all services" @@ -499,3 +805,118 @@ load '../lib/shared' unstub docker-compose unstub buildkite-agent } + +@test "Build with target" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_TARGET=intermediate + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --target \* \* : echo built \${11} with target \${10}" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with target intermediate" + + unstub docker-compose +} + +@test "Build with ssh option (but no buildkit)" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SSH=true + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "You can not use the ssh option if you are not using buildkit" + refute_output --partial "built myservice" +} + +@test "Build with ssh option as true and buildkit" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILDKIT=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SSH=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --ssh default \* : echo built \${11} with ssh" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with ssh" + + unstub docker-compose +} + +@test "Build with ssh option as string and buildkit" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILDKIT=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SSH=context + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --ssh context \* : echo built \${11} with ssh" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with ssh" + + unstub docker-compose +} + +@test "Build with secrets" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SECRETS_0='id=test,file=~/.test' + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SECRETS_1='id=SECRET_VAR' + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --secret \* --secret \* \* : echo built \${13} with secrets \${10} and \${12}" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with secrets id=test,file=~/.test and id=SECRET_VAR" + + unstub docker-compose +} + +@test "Build without pull" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SKIP_PULL=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build myservice : echo built myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker-compose +} diff --git a/tests/cleanup.bats b/tests/cleanup.bats old mode 100644 new mode 100755 index fd0be0f4..1bec6d49 --- a/tests/cleanup.bats +++ b/tests/cleanup.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/run' @@ -17,13 +17,13 @@ load '../lib/run' export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=true stub docker-compose \ - "-f docker-compose.yml -p buildkite1111 kill : echo killing containers" \ - "-f docker-compose.yml -p buildkite1111 rm --force -v : echo removing stopped containers" \ - "-f docker-compose.yml -p buildkite1111 down --volumes : echo removing everything" + "-f docker-compose.yml -p buildkite1111 rm --force -v : echo killing and removing stopped containers" \ + "-f docker-compose.yml -p buildkite1111 down --remove-orphans --volumes : echo removing everything" - run $PWD/hooks/pre-exit + run "$PWD"/hooks/pre-exit assert_success assert_output --partial "Cleaning up after docker-compose" + unstub docker-compose } diff --git a/tests/composefiles/docker-compose.no-version.yml b/tests/composefiles/docker-compose.no-version.yml new file mode 100644 index 00000000..2f420f69 --- /dev/null +++ b/tests/composefiles/docker-compose.no-version.yml @@ -0,0 +1,2 @@ +helloworld: + build: . diff --git a/tests/composefiles/docker-compose.v1.0.yml b/tests/composefiles/docker-compose.v1.0.yml index 94500c85..4f343829 100644 --- a/tests/composefiles/docker-compose.v1.0.yml +++ b/tests/composefiles/docker-compose.v1.0.yml @@ -1,2 +1,3 @@ helloworld: - build: . \ No newline at end of file + build: . +version: '1.0' diff --git a/tests/composefiles/docker-compose.v2.0.with-version-arg-and-whitespace.yml b/tests/composefiles/docker-compose.v2.0.with-version-arg-and-whitespace.yml new file mode 100644 index 00000000..dc2e9643 --- /dev/null +++ b/tests/composefiles/docker-compose.v2.0.with-version-arg-and-whitespace.yml @@ -0,0 +1,7 @@ + services: + helloworld: + build: + context: . + args: + version: 73.976.3 + version: '2.0' \ No newline at end of file diff --git a/tests/composefiles/docker-compose.v3.2.with-version-arg.yml b/tests/composefiles/docker-compose.v3.2.with-version-arg.yml new file mode 100644 index 00000000..3256547f --- /dev/null +++ b/tests/composefiles/docker-compose.v3.2.with-version-arg.yml @@ -0,0 +1,8 @@ +services: + helloworld: + build: + context: . + args: + version: 73.976.3 + +version: "3.2" \ No newline at end of file diff --git a/tests/docker-compose-cleanup.bats b/tests/docker-compose-cleanup.bats index e30e104a..7cd3ada6 100644 --- a/tests/docker-compose-cleanup.bats +++ b/tests/docker-compose-cleanup.bats @@ -1,43 +1,40 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/run' -@test "Default cleanup of docker-compose" { +setup () { run_docker_compose() { + # shellcheck disable=2317 # funtion used by loaded scripts echo "$@" } +} + +@test "Default cleanup of docker-compose" { run compose_cleanup assert_success - assert_equal "${lines[0]}" "kill" - assert_equal "${lines[1]}" "rm --force -v" - assert_equal "${lines[2]}" "down --volumes" + assert_equal "${lines[0]}" "rm --force -v" + assert_equal "${lines[1]}" "down --remove-orphans --volumes" } @test "Possible to gracefully shutdown containers in docker-compose cleanup" { - export BUILDKITE_PLUGIN_DOCKER_COMPOSE_GRACEFUL_SHUTDOWN=1 - run_docker_compose() { - echo "$@" - } + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_GRACEFUL_SHUTDOWN="true" run compose_cleanup assert_success - assert_equal "${lines[0]}" "stop" - assert_equal "${lines[1]}" "rm --force -v" - assert_equal "${lines[2]}" "down --volumes" + assert_output --partial "wait" + assert_equal "${lines[1]}" "exit code was 0" + assert_equal "${lines[2]}" "rm --force -v" + assert_equal "${lines[3]}" "down --remove-orphans --volumes" } @test "Possible to skip volume destruction in docker-compose cleanup" { export BUILDKITE_PLUGIN_DOCKER_COMPOSE_LEAVE_VOLUMES=1 - run_docker_compose() { - echo "$@" - } run compose_cleanup assert_success - assert_equal "${lines[0]}" "kill" - assert_equal "${lines[1]}" "rm --force" - assert_equal "${lines[2]}" "down" -} \ No newline at end of file + assert_equal "${lines[0]}" "rm --force" + assert_equal "${lines[1]}" "down --remove-orphans" +} diff --git a/tests/docker-compose-config.bats b/tests/docker-compose-config.bats index aee136bd..910d2531 100644 --- a/tests/docker-compose-config.bats +++ b/tests/docker-compose-config.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' @test "Read docker-compose config when none exists" { @@ -52,9 +52,38 @@ load '../lib/shared' assert_output "2.1" } +@test "Read version from docker-compose file with empty version" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.no-version.yml" + run docker_compose_config_version + assert_success + assert_output "" +} + +@test "Read version from first of two docker-compose files configured" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0="tests/composefiles/docker-compose.v2.1.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_1="tests/composefiles/docker-compose.v3.2.yml" + run docker_compose_config_version + assert_success + assert_output "2.1" +} + +@test "Read version given docker-compose file with argument named version" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0="tests/composefiles/docker-compose.v3.2.with-version-arg.yml" + run docker_compose_config_version + assert_success + assert_output "3.2" +} + +@test "Read version given docker-compose file with argument named version and whitespace" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0="tests/composefiles/docker-compose.v2.0.with-version-arg-and-whitespace.yml" + run docker_compose_config_version + assert_success + assert_output "2.0" +} + @test "Whether docker-compose supports cache_from directive" { run docker_compose_supports_cache_from "" - assert_failure + assert_success run docker_compose_supports_cache_from "1.0" assert_failure diff --git a/tests/docker-compose-images.bats b/tests/docker-compose-images.bats index dca55923..a47838c2 100644 --- a/tests/docker-compose-images.bats +++ b/tests/docker-compose-images.bats @@ -2,7 +2,7 @@ # export DOCKER_COMPOSE_STUB_DEBUG=/dev/tty -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/push' diff --git a/tests/docker-compose-verbosity.bats b/tests/docker-compose-verbosity.bats index 2240c474..75cb1769 100644 --- a/tests/docker-compose-verbosity.bats +++ b/tests/docker-compose-verbosity.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/run' diff --git a/tests/image-override-file.bats b/tests/image-override-file.bats index 1386afbb..cd052fac 100644 --- a/tests/image-override-file.bats +++ b/tests/image-override-file.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' myservice_override_file1=$(cat <<-EOF @@ -32,61 +32,80 @@ services: EOF ) -@test "Build an docker-compose override file" { - run build_image_override_file_with_version "2.1" "myservice" "newimage:1.0.0" "" +myservice_override_file4=$(cat <<-EOF +version: '3.2' +services: + myservice: + image: newimage:1.0.0 + build: + cache_from: + - my.repository/myservice:latest + - my.repository/myservice:target +EOF +) + +@test "Build a docker-compose override file" { + run build_image_override_file_with_version "2.1" "myservice" "newimage:1.0.0" 0 assert_success assert_output "$myservice_override_file1" } -@test "Build an docker-compose override file with multiple entries" { +@test "Build a docker-compose override file with multiple entries" { run build_image_override_file_with_version "2.1" \ - "myservice1" "newimage1:1.0.0" "" \ - "myservice2" "newimage2:1.0.0" "" + "myservice1" "newimage1:1.0.0" 0 \ + "myservice2" "newimage2:1.0.0" 0 assert_success assert_output "$myservice_override_file2" } @test "Build a docker-compose file with cache-from" { - run build_image_override_file_with_version "3.2" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "3.2" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_success assert_output "$myservice_override_file3" } +@test "Build a docker-compose file with multiple cache-from entries" { + run build_image_override_file_with_version "3.2" "myservice" "newimage:1.0.0" 2 "my.repository/myservice:latest" "my.repository/myservice:target" + + assert_success + assert_output "$myservice_override_file4" +} + @test "Build a docker-compose file with cache-from and compose-file version 2" { - run build_image_override_file_with_version "2" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "2" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } @test "Build a docker-compose file with cache-from and compose-file version 2.0" { - run build_image_override_file_with_version "2.0" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "2.0" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } @test "Build a docker-compose file with cache-from and compose-file version 2.1" { - run build_image_override_file_with_version "2.1" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "2.1" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } @test "Build a docker-compose file with cache-from and compose-file version 3" { - run build_image_override_file_with_version "3" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "3" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } @test "Build a docker-compose file with cache-from and compose-file version 3.0" { - run build_image_override_file_with_version "3.0" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "3.0" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } @test "Build a docker-compose file with cache-from and compose-file version 3.1" { - run build_image_override_file_with_version "3.1" "myservice" "newimage:1.0.0" "my.repository/myservice:latest" + run build_image_override_file_with_version "3.1" "myservice" "newimage:1.0.0" 1 "my.repository/myservice:latest" assert_failure } diff --git a/tests/logs.bats b/tests/logs.bats index bd11416d..3d05d845 100644 --- a/tests/logs.bats +++ b/tests/logs.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/run' load '../lib/shared' @@ -8,16 +8,20 @@ load '../lib/shared' # export CHECK_LINKED_CONTAINERS_AND_SAVE_LOGS_STUB_DEBUG=/dev/tty # export DOCKER_STUB_DEBUG=/dev/tty -@test "Upload log settings: on-error" { - export LOG_DIR="docker-compose-logs" - +setup () { function docker_ps_by_project() { + # shellcheck disable=2317 # funtion used by loaded scripts cat tests/fixtures/id-service-multiple-services.txt } function plugin_prompt_and_run() { + # shellcheck disable=2317 # funtion used by loaded scripts echo "ran plugin_prompt_and_run" } +} + +@test "Upload log settings: on-error" { + export LOG_DIR="docker-compose-logs" stub docker \ "inspect --format={{.State.ExitCode}} 456456 : echo 1" \ @@ -36,14 +40,6 @@ load '../lib/shared' @test "Upload log settings: always" { export LOG_DIR="docker-compose-logs" - function docker_ps_by_project() { - cat tests/fixtures/id-service-multiple-services.txt - } - - function plugin_prompt_and_run() { - echo "ran plugin_prompt_and_run" - } - stub docker \ "inspect --format={{.State.ExitCode}} 456456 : echo 1" \ "logs -t 456456 : echo got logs for failed" \ @@ -62,14 +58,6 @@ load '../lib/shared' @test "Upload log settings: never" { export LOG_DIR="docker-compose-logs" - function docker_ps_by_project() { - cat tests/fixtures/id-service-multiple-services.txt - } - - function plugin_prompt_and_run() { - echo "ran plugin_prompt_and_run" - } - run check_linked_containers_and_save_logs \ "main" "/tmp/docker-compose-logs" "never" diff --git a/tests/metadata.bats b/tests/metadata.bats index 80c9f1a7..d520c73f 100644 --- a/tests/metadata.bats +++ b/tests/metadata.bats @@ -2,7 +2,7 @@ # export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/metadata' diff --git a/tests/multiple-commands.bats b/tests/multiple-commands.bats new file mode 100644 index 00000000..f4989bb5 --- /dev/null +++ b/tests/multiple-commands.bats @@ -0,0 +1,159 @@ +#!/usr/bin/env bats + +load "${BATS_PLUGIN_PATH}/load.bash" +load '../lib/shared' +load '../lib/metadata' + +# export DOCKER_COMPOSE_STUB_DEBUG=/dev/tty +# export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty +# export BATS_MOCK_TMPDIR=$PWD + +setup_file() { + # General pipeline variables + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="pwd" + export BUILDKITE_JOB_ID=12 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN_LABELS="false" +} + +@test "Build and run" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + + # necessary for build + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + + stub docker-compose \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran dependencies" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml run --name buildkite12_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + # these commands simulate metadata for a specific value by using an intermediate-file + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice \* : echo \$4 > /tmp/build-run-metadata" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : test -f /tmp/build-run-metadata" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : cat /tmp/build-run-metadata" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Building services myservice" + assert_output --partial "Pushing built images to my.repository/llamas" + assert_output --partial "Found a pre-built image for myservice" + assert_output --partial "Starting dependencies" + assert_output --partial "ran myservice" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Build and push" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice + + # necessary for build + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + + stub docker-compose \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml push myservice : echo build-pushed myservice" \ + "-f docker-compose.yml -p buildkite12 config : echo ''" \ + "-f docker-compose.yml -p buildkite12 push myservice : echo push-pushed myservice" + + # these commands simulate metadata for a specific value by using an intermediate-file + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice \* : echo \$4 > /tmp/build-push-metadata" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : test -f /tmp/build-push-metadata" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : cat /tmp/build-push-metadata" + + stub docker \ + "pull my.repository/llamas:test-myservice-build-1 : echo pulled pre-built image" \ + "tag my.repository/llamas:test-myservice-build-1 buildkite12_myservice : echo re-tagged pre-built image" + + run "$PWD"/hooks/command + + assert_success + + assert_output --partial "Building services myservice" + assert_output --partial "Pushing built images to my.repository/llamas" + assert_output --partial "Pulling pre-built service myservice" + assert_output --partial "Tagging pre-built service myservice" + assert_output --partial "Pushing images for myservice" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run and push without pre-built image" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice + + stub docker-compose \ + "-f docker-compose.yml -p buildkite12 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite12 up -d --scale myservice=0 myservice : echo ran dependencies" \ + "-f docker-compose.yml -p buildkite12 run --name buildkite12_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" \ + "-f docker-compose.yml -p buildkite12 config : echo ''" \ + "-f docker-compose.yml -p buildkite12 build myservice : echo built-2 myservice" \ + "-f docker-compose.yml -p buildkite12 push myservice : echo pushed myservice" + + # these make sure that the image is not pre-built + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + + assert_output --partial "Building Docker Compose Service: myservice" + assert_output --partial "No pre-built image found from a previous " + assert_output --partial "Starting dependencies" + assert_output --partial "ran myservice" + assert_output --partial "Building myservice" + assert_output --partial "Pushing images for myservice" + + unstub docker-compose + unstub buildkite-agent +} + + +@test "Run and push with pre-built image" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice + + stub docker-compose \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran dependencies" \ + "-f docker-compose.yml -p buildkite12 -f docker-compose.buildkite-1-override.yml run --name buildkite12_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" \ + "-f docker-compose.yml -p buildkite12 config : echo ''" \ + "-f docker-compose.yml -p buildkite12 push myservice : echo pushed myservice" + + # these make sure that the image is not pre-built + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myservice-tag" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myservice-tag" + + stub docker \ + "pull myservice-tag : echo pulled pre-built image" \ + "tag myservice-tag buildkite12_myservice : echo re-tagged pre-built image" + + run "$PWD"/hooks/command + + assert_success + + refute_output --partial "Building services myservice" + assert_output --partial "Found a pre-built image for myservice" + assert_output --partial "Pulling services myservice" + assert_output --partial "Starting dependencies" + assert_output --partial "Pulling pre-built service myservice" + assert_output --partial "Pushing images for myservice" + + unstub docker-compose + unstub buildkite-agent +} + diff --git a/tests/output.bats b/tests/output.bats index 5cd617e3..f5318b6f 100644 --- a/tests/output.bats +++ b/tests/output.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/run' @@ -9,6 +9,9 @@ load '../lib/run' # export DOCKER_STUB_DEBUG=/dev/tty # export BATS_MOCK_TMPDIR=$PWD +setup_file() { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN_LABELS="false" +} @test "Logs: Detect some containers KO" { export BUILDKITE_AGENT_ACCESS_TOKEN="123123" @@ -25,28 +28,72 @@ load '../lib/run' stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" \ - "artifact upload : exit 0" + "artifact upload docker-compose-logs/\*.log : exit 0" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice command" stub docker \ "ps -a --filter label=com.docker.compose.project=buildkite1111 -q : cat tests/fixtures/id-multiple-services.txt" \ - "inspect -f {{if\ ne\ 0\ .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{\ end\ }} 456456 : echo 456456.1" \ - "ps -a --filter label=com.docker.compose.project=buildkite1111 --format : cat tests/fixtures/service-id-exit-multiple-services-failed.txt" \ - "ps -a --filter label=com.docker.compose.project=buildkite1111 --format : cat tests/fixtures/id-service-multiple-services.txt" \ + "inspect -f {{if\ ne\ 0\ .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{\ end\ }} 456456 789789 : echo 456456.1" \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format \* : cat tests/fixtures/service-id-exit-multiple-services-failed.txt" \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format \* : cat tests/fixtures/id-service-multiple-services.txt" \ "inspect --format={{.State.ExitCode}} 456456 : echo 1" \ "logs --timestamps --tail 5 456456 : exit 0" \ "logs -t 456456 : exit 0" \ "inspect --format={{.State.ExitCode}} 789789 : echo 0" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" - assert_output --partial "ran myservice" + assert_output --partial "ran myservice dependencies" + assert_output --partial "ran myservice command" + assert_output --partial "Some containers had non-zero exit codes" + unstub buildkite-agent + unstub docker-compose + unstub docker +} + +@test "Logs: Detect dependent services KO" { + # Test for Issue #327, Container logs are not uploaded when services fail to start. + export BUILDKITE_AGENT_ACCESS_TOKEN="123123" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMMAND_0=echo + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMMAND_1="hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" \ + "artifact upload docker-compose-logs/\*.log : exit 0" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : exit 1" \ + + stub docker \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 -q : cat tests/fixtures/id-multiple-services.txt" \ + "inspect -f {{if\ ne\ 0\ .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{\ end\ }} 456456 789789 : echo 456456.1" \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format \* : cat tests/fixtures/service-id-exit-multiple-services-failed.txt" \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format \* : cat tests/fixtures/id-service-multiple-services.txt" \ + "inspect --format={{.State.ExitCode}} 456456 : echo 1" \ + "logs --timestamps --tail 5 456456 : exit 0" \ + "logs -t 456456 : exit 0" \ + "inspect --format={{.State.ExitCode}} 789789 : echo 0" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "built myservice" + assert_output --partial "Failed to start dependencies" assert_output --partial "Some containers had non-zero exit codes" unstub buildkite-agent unstub docker-compose @@ -71,21 +118,22 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice command" stub docker \ "ps -a --filter label=com.docker.compose.project=buildkite1111 -q : cat tests/fixtures/id-multiple-services.txt" \ "inspect -f {{if\ ne\ 0\ .State.ExitCode}}{{.Name}}.{{.State.ExitCode}}{{\ end\ }} 456456 789789 : echo" \ - "ps -a --filter : cat tests/fixtures/id-service-multiple-services.txt" \ + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format \* : cat tests/fixtures/id-service-multiple-services.txt" \ "inspect --format={{.State.ExitCode}} 456456 : echo 0" \ "inspect --format={{.State.ExitCode}} 789789 : echo 0" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" - assert_output --partial "ran myservice" + assert_output --partial "ran myservice dependencies" + assert_output --partial "ran myservice command" refute_output --partial "Some containers had non-zero exit codes" unstub docker unstub docker-compose @@ -113,18 +161,19 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice command" stub docker \ "ps -a --filter label=com.docker.compose.project=buildkite1111 -q : echo" \ - "ps -a --filter : cat tests/fixtures/id-service-no-services.txt" + "ps -a --filter label=com.docker.compose.project=buildkite1111 --format '{{.ID}}\\t{{.Label \"com.docker.compose.service\"}}' : cat tests/fixtures/id-service-no-services.txt" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" - assert_output --partial "ran myservice" + assert_output --partial "ran myservice dependencies" + assert_output --partial "ran myservice command" refute_output --partial "Uploading linked container logs" unstub docker unstub docker-compose diff --git a/tests/plugin-config.bats b/tests/plugin-config.bats index a9b3c046..cf590b71 100644 --- a/tests/plugin-config.bats +++ b/tests/plugin-config.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' @test "Read existing config without default" { diff --git a/tests/project-name.bats b/tests/project-name.bats index 4678b4e8..18f00e7c 100644 --- a/tests/project-name.bats +++ b/tests/project-name.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' @test "Project name comes from BUILDKITE_JOB_ID" { diff --git a/tests/push.bats b/tests/push.bats index 49003647..459aa6b6 100644 --- a/tests/push.bats +++ b/tests/push.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' # export DOCKER_COMPOSE_STUB_DEBUG=/dev/tty @@ -24,7 +24,7 @@ load '../lib/shared' stub docker \ "image inspect somewhere.dkr.ecr.some-region.amazonaws.com/blah : exit 0" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial ":warning: Skipping build" @@ -59,7 +59,7 @@ load '../lib/shared' "meta-data exists docker-compose-plugin-built-image-tag-myservice1 : exit 1" \ "meta-data exists docker-compose-plugin-built-image-tag-myservice2 : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "tagging image1" @@ -90,7 +90,7 @@ load '../lib/shared' "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled prebuilt image" @@ -101,6 +101,24 @@ load '../lib/shared' unstub buildkite-agent } +@test "Push a prebuilt image with an invalid tag" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice:my.repository/myservice:-llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 config : echo blah" + + run "$PWD"/hooks/command + + assert_success + refute_output --partial "pulled prebuilt image" + refute_output --partial "tagged image" + assert_output --partial "invalid tag" + unstub docker-compose +} + @test "Push a prebuilt image to multiple tags" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_0=myservice:my.repository/myservice:llamas @@ -134,7 +152,7 @@ load '../lib/shared' "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo prebuilt" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled prebuilt image" @@ -167,7 +185,7 @@ load '../lib/shared' "tag buildkite1111_helper my.repository/helper:llamas : echo tagged helper" \ "push my.repository/helper:llamas : echo pushed helper" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built helper" diff --git a/tests/run.bats b/tests/run.bats index 237e1e50..3569addd 100644 --- a/tests/run.bats +++ b/tests/run.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load '/usr/local/lib/bats/load.bash' +load "${BATS_PLUGIN_PATH}/load.bash" load '../lib/shared' load '../lib/run' @@ -8,6 +8,10 @@ load '../lib/run' # export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty # export BATS_MOCK_TMPDIR=$PWD +setup_file() { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN_LABELS="false" +} + @test "Run without a prebuilt image" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice @@ -19,13 +23,13 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -45,15 +49,16 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success + refute_output --partial "The Docker Compose Plugin does not correctly support step-level array commands" assert_output --partial "built myservice" assert_output --partial "ran myservice" unstub docker-compose @@ -72,13 +77,13 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --workdir=/test_workdir --rm myservice : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -98,13 +103,13 @@ load '../lib/run' stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'sh -c \'echo hello world\'' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c $'sh -c \'echo hello world\'' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -126,15 +131,16 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'cmd1\ncmd2\ncmd3' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c $'cmd1\ncmd2\ncmd3' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success + assert_output --partial "The Docker Compose Plugin does not correctly support step-level array commands" assert_output --partial "built myservice" assert_output --partial "ran myservice" unstub docker-compose @@ -154,15 +160,16 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo hello world' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success + refute_output --partial "The Docker Compose Plugin does not correctly support step-level array commands" assert_output --partial "built myservice" assert_output --partial "ran myservice" unstub docker-compose @@ -185,13 +192,13 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -e MYENV=0 -e MYENV -e MYENV=2 -e MYENV -e ANOTHER=this\ is\ a\ long\ string\ with\ spaces\;\ and\ semi-colons --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice" @@ -211,13 +218,13 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull --no-cache myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -239,13 +246,13 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull --build-arg MYARG=0 --build-arg MYARG=1 myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -254,6 +261,66 @@ cmd3" unstub buildkite-agent } +@test "Run with a prebuilt image and propagate environment but no BUILDKITE_ENV_FILE" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_ENVIRONMENT=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Running /bin/sh -e -c 'pwd' in service myservice" + assert_output --partial "Not propagating environment variables to container" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with a prebuilt image and propagate environment" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_ENV_FILE=/tmp/test_env + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_ENVIRONMENT=true + + echo "VAR0=1" > "${BUILDKITE_ENV_FILE}" + echo "VAR2=lalala" >> "${BUILDKITE_ENV_FILE}" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -e \* -e \* --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with vars \${11} and \${13}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Running /bin/sh -e -c 'pwd' in service myservice" + assert_output --partial "ran myservice with vars VAR0 and VAR2" + + unstub docker-compose + unstub buildkite-agent + + rm "${BUILDKITE_ENV_FILE}" +} + @test "Run with a prebuilt image" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice @@ -265,14 +332,14 @@ cmd3" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ - "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice" @@ -292,14 +359,14 @@ cmd3" stub docker-compose \ "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ - "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice" @@ -320,14 +387,41 @@ export BUILDKITE_JOB_ID=1111 stub docker-compose \ "-f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ - "-f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 : echo pulled myservice" \ + "-f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo pulled myservice" \ "-f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with a prebuilt image and custom config file set from COMPOSE_FILE" { + export COMPOSE_FILE=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker-compose \ + "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : echo myimage" + + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice" @@ -351,7 +445,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_failure assert_output --partial "Exited with 2" @@ -379,7 +473,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled myservice" @@ -407,7 +501,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice without tty" @@ -433,7 +527,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice without dependencies" @@ -441,6 +535,32 @@ export BUILDKITE_JOB_ID=1111 unstub buildkite-agent } +@test "Run with dependencies but in a single step" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PRE_RUN_DEPENDENCIES=false + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with dependencies" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with dependencies" + unstub docker-compose + unstub buildkite-agent +} + @test "Run without ansi output" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice @@ -452,15 +572,15 @@ export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ANSI=false stub docker-compose \ - "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ - "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ - "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml --no-ansi run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without ansi output" + "--no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "--no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "--no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without ansi output" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice without ansi output" @@ -468,6 +588,33 @@ export BUILDKITE_JOB_ID=1111 unstub buildkite-agent } + +@test "Run without pull" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SKIP_PULL=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without pull" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Running /bin/sh -e -c 'pwd' in service myservice" + assert_output --partial "ran myservice without pull" + + unstub docker-compose + unstub buildkite-agent +} + @test "Run with use aliases" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice @@ -487,7 +634,34 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with use aliases output" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with compatibility mode" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMPATIBILITY=true + + stub docker-compose \ + "--compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "--compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "--compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with use aliases output" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice with use aliases output" @@ -515,7 +689,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice with volumes" @@ -542,7 +716,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice with volumes" @@ -572,7 +746,111 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with volumes with variables" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_0="\$SUPER_VARIABLE:/mnt" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_1="/:\$OTHER_VARIABLE" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_2="\$RELATIVE_VARIABLE:/srv" + + export SUPER_VARIABLE='/test/path' + export OTHER_VARIABLE='/path/tested' + export RELATIVE_VARIABLE='./path' + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v \\\$SUPER_VARIABLE:/mnt -v /:\\\$OTHER_VARIABLE -v \\\$RELATIVE_VARIABLE:/srv --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker-compose + unstub buildkite-agent +} + + +@test "Run with volumes with variables but option turned off" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_EXPAND_VOLUME_VARS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_0="\$SUPER_VARIABLE:/mnt" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_1="/:\$OTHER_VARIABLE" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_2="\$RELATIVE_VARIABLE:/srv" + + + export SUPER_VARIABLE='/test/path' + export OTHER_VARIABLE='/path/tested' + export RELATIVE_VARIABLE='./path' + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v \\\$SUPER_VARIABLE:/mnt -v /:\\\$OTHER_VARIABLE -v \\\$RELATIVE_VARIABLE:/srv --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with volumes with variables and option turned on" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_EXPAND_VOLUME_VARS=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_0="\$SUPER_VARIABLE:/mnt" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_1="/:\$OTHER_VARIABLE" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_2="\$RELATIVE_VARIABLE:/srv" + + + export SUPER_VARIABLE='/test/path' + export OTHER_VARIABLE='/path/tested' + export RELATIVE_VARIABLE='./path' + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v /test/path:/mnt -v /:/path/tested -v $PWD/path:/srv --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice with volumes" @@ -599,7 +877,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice with volumes" @@ -627,7 +905,7 @@ export BUILDKITE_JOB_ID=1111 stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice-llamas1.yml-llamas2.yml-llamas3.yml : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -653,7 +931,7 @@ export BUILDKITE_JOB_ID=1111 stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_failure assert_output --partial "^^^ +++" @@ -684,7 +962,7 @@ export BUILDKITE_JOB_ID=1111 "meta-data exists docker-compose-plugin-built-image-tag-myservice2 : exit 0" \ "meta-data get docker-compose-plugin-built-image-tag-myservice2 : echo myimage2" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "pulled myservice1 and myservice2" @@ -706,12 +984,12 @@ export BUILDKITE_JOB_ID=1111 stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000 myservice /bin/sh -e -c 'sh -c \'whoami\'' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000 --rm myservice /bin/sh -e -c $'sh -c \'whoami\'' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -728,17 +1006,17 @@ export BUILDKITE_JOB_ID=1111 export BUILDKITE_COMMAND="sh -c 'whoami'" export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false - export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000:1001" stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ - "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000:1000 myservice /bin/sh -e -c 'sh -c \'whoami\'' : echo ran myservice" + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000:1001 --rm myservice /bin/sh -e -c $'sh -c \'whoami\'' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -747,6 +1025,29 @@ export BUILDKITE_JOB_ID=1111 unstub buildkite-agent } +@test "Fail with custom user and propagate UIDs" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="sh -c 'whoami'" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000:1001" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_UID_GID="true" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "Error" + assert_output --partial "Can't set both user and propagate-uid-gid" + unstub buildkite-agent +} + + @test "Run without --rm" { export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice @@ -760,13 +1061,13 @@ export BUILDKITE_JOB_ID=1111 stub docker-compose \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ - "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 myservice /bin/sh -e -c 'pwd' : echo ran myservice without tty" + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 myservice /bin/sh -e -c $'pwd' : echo ran myservice without tty" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : echo myimage" \ "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "ran myservice without tty" @@ -784,17 +1085,42 @@ export BUILDKITE_JOB_ID=1111 export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENTRYPOINT="my custom entrypoint" - ENTRYPOINT='--entrypoint\ \"my\ custom\ entrypoint\"' - stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm --entrypoint 'my custom entrypoint' myservice : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-buildkite-agent enabled" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_BUILDKITE_AGENT=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e BUILDKITE_JOB_ID -e BUILDKITE_BUILD_ID -e BUILDKITE_AGENT_ACCESS_TOKEN -v $BATS_MOCK_TMPDIR/bin/buildkite-agent:/usr/bin/buildkite-agent myservice : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" @@ -816,17 +1142,536 @@ export BUILDKITE_JOB_ID=1111 stub docker-compose \ "-f docker-compose.yml -p buildkite1111 build --pull --no-cache --parallel myservice : echo built myservice" \ - "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" stub buildkite-agent \ "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" - run $PWD/hooks/command + run "$PWD"/hooks/command assert_success assert_output --partial "built myservice" assert_output --partial "ran myservice" unstub docker-compose unstub buildkite-agent -} \ No newline at end of file +} + +@test "Run with git-mirrors" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_REPO_MIRROR=/tmp/sample-mirror + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -v /tmp/sample-mirror:/tmp/sample-mirror:ro --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-ssh-agent" { + export SSH_AUTH_SOCK=/tmp/ssh_auth_sock + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e SSH_AUTH_SOCK=/ssh-agent -v /tmp/ssh_auth_sock:/ssh-agent -v /root/.ssh/known_hosts:/root/.ssh/known_hosts myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + apk add netcat-openbsd + nc -lkvU $SSH_AUTH_SOCK & + + run "$PWD"/hooks/command + + assert_success + + kill %1 + + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-ssh-agent on particular folder" { + export SSH_AUTH_SOCK=/tmp/ssh_auth_sock + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT=/tmp/test + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e SSH_AUTH_SOCK=/ssh-agent -v /tmp/ssh_auth_sock:/ssh-agent -v /root/.ssh/known_hosts:/tmp/test/.ssh/known_hosts myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + apk add netcat-openbsd + nc -lkvU $SSH_AUTH_SOCK & + + run "$PWD"/hooks/command + + assert_success + + kill %1 + + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run without mount-checkout doesn't set volume" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=false + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without mount-checkout" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without mount-checkout" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set to true" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --workdir=/workdir -v $PWD:/workdir --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set to true with custom workdir" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --workdir=$BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR -v $PWD:$BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set to specific path" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="/special" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 -v \* --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout on \${11}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout on /plugin:/special" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set to specific path and workdir set" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="/special" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 \* -v \* --rm myservice /bin/sh -e -c 'pwd' : echo echo ran myservice with mount-checkout on \${12}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout on /plugin:/special" + assert_output --partial "--workdir=/custom_workdir" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set something else" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="not_absolute" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "mount-checkout should be either true or an absolute path to use as a mountpoint" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with mount-checkout set something else and workdir set" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="not-absolute" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "mount-checkout should be either true or an absolute path to use as a mountpoint" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run waiting for dependencies" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WAIT=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up --wait -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Starting dependencies" + assert_output --partial "ran myservice" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with --service-ports" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SERVICE_PORTS=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm --service-ports myservice /bin/sh -e -c $'pwd' : echo ran myservice without tty" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : echo myimage" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without tty" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with Docker labels" { + # Pipeline vars + export BUILDKITE_AGENT_ID="1234" + export BUILDKITE_AGENT_NAME="agent" + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_LABEL="Testjob" + export BUILDKITE_PIPELINE_NAME="label-test" + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_STEP_KEY="test-job" + + # Plugin config + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN_LABELS="true" + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "-f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 \ + --label com.buildkite.pipeline_name=${BUILDKITE_PIPELINE_NAME} \ + --label com.buildkite.pipeline_slug=${BUILDKITE_PIPELINE_SLUG} \ + --label com.buildkite.build_number=${BUILDKITE_BUILD_NUMBER} \ + --label com.buildkite.job_id=${BUILDKITE_JOB_ID} \ + --label com.buildkite.job_label=${BUILDKITE_LABEL} \ + --label com.buildkite.step_key=${BUILDKITE_STEP_KEY} \ + --label com.buildkite.agent_name=${BUILDKITE_AGENT_NAME} \ + --label com.buildkite.agent_id=${BUILDKITE_AGENT_ID} \ + --rm myservice /bin/sh -e -c $'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : echo myimage" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with --quiet-pull" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_QUIET_PULL=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up --quiet-pull -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + refute_output --partial "Pulling" + assert_output --partial "ran myservice" + + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with a list of propagated env vars" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_PROPAGATION_LIST="LIST_OF_VARS" + export LIST_OF_VARS="VAR_A VAR_B VAR_C" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -e VAR_A -e VAR_B -e VAR_C --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with a list of propagated env vars - unless you forgot to define the variable" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_PROPAGATION_LIST="LIST_OF_VARS" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "env-propagation-list desired, but LIST_OF_VARS is not defined!" + unstub buildkite-agent +} + +@test "Run with expanded run log group by default" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "+++ :docker: Running /bin/sh -e -c 'echo hello world' in service myservice" + unstub docker-compose + unstub buildkite-agent +} + +@test "Run with collapsed run log group" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COLLAPSE_RUN_LOG_GROUP=true + + stub docker-compose \ + "-f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "-f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "-f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "--- :docker: Running /bin/sh -e -c 'echo hello world' in service myservice" + unstub docker-compose + unstub buildkite-agent + + +} diff --git a/tests/v2/build.bats b/tests/v2/build.bats new file mode 100644 index 00000000..535679ec --- /dev/null +++ b/tests/v2/build.bats @@ -0,0 +1,712 @@ +#!/usr/bin/env bats + +load "${BATS_PLUGIN_PATH}/load.bash" +load '../../lib/shared' + +# export DOCKER_COMPOSE_STUB_DEBUG=/dev/stdout +# export BUILDKITE_AGENT_STUB_DEBUG=/dev/stdout +# export BATS_MOCK_TMPDIR=$PWD + +setup_file() { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLI_VERSION=2 +} + +@test "Build without a repository" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" + + run "$PWD"/hooks/command + + unstub docker + assert_success + assert_output --partial "built myservice" +} + +@test "Build with no-cache" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_NO_CACHE=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --no-cache myservice : echo built myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker +} + +@test "Build with parallel" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_PARALLEL=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --parallel myservice : echo built myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker +} + +@test "Build with build args" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ARGS_0=MYARG=0 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ARGS_1=MYARG=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --build-arg MYARG=0 --build-arg MYARG=1 myservice : echo built myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker +} + +@test "Build with a repository" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build with a repository and multiple build aliases" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_ALIAS_0=myservice-1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_ALIAS_1=myservice-2 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" \ + "meta-data set docker-compose-plugin-built-image-tag-myservice-1 my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice-1" \ + "meta-data set docker-compose-plugin-built-image-tag-myservice-2 my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice-2" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + assert_output --partial "set image metadata for myservice-1" + assert_output --partial "set image metadata for myservice-2" + unstub docker + unstub buildkite-agent +} + +@test "Build with a repository and push retries" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_RETRIES=3 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : exit 1" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : exit 1" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build with a repository and custom config file" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build with a repository and multiple custom config files" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_1=tests/composefiles/docker-compose.v2.1.yml + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml my.repository/llamas:test-myservice-build-1 : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build with a repository and multiple services" { + export BUILDKITE_JOB_ID=1112 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=myservice1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_1=myservice2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1112 -f docker-compose.buildkite-1-override.yml build --pull myservice1 myservice2 : echo built all services" \ + "compose -f docker-compose.yml -p buildkite1112 -f docker-compose.buildkite-1-override.yml push myservice1 myservice2 : echo pushed all services" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice1 my.repository/llamas:test-myservice1-build-1 : echo set image metadata for myservice1" \ + "meta-data set docker-compose-plugin-built-image-tag-myservice2 my.repository/llamas:test-myservice2-build-1 : echo set image metadata for myservice2" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built all services" + assert_output --partial "pushed all services" + assert_output --partial "set image metadata for myservice1" + assert_output --partial "set image metadata for myservice2" + unstub docker + unstub buildkite-agent +} + +@test "Build with a docker-compose v1.0 configuration file" { + export BUILDKITE_JOB_ID=1112 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v1.0.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "Compose file versions 2.0 and above" +} + +@test "Build with a cache-from image" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a cache-from image and custom separator" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0='helloworld#my.repository:port/myservice_cache#latest' + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SEPARATOR_CACHE_FROM='#' + + stub docker \ + "pull my.repository:port/myservice_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository:port/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with an invalid cache-from tag" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:-latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + refute_output --partial "pulled cache image" + refute_output --partial "- my.repository/myservice_cache:-latest" + assert_output --partial "invalid tag so it will be ignored" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a cache-from image with no tag" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + + stub docker \ + "pull my.repository/myservice_cache : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/myservice_cache" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a cache-from image with no-cache also set" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_NO_CACHE=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --no-cache helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + refute_output --partial "pulled cache image" + refute_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with several cache-from images for one service" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:branch-name : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/myservice_cache:branch-name" + refute_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with several cache-from images for one service with first image being not available" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:branch-name + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_1=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:branch-name : exit 1" \ + "pull my.repository/myservice_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + refute_output --partial "- my.repository/myservice_cache:branch-name" + assert_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a cache-from image when pulling of the cache-from image failed" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/myservice_cache:latest : exit 1" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "my.repository/myservice_cache:latest will not be used as a cache for helloworld" + refute_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a cache-from image with hyphen" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=hello-world + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=hello-world:my.repository/my-service_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/my-service_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull hello-world : echo built hello-world" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/my-service_cache:latest" + assert_output --partial "built hello-world" + unstub docker +} + +@test "Build with a service name and cache-from with period" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=hello.world + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=hello.world:my.repository/my-service_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "pull my.repository/my-service_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull \* : echo built \${10}" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/my-service_cache:latest" + assert_output --partial "built hello.world" + unstub docker +} + +@test "Build with a cache-from image retry on failing pull" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=helloworld + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CACHE_FROM_0=helloworld:my.repository/myservice_cache:latest + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PULL_RETRIES=3 + + stub docker \ + "pull my.repository/myservice_cache:latest : exit 1" \ + "pull my.repository/myservice_cache:latest : exit 1" \ + "pull my.repository/myservice_cache:latest : echo pulled cache image" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull helloworld : echo built helloworld" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled cache image" + assert_output --partial "- my.repository/myservice_cache:latest" + assert_output --partial "built helloworld" + unstub docker +} + +@test "Build with a custom image-name" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=my-llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice my.repository/llamas:my-llamas-image : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build with an invalid image-name (start with hyphen) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=-llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "-llamas-image is not a valid tag name" +} + +@test "Build with an invalid image-name (start with period) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=.llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial ".llamas-image is not a valid tag name" +} + +@test "Build with an invalid image-name (too long) " { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + # shellcheck disable=SC2155 # numbers from 1 to 69 result in 129 characters + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME="$(seq 69 | tr -d "\n")" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_BUILD_NUMBER=1 + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "is not a valid tag name" +} + +@test "Build with a custom image-name and a config" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG="tests/composefiles/docker-compose.v3.2.yml" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME=my-llamas-image + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull myservice : echo built myservice" \ + "compose -f tests/composefiles/docker-compose.v3.2.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml push myservice : echo pushed myservice" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v3.2.yml my.repository/llamas:my-llamas-image : echo set image metadata for myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "pushed myservice" + assert_output --partial "set image metadata for myservice" + unstub docker + unstub buildkite-agent +} + +@test "Build multiple images with custom image-names" { + export BUILDKITE_JOB_ID=1112 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_0=myservice1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_1=myservice2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME_0=my-llamas-image-1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_NAME_1=my-llamas-image-2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_IMAGE_REPOSITORY=my.repository/llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1112 -f docker-compose.buildkite-1-override.yml build --pull myservice1 myservice2 : echo built all services" \ + "compose -f docker-compose.yml -p buildkite1112 -f docker-compose.buildkite-1-override.yml push myservice1 myservice2 : echo pushed all services" \ + + stub buildkite-agent \ + "meta-data set docker-compose-plugin-built-image-tag-myservice1 my.repository/llamas:my-llamas-image-1 : echo set image metadata for myservice1" \ + "meta-data set docker-compose-plugin-built-image-tag-myservice2 my.repository/llamas:my-llamas-image-2 : echo set image metadata for myservice2" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built all services" + assert_output --partial "pushed all services" + assert_output --partial "set image metadata for myservice1" + assert_output --partial "set image metadata for myservice2" + unstub docker + unstub buildkite-agent +} + +@test "Build with target" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_TARGET=intermediate + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --target \* \* : echo built \${12} with target \${11}" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with target intermediate" + + unstub docker +} + +@test "Build with ssh option as boolean" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SSH=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --ssh default \* : echo built \${12} with ssh" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with ssh" + + unstub docker +} + +@test "Build with ssh option as string" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SSH=context + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --ssh context \* : echo built \${12} with ssh" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with ssh" + + unstub docker +} + +@test "Build with secrets" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SECRETS_0='id=test,file=~/.test' + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SECRETS_1='id=SECRET_VAR' + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build --pull --secret \* --secret \* \* : echo built \${14} with secrets \${11} and \${13}" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "with secrets id=test,file=~/.test and id=SECRET_VAR" + + unstub docker +} + +@test "Build without pull" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SKIP_PULL=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml build myservice : echo built myservice" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + unstub docker +} diff --git a/tests/v2/push.bats b/tests/v2/push.bats new file mode 100644 index 00000000..4bef2dbc --- /dev/null +++ b/tests/v2/push.bats @@ -0,0 +1,216 @@ +#!/usr/bin/env bats + +load "${BATS_PLUGIN_PATH}/load.bash" +load '../../lib/shared' + +# export DOCKER_COMPOSE_STUB_DEBUG=/dev/tty +# export DOCKER_STUB_DEBUG=/dev/tty +# export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty +# export BATS_MOCK_TMPDIR=$PWD + +setup_file() { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLI_VERSION=2 +} + +@test "Push a single service with an image in it's config" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=app + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-app : exit 1" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : cat $PWD/tests/composefiles/docker-compose.config.v3.2.yml" \ + "image inspect somewhere.dkr.ecr.some-region.amazonaws.com/blah : exit 0" \ + "compose -f docker-compose.yml -p buildkite1111 push app : echo pushed app" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial ":warning: Skipping build" + assert_output --partial "pushed app" + unstub docker + unstub buildkite-agent +} + +@test "Push two services with target repositories and tags" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_0=myservice1:my.repository/myservice1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_1=myservice2:my.repository/myservice2:llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah " \ + "image inspect buildkite1111-myservice1 : exit 1" \ + "compose -f docker-compose.yml -p buildkite1111 build myservice1 : echo blah " \ + "tag buildkite1111-myservice1 my.repository/myservice1 : echo tagging image1" \ + "push my.repository/myservice1 : echo pushing myservice1 image" \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah " \ + "image inspect buildkite1111-myservice2 : exit 1" \ + "compose -f docker-compose.yml -p buildkite1111 build myservice2 : echo blah " \ + "tag buildkite1111-myservice2 my.repository/myservice2:llamas : echo tagging image2" \ + "push my.repository/myservice2:llamas : echo pushing myservice2 image" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice1 : exit 1" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice2 : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "tagging image1" + assert_output --partial "pushing myservice1 image" + assert_output --partial "tagging image2" + assert_output --partial "pushing myservice2 image" + unstub docker + unstub buildkite-agent +} + +@test "Push a prebuilt image with a repository and a tag" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice:my.repository/myservice:llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah" \ + "pull myimage : echo pulled prebuilt image" \ + "tag myimage buildkite1111-myservice : echo " \ + "tag buildkite1111-myservice my.repository/myservice:llamas : echo tagged image" \ + "push my.repository/myservice:llamas : echo pushed myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled prebuilt image" + assert_output --partial "tagged image" + assert_output --partial "pushed myservice" + unstub docker + unstub buildkite-agent +} + +@test "Push a prebuilt image with a repository and a tag in compatibility mode" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice:my.repository/myservice:llamas + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMPATIBILITY=true + + stub docker \ + "compose --compatibility -f docker-compose.yml -p buildkite1111 config : echo blah" \ + "pull myimage : echo pulled prebuilt image" \ + "tag myimage buildkite1111_myservice : echo " \ + "tag buildkite1111_myservice my.repository/myservice:llamas : echo tagged image" \ + "push my.repository/myservice:llamas : echo pushed myservice" + + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled prebuilt image" + assert_output --partial "tagged image" + assert_output --partial "pushed myservice" + unstub docker + unstub buildkite-agent +} + +@test "Push a prebuilt image with an invalid tag" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=myservice:my.repository/myservice:-llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah" + + run "$PWD"/hooks/command + + assert_success + refute_output --partial "pulled prebuilt image" + refute_output --partial "tagged image" + assert_output --partial "invalid tag" + unstub docker +} + +@test "Push a prebuilt image to multiple tags" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_0=myservice:my.repository/myservice:llamas + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_1=myservice:my.repository/myservice:latest + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_2=myservice:my.repository/myservice:alpacas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah" \ + "pull prebuilt : echo pulled prebuilt image" \ + "tag prebuilt buildkite1111-myservice : echo " \ + "tag buildkite1111-myservice my.repository/myservice:llamas : echo tagged image1" \ + "push my.repository/myservice:llamas : echo pushed myservice1" \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah" \ + "tag prebuilt buildkite1111-myservice : echo " \ + "tag buildkite1111-myservice my.repository/myservice:latest : echo tagged image2" \ + "push my.repository/myservice:latest : echo pushed myservice2" \ + "compose -f docker-compose.yml -p buildkite1111 config : echo blah" \ + "tag prebuilt buildkite1111-myservice : echo " \ + "tag buildkite1111-myservice my.repository/myservice:alpacas : echo tagged image3" \ + "push my.repository/myservice:alpacas : echo pushed myservice3" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo prebuilt" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo prebuilt" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo prebuilt" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled prebuilt image" + assert_output --partial "tagged image1" + assert_output --partial "pushed myservice1" + assert_output --partial "tagged image2" + assert_output --partial "pushed myservice2" + assert_output --partial "tagged image3" + assert_output --partial "pushed myservice3" + unstub docker + unstub buildkite-agent +} + +@test "Push a single service that needs to be built" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH=helper:my.repository/helper:llamas + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-helper : exit 1" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 config : cat $PWD/tests/composefiles/docker-compose.config.v3.2.yml" \ + "image inspect buildkite1111-helper : exit 1" \ + "compose -f docker-compose.yml -p buildkite1111 build helper : echo built helper" \ + "tag buildkite1111-helper my.repository/helper:llamas : echo tagged helper" \ + "push my.repository/helper:llamas : echo pushed helper" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built helper" + assert_output --partial "tagged helper" + assert_output --partial "pushed helper" + unstub docker + unstub buildkite-agent +} diff --git a/tests/v2/run.bats b/tests/v2/run.bats new file mode 100644 index 00000000..d8dcf64e --- /dev/null +++ b/tests/v2/run.bats @@ -0,0 +1,1482 @@ +#!/usr/bin/env bats + +load "${BATS_PLUGIN_PATH}/load.bash" +load '../../lib/shared' +load '../../lib/run' + +# export DOCKER_COMPOSE_STUB_DEBUG=/dev/tty +# export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty +# export BATS_MOCK_TMPDIR=$PWD + +setup_file() { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLI_VERSION=2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN_LABELS="false" +} + +@test "Run without a prebuilt image" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image and an empty command" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image and a custom workdir" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR=/test_workdir + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --workdir=/test_workdir --rm myservice : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with a quoted command" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="sh -c 'echo hello world'" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c $'sh -c \'echo hello world\'' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with a multi-line command" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="cmd1 +cmd2 +cmd3" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c $'cmd1\ncmd2\ncmd3' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with a command config" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMMAND_0=echo + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMMAND_1="hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice echo 'hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with custom env" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_0=MYENV=0 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_1=MYENV + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENVIRONMENT_0=MYENV=2 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENVIRONMENT_1=MYENV + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENVIRONMENT_2=ANOTHER="this is a long string with spaces; and semi-colons" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -e MYENV=0 -e MYENV -e MYENV=2 -e MYENV -e ANOTHER=this\ is\ a\ long\ string\ with\ spaces\;\ and\ semi-colons --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with no-cache" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_NO_CACHE=true + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull --no-cache myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image with build args" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ARGS_0=MYARG=0 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ARGS_1=MYARG=1 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull --build-arg MYARG=0 --build-arg MYARG=1 myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image without pulling" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SKIP_PULL=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a prebuilt image and propagate environment but no BUILDKITE_ENV_FILE" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_ENVIRONMENT=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Running /bin/sh -e -c 'pwd' in service myservice" + assert_output --partial "Not propagating environment variables to container" + + unstub docker + unstub buildkite-agent +} + +@test "Run with a prebuilt image and propagate environment" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_ENV_FILE=/tmp/test_env + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_ENVIRONMENT=true + + echo "VAR0=1" > "${BUILDKITE_ENV_FILE}" + echo "VAR2=lalala" >> "${BUILDKITE_ENV_FILE}" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -e \* -e \* --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with vars \${12} and \${14}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "Running /bin/sh -e -c 'pwd' in service myservice" + assert_output --partial "ran myservice with vars VAR0 and VAR2" + + unstub docker + unstub buildkite-agent + + rm "${BUILDKITE_ENV_FILE}" +} + +@test "Run with a prebuilt image" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a prebuilt image and custom config file" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a prebuilt image and multiple custom config files" { +export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_1=tests/composefiles/docker-compose.v2.1.yml + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo pulled myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -f tests/composefiles/docker-compose.v2.1.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml-tests/composefiles/docker-compose.v2.1.yml : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a prebuilt image and custom config file set from COMPOSE_FILE" { + export COMPOSE_FILE=tests/composefiles/docker-compose.v2.0.yml + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f tests/composefiles/docker-compose.v2.0.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice-tests/composefiles/docker-compose.v2.0.yml : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a single prebuilt image, no retry on failed pull" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : exit 2" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "Exited with 2" + unstub docker + unstub buildkite-agent +} + +@test "Run with a single prebuilt image, retry on failed pull" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PULL_RETRIES=3 + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : exit 2" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a TTY" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_TTY=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -T --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without tty" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without tty" + unstub docker + unstub buildkite-agent +} + +@test "Run without dependencies" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_DEPENDENCIES=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --no-deps --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without dependencies" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without dependencies" + unstub docker + unstub buildkite-agent +} + +@test "Run with dependencies but in a single step" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PRE_RUN_DEPENDENCIES=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with dependencies" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with dependencies" + + unstub docker + unstub buildkite-agent +} + +@test "Run without ansi output" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ANSI=false + + stub docker \ + "compose --no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose --no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose --no-ansi -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without ansi output" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without ansi output" + unstub docker + unstub buildkite-agent +} + +@test "Run with use aliases" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USE_ALIASES=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --use-aliases --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with use aliases output" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with use aliases output" + unstub docker + unstub buildkite-agent +} + + +@test "Run with compatibility mode" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_COMPATIBILITY=true + + stub docker \ + "compose --compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose --compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose --compatibility -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with use aliases output" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with use aliases output" + unstub docker + unstub buildkite-agent +} + +@test "Run with a volumes option" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_0="./dist:/app/dist" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES_1="./pkg:/app/pkg" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v $PWD/dist:/app/dist -v $PWD/pkg:/app/pkg --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker + unstub buildkite-agent +} + +@test "Run with an external volume" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_VOLUMES="buildkite:/buildkite" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v buildkite:/buildkite --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker + unstub buildkite-agent +} + +@test "Run with default volumes, extra delimiters" { + # Tests introduction of extra delimiters, as would occur if + # EXPORT BUILDKITE_DOCKER_DEFAULT_VOLUMES="new:mount; ${BUILDKITE_DOCKER_DEFAULT_VOLUMES:-}" + # was used with no existing value + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_DOCKER_DEFAULT_VOLUMES="buildkite:/buildkite; ./dist:/app/dist;; ; ;" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v buildkite:/buildkite -v $PWD/dist:/app/dist --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker + unstub buildkite-agent +} + +@test "Run with default volumes" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_DOCKER_DEFAULT_VOLUMES="buildkite:/buildkite;./dist:/app/dist" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 -v buildkite:/buildkite -v $PWD/dist:/app/dist --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with volumes" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with volumes" + unstub docker + unstub buildkite-agent +} + +@test "Run with multiple config files" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_0="llamas1.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_1="llamas2.yml" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CONFIG_2="llamas3.yml" + + stub docker \ + "compose -f llamas1.yml -f llamas2.yml -f llamas3.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f llamas1.yml -f llamas2.yml -f llamas3.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f llamas1.yml -f llamas2.yml -f llamas3.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice-llamas1.yml-llamas2.yml-llamas3.yml : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a failure should expand previous group" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : exit 2" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "^^^ +++" + assert_output --partial "Failed to run command, exited with 2" + unstub docker + unstub buildkite-agent +} + +@test "Run with multiple prebuilt images and multiple pulls" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PULL_0=myservice1 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PULL_1=myservice2 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull --parallel myservice1 myservice2 : echo pulled myservice1 and myservice2" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice1=0 myservice1 : echo started dependencies for myservice1" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice1_build_1 --rm myservice1 /bin/sh -e -c 'pwd' : echo ran myservice1" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice1 : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice1 : echo myimage1" \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice2 : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice2 : echo myimage2" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "pulled myservice1 and myservice2" + assert_output --partial "ran myservice1" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image and a custom user" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="sh -c 'whoami'" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000 --rm myservice /bin/sh -e -c $'sh -c \'whoami\'' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without a prebuilt image and a custom user and group" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="sh -c 'whoami'" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000:1001" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --user=1000:1001 --rm myservice /bin/sh -e -c $'sh -c \'whoami\'' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Fail with custom user and propagate UIDs" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="sh -c 'whoami'" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_USER="1000:1001" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_PROPAGATE_UID_GID="true" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "Error" + assert_output --partial "Can't set both user and propagate-uid-gid" + unstub buildkite-agent +} + + +@test "Run without --rm" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RM=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 myservice /bin/sh -e -c $'pwd' : echo ran myservice without tty" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : echo myimage" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without tty" + unstub docker + unstub buildkite-agent +} + +@test "Run with custom entrypoint" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENTRYPOINT="my custom entrypoint" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm --entrypoint 'my custom entrypoint' myservice : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-buildkite-agent enabled" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_BUILDKITE_AGENT=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e BUILDKITE_JOB_ID -e BUILDKITE_BUILD_ID -e BUILDKITE_AGENT_ACCESS_TOKEN -v $BATS_MOCK_TMPDIR/bin/buildkite-agent:/usr/bin/buildkite-agent myservice : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with various build arguments" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_NO_CACHE=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_BUILD_PARALLEL=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull --no-cache --parallel myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with git-mirrors" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_REPO_MIRROR=/tmp/sample-mirror + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -v /tmp/sample-mirror:/tmp/sample-mirror:ro --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-ssh-agent" { + export SSH_AUTH_SOCK=/tmp/ssh_auth_sock + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e SSH_AUTH_SOCK=/ssh-agent -v /tmp/ssh_auth_sock:/ssh-agent -v /root/.ssh/known_hosts:/root/.ssh/known_hosts myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + apk add netcat-openbsd + nc -lkvU $SSH_AUTH_SOCK & + + run "$PWD"/hooks/command + + kill %1 + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-ssh-agent on particular folder" { + export SSH_AUTH_SOCK=/tmp/ssh_auth_sock + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_SSH_AGENT=/tmp/test + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm -e SSH_AUTH_SOCK=/ssh-agent -v /tmp/ssh_auth_sock:/ssh-agent -v /root/.ssh/known_hosts:/tmp/test/.ssh/known_hosts myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + apk add netcat-openbsd + nc -lkvU $SSH_AUTH_SOCK & + + run "$PWD"/hooks/command + + kill %1 + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run without mount-checkout doesn't set volume" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice without mount-checkout" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without mount-checkout" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set to true" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 --workdir=/workdir -v /plugin:/workdir --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set to true with custom workdir" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT=true + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 \* -v \* --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout on \${13}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout on /plugin:/custom_workdir" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set to specific path" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="/special" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 -v \* --rm myservice /bin/sh -e -c 'pwd' : echo ran myservice with mount-checkout on \${12}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout on /plugin:/special" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set to specific path and workdir set" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="/special" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f \* run --name buildkite1111_myservice_build_1 \* -v \* --rm myservice /bin/sh -e -c 'pwd' : echo echo ran myservice with mount-checkout on \${13}" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice with mount-checkout on /plugin:/special" + assert_output --partial "--workdir=/custom_workdir" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set something else" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="not_absolute" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "mount-checkout should be either true or an absolute path to use as a mountpoint" + + unstub docker + unstub buildkite-agent +} + +@test "Run with mount-checkout set something else and workdir set" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_COMMAND=pwd + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_MOUNT_CHECKOUT="not-absolute" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WORKDIR="/custom_workdir" + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f \* pull myservice : echo pulled myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 0" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "mount-checkout should be either true or an absolute path to use as a mountpoint" + + unstub docker + unstub buildkite-agent +} + +@test "Run waiting for dependencies" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_WAIT=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up --wait -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + + unstub docker + unstub buildkite-agent +} + +@test "Run with --service-ports" { + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND=pwd + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_SERVICE_PORTS=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml pull myservice : echo pulled myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml up -d --scale myservice=0 myservice : echo started dependencies for myservice" \ + "compose -f docker-compose.yml -p buildkite1111 -f docker-compose.buildkite-1-override.yml run --name buildkite1111_myservice_build_1 --rm --service-ports myservice /bin/sh -e -c $'pwd' : echo ran myservice without tty" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : echo myimage" \ + "meta-data get docker-compose-plugin-built-image-tag-myservice : echo myimage" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "ran myservice without tty" + unstub docker + unstub buildkite-agent +} + +@test "Run with --quiet-pull" { + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PIPELINE_SLUG=test + + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_QUIET_PULL=true + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up --quiet-pull -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + refute_output --partial "Pulling" + assert_output --partial "ran myservice" + + unstub docker + unstub buildkite-agent +} + +@test "Run with a list of propagated env vars" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_PROPAGATION_LIST="LIST_OF_VARS" + export LIST_OF_VARS="VAR_A VAR_B VAR_C" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub docker \ + "compose -f docker-compose.yml -p buildkite1111 build --pull myservice : echo built myservice" \ + "compose -f docker-compose.yml -p buildkite1111 up -d --scale myservice=0 myservice : echo ran myservice dependencies" \ + "compose -f docker-compose.yml -p buildkite1111 run --name buildkite1111_myservice_build_1 -e VAR_A -e VAR_B -e VAR_C --rm myservice /bin/sh -e -c 'echo hello world' : echo ran myservice" + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_success + assert_output --partial "built myservice" + assert_output --partial "ran myservice" + unstub docker + unstub buildkite-agent +} + +@test "Run with a list of propagated env vars - unless you forgot to define the variable" { + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_ENV_PROPAGATION_LIST="LIST_OF_VARS" + export BUILDKITE_JOB_ID=1111 + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_RUN=myservice + export BUILDKITE_PIPELINE_SLUG=test + export BUILDKITE_BUILD_NUMBER=1 + export BUILDKITE_COMMAND="echo hello world" + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CHECK_LINKED_CONTAINERS=false + export BUILDKITE_PLUGIN_DOCKER_COMPOSE_CLEANUP=false + + stub buildkite-agent \ + "meta-data exists docker-compose-plugin-built-image-tag-myservice : exit 1" + + run "$PWD"/hooks/command + + assert_failure + assert_output --partial "env-propagation-list desired, but LIST_OF_VARS is not defined!" + unstub buildkite-agent +}