From 30f3a1321cb3647ac319650c4195f524d920b624 Mon Sep 17 00:00:00 2001 From: Maciej Bogusz Date: Tue, 17 Jun 2025 17:35:07 +0200 Subject: [PATCH 1/7] Add docker_compose_v2_build module (#956) --- meta/runtime.yml | 1 + plugins/modules/docker_compose_v2_build.py | 190 +++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 plugins/modules/docker_compose_v2_build.py diff --git a/meta/runtime.yml b/meta/runtime.yml index e228ab555..195911d17 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -7,6 +7,7 @@ requires_ansible: '>=2.15.0' action_groups: docker: - docker_compose_v2 + - docker_compose_v2_build - docker_compose_v2_exec - docker_compose_v2_pull - docker_compose_v2_run diff --git a/plugins/modules/docker_compose_v2_build.py b/plugins/modules/docker_compose_v2_build.py new file mode 100644 index 000000000..52c4308f7 --- /dev/null +++ b/plugins/modules/docker_compose_v2_build.py @@ -0,0 +1,190 @@ +#!/usr/bin/python +# +# Copyright (c) 2023, Felix Fontein +# Copyright (c) 2025, Maciej Bogusz (@mjbogusz) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +module: docker_compose_v2_build + +short_description: Build a Docker compose project + +version_added: 4.7.0 + +description: + - Uses Docker Compose to build images for a project. +extends_documentation_fragment: + - community.docker.compose_v2 + - community.docker.compose_v2.minimum_version + - community.docker.docker.cli_documentation + - community.docker.attributes + - community.docker.attributes.actiongroup_docker + +attributes: + check_mode: + support: full + diff_mode: + support: none + idempotent: + support: full + +options: + no_cache: + description: + - If set to V(true), will not use cache when building the images. + type: bool + default: false + pull: + description: + - If set to V(true), will attempt to pull newer version of the image + type: bool + default: false + with_dependencies: + description: + - If set to V(true), also build services that are declared as dependencies. + - This only makes sense if O(services) is used. + type: bool + default: false + memory_limit: + description: + - Memory limit for the build container, in bytes. Not supported by BuildKit + type: int + services: + description: + - Specifies a subset of services to be targeted. + type: list + elements: str + +author: + - Maciej Bogusz (@mjbogusz) + +seealso: + - module: community.docker.docker_compose_v2 +""" + +EXAMPLES = r""" +--- +- name: Build images for flask project + community.docker.docker_compose_v2_build: + project_src: /path/to/flask +""" + +RETURN = r""" +actions: + description: + - A list of actions that have been applied. + returned: success + type: list + elements: dict + contains: + what: + description: + - What kind of resource was changed. + type: str + sample: container + choices: + - image + - unknown + id: + description: + - The ID of the resource that was changed. + type: str + sample: container + status: + description: + - The status change that happened. + type: str + sample: Building + choices: + - Building +""" + +import traceback + +from ansible.module_utils.common.text.converters import to_native + +from ansible_collections.community.docker.plugins.module_utils.common_cli import ( + AnsibleModuleDockerClient, + DockerException, +) + +from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( + BaseComposeManager, + common_compose_argspec_ex, +) + + +class BuildManager(BaseComposeManager): + def __init__(self, client): + super(BuildManager, self).__init__(client) + parameters = self.client.module.params + + self.no_cache = parameters['no_cache'] + self.pull = parameters['pull'] + self.with_dependencies = parameters['with_dependencies'] + self.memory_limit = parameters['memory_limit'] + self.services = parameters['services'] or [] + + def get_build_cmd(self, dry_run): + args = self.get_base_args() + ['build'] + if self.no_cache: + args.append('--no-cache') + if self.pull: + args.append('--pull') + if self.with_dependencies: + args.append('--with-dependencies') + if self.memory_limit: + args.extend(['--memory', str(self.memory_limit)]) + if dry_run: + args.append('--dry-run') + args.append('--') + for service in self.services: + args.append(service) + return args + + def run(self): + result = dict() + args = self.get_build_cmd(self.check_mode) + rc, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src) + events = self.parse_events(stderr, dry_run=self.check_mode, nonzero_rc=rc != 0) + self.emit_warnings(events) + self.update_result(result, events, stdout, stderr, ignore_ignore_build_events=not self.check_mode) + self.update_failed(result, events, args, stdout, stderr, rc) + self.cleanup_result(result) + return result + + +def main(): + argument_spec = dict( + no_cache=dict(type='bool', default=False), + pull=dict(type='bool', default=False), + with_dependencies=dict(type='bool', default=False), + memory_limit=dict(type='int'), + services=dict(type='list', elements='str'), + ) + argspec_ex = common_compose_argspec_ex() + argument_spec.update(argspec_ex.pop('argspec')) + + client = AnsibleModuleDockerClient( + argument_spec=argument_spec, + supports_check_mode=True, + needs_api_version=False, + **argspec_ex + ) + + try: + manager = BuildManager(client) + result = manager.run() + manager.cleanup() + client.module.exit_json(**result) + except DockerException as e: + client.fail('An unexpected docker error occurred: {0}'.format(to_native(e)), exception=traceback.format_exc()) + + +if __name__ == '__main__': + main() From 48745bf8c1cc32557a036108f7ad60456f299616 Mon Sep 17 00:00:00 2001 From: Maciej Bogusz Date: Wed, 9 Jul 2025 19:40:20 +0200 Subject: [PATCH 2/7] review fix --- plugins/modules/docker_compose_v2_build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/docker_compose_v2_build.py b/plugins/modules/docker_compose_v2_build.py index 52c4308f7..a5d695a14 100644 --- a/plugins/modules/docker_compose_v2_build.py +++ b/plugins/modules/docker_compose_v2_build.py @@ -41,7 +41,7 @@ default: false pull: description: - - If set to V(true), will attempt to pull newer version of the image + - If set to V(true), will attempt to pull newer version of the image. type: bool default: false with_dependencies: @@ -52,7 +52,7 @@ default: false memory_limit: description: - - Memory limit for the build container, in bytes. Not supported by BuildKit + - Memory limit for the build container, in bytes. Not supported by BuildKit. type: int services: description: From c8ff5847a3835b8bf680b3a3272484ce15d21993 Mon Sep 17 00:00:00 2001 From: Maciej Bogusz Date: Wed, 9 Jul 2025 19:40:40 +0200 Subject: [PATCH 3/7] docker_compose_v2_build: initial test rig --- .../targets/docker_compose_v2_build/aliases | 6 + .../docker_compose_v2_build/meta/main.yml | 10 ++ .../docker_compose_v2_build/tasks/main.yml | 52 ++++++++ .../tasks/run-test.yml | 7 ++ .../tasks/tests/build.yml | 114 ++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 tests/integration/targets/docker_compose_v2_build/aliases create mode 100644 tests/integration/targets/docker_compose_v2_build/meta/main.yml create mode 100644 tests/integration/targets/docker_compose_v2_build/tasks/main.yml create mode 100644 tests/integration/targets/docker_compose_v2_build/tasks/run-test.yml create mode 100644 tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml diff --git a/tests/integration/targets/docker_compose_v2_build/aliases b/tests/integration/targets/docker_compose_v2_build/aliases new file mode 100644 index 000000000..2e1acc0ad --- /dev/null +++ b/tests/integration/targets/docker_compose_v2_build/aliases @@ -0,0 +1,6 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/4 +destructive diff --git a/tests/integration/targets/docker_compose_v2_build/meta/main.yml b/tests/integration/targets/docker_compose_v2_build/meta/main.yml new file mode 100644 index 000000000..aefcf50f2 --- /dev/null +++ b/tests/integration/targets/docker_compose_v2_build/meta/main.yml @@ -0,0 +1,10 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +dependencies: + - setup_docker_cli_compose + # The Python dependencies are needed for the other modules + - setup_docker_python_deps + - setup_remote_tmp_dir diff --git a/tests/integration/targets/docker_compose_v2_build/tasks/main.yml b/tests/integration/targets/docker_compose_v2_build/tasks/main.yml new file mode 100644 index 000000000..d2c8497b1 --- /dev/null +++ b/tests/integration/targets/docker_compose_v2_build/tasks/main.yml @@ -0,0 +1,52 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Create random name prefix (for services, ...) +- name: Create random container name prefix + set_fact: + name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}" + cnames: [] + dnetworks: [] + +- debug: + msg: "Using name prefix {{ name_prefix }}" + +- name: Show images + command: docker images --all --digests + +# Run the tests +- block: + - name: Show docker compose --help output + command: docker compose --help + + - include_tasks: run-test.yml + with_fileglob: + - "tests/*.yml" + loop_control: + loop_var: test_name + + always: + - name: "Make sure all containers are removed" + docker_container: + name: "{{ item }}" + state: absent + force_kill: true + with_items: "{{ cnames }}" + diff: false + + - name: "Make sure all networks are removed" + docker_network: + name: "{{ item }}" + state: absent + force: true + with_items: "{{ dnetworks }}" + diff: false + + when: docker_has_compose and docker_compose_version is version('2.18.0', '>=') diff --git a/tests/integration/targets/docker_compose_v2_build/tasks/run-test.yml b/tests/integration/targets/docker_compose_v2_build/tasks/run-test.yml new file mode 100644 index 000000000..72a58962d --- /dev/null +++ b/tests/integration/targets/docker_compose_v2_build/tasks/run-test.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: "Loading tasks from {{ test_name }}" + include_tasks: "{{ test_name }}" diff --git a/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml b/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml new file mode 100644 index 000000000..b30ca1f30 --- /dev/null +++ b/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml @@ -0,0 +1,114 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- vars: + bname: "{{ name_prefix }}-build" + cname: "{{ name_prefix }}-cont" + non_existing_image: does-not-exist:latest + dockerfile_path: test-dockerfile + base_image: hello-world:latest + project_src: "{{ remote_tmp_dir }}/{{ bname }}" + test_service_non_existing: | + version: '3' + services: + {{ cname }}: + image: {{ non_existing_image }} + build: + dockerfile: Dockerfile-does-not-exist + test_service_simple: | + version: '3' + services: + {{ cname }}: + image: {{ docker_test_image_simple_1 }} + build: + dockerfile: {{ dockerfile_path }} + command: 10m + stop_grace_period: 1s + test_service_simple_dockerfile: | + FROM {{ base_image }} + + block: + - name: Registering container name + set_fact: + cnames: "{{ cnames + [bname ~ '-' ~ cname ~ '-1'] + [ base_image ]}}" + dnetworks: "{{ dnetworks + [bname ~ '_default'] }}" + + - name: Create project directory + file: + path: '{{ project_src }}' + state: directory + + - name: Make sure images are not around + docker_image_remove: + name: '{{ item }}' + loop: + - '{{ base_image }}' + - '{{ non_existing_image }}' + - '{{ docker_test_image_simple_1 }}' + +#################################################################### +## Image with missing dockerfile ################################### +#################################################################### + + - name: Template project file with non-existing image + copy: + dest: '{{ project_src }}/docker-compose.yml' + content: '{{ test_service_non_existing }}' + + - name: Build (check) + docker_compose_v2_build: + project_src: '{{ project_src }}' + check_mode: true + register: build_1_check + ignore_errors: true + + - name: Build + docker_compose_v2_build: + project_src: '{{ project_src }}' + register: build_1 + ignore_errors: true + + - assert: + that: + - build_1_check is failed or build_1_check is changed + - build_1_check is changed or build_1_check.msg.startswith('Error when processing ') + - build_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 + - build_1 is failed + - build_1.msg.startswith('Error when processing ') + - build_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 + +#################################################################### +## Regular image ################################################### +#################################################################### + + - name: Template project file with simple dockerfile + copy: + dest: '{{ project_src }}/docker-compose.yml' + content: '{{ test_service_simple }}' + + - name: Template dockerfile + copy: + dest: '{{ project_src }}/{{ dockerfile_path }}' + content: '{{ test_service_simple_dockerfile }}' + + - name: Build (check) + docker_compose_v2_build: + project_src: '{{ project_src }}' + check_mode: true + register: build_1_check + + - name: Build + docker_compose_v2_build: + project_src: '{{ project_src }}' + register: build_1 + + - assert: + that: + - build_1_check is changed + - (build_1_check.actions | selectattr('status', 'eq', 'Building') | first) is truthy + - build_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 + - build_1 is changed + - (build_1.actions | selectattr('status', 'eq', 'Building') | first) is truthy + - build_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 From f9925d770e2fe39f14352821985f918d0d7aadd6 Mon Sep 17 00:00:00 2001 From: Maciej Bogusz Date: Wed, 9 Jul 2025 19:41:06 +0200 Subject: [PATCH 4/7] docker_compose_v2_build: reference in other docs --- README.md | 1 + docs/docsite/rst/scenario_guide.rst | 3 +++ plugins/modules/docker_compose_v2.py | 3 +++ 3 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 622a2f450..d65c254ec 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ If you use the Ansible package and do not update collections independently, use - community.docker.docker_volume_info: retrieve information on Docker volumes * Docker Compose: - community.docker.docker_compose_v2: manage Docker Compose files (Docker compose CLI plugin) + - community.docker.docker_compose_v2_build: build images for a Docker compose project - community.docker.docker_compose_v2_exec: run command in a container of a Compose service - community.docker.docker_compose_v2_pull: pull a Docker compose project - community.docker.docker_compose_v2_run: run command in a new container of a Compose service diff --git a/docs/docsite/rst/scenario_guide.rst b/docs/docsite/rst/scenario_guide.rst index e17bae561..152996b8c 100644 --- a/docs/docsite/rst/scenario_guide.rst +++ b/docs/docsite/rst/scenario_guide.rst @@ -263,6 +263,9 @@ There are several modules for working with Docker Compose projects: community.docker.docker_compose_v2 The :ansplugin:`community.docker.docker_compose_v2 module ` allows you to use your existing Docker Compose files to orchestrate containers on a single Docker daemon or on Swarm. + community.docker.docker_compose_v2_build + The :ansplugin:`community.docker.docker_compose_v2_pull module ` allows you to build images for Docker compose projects. + community.docker.docker_compose_v2_exec The :ansplugin:`community.docker.docker_compose_v2_exec module ` allows you to run a command in a container of Docker Compose projects. diff --git a/plugins/modules/docker_compose_v2.py b/plugins/modules/docker_compose_v2.py index cab98b29b..224823a3a 100644 --- a/plugins/modules/docker_compose_v2.py +++ b/plugins/modules/docker_compose_v2.py @@ -174,7 +174,10 @@ - Felix Fontein (@felixfontein) seealso: + - module: community.docker.docker_compose_v2_build + - module: community.docker.docker_compose_v2_exec - module: community.docker.docker_compose_v2_pull + - module: community.docker.docker_compose_v2_run """ EXAMPLES = r""" From ddbbbe5b9ed02c1f4ac0791d714b63387046204e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 10 Jul 2025 22:12:42 +0200 Subject: [PATCH 5/7] Fix wrong parameter name. --- plugins/modules/docker_compose_v2_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/docker_compose_v2_build.py b/plugins/modules/docker_compose_v2_build.py index a5d695a14..383d48f55 100644 --- a/plugins/modules/docker_compose_v2_build.py +++ b/plugins/modules/docker_compose_v2_build.py @@ -153,7 +153,7 @@ def run(self): rc, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src) events = self.parse_events(stderr, dry_run=self.check_mode, nonzero_rc=rc != 0) self.emit_warnings(events) - self.update_result(result, events, stdout, stderr, ignore_ignore_build_events=not self.check_mode) + self.update_result(result, events, stdout, stderr, ignore_build_events=not self.check_mode) self.update_failed(result, events, args, stdout, stderr, rc) self.cleanup_result(result) return result From 76b5c2c742fff35d93bba6fc93f0a6f26457bbe7 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 12 Jul 2025 21:18:04 +0200 Subject: [PATCH 6/7] Never ignore build events. --- plugins/modules/docker_compose_v2_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/docker_compose_v2_build.py b/plugins/modules/docker_compose_v2_build.py index 383d48f55..df002d53a 100644 --- a/plugins/modules/docker_compose_v2_build.py +++ b/plugins/modules/docker_compose_v2_build.py @@ -153,7 +153,7 @@ def run(self): rc, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src) events = self.parse_events(stderr, dry_run=self.check_mode, nonzero_rc=rc != 0) self.emit_warnings(events) - self.update_result(result, events, stdout, stderr, ignore_build_events=not self.check_mode) + self.update_result(result, events, stdout, stderr, ignore_build_events=False) self.update_failed(result, events, args, stdout, stderr, rc) self.cleanup_result(result) return result From 260e9cc254a12fa6e082221bdf19e4bcf09edc27 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 12 Jul 2025 21:30:07 +0200 Subject: [PATCH 7/7] Try to get tests running. --- .../docker_compose_v2_build/tasks/main.yml | 7 ++ .../tasks/tests/build.yml | 65 ++++++++++++++++--- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/tests/integration/targets/docker_compose_v2_build/tasks/main.yml b/tests/integration/targets/docker_compose_v2_build/tasks/main.yml index d2c8497b1..89784660c 100644 --- a/tests/integration/targets/docker_compose_v2_build/tasks/main.yml +++ b/tests/integration/targets/docker_compose_v2_build/tasks/main.yml @@ -13,6 +13,7 @@ set_fact: name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}" cnames: [] + inames: [] dnetworks: [] - debug: @@ -41,6 +42,12 @@ with_items: "{{ cnames }}" diff: false + - name: "Make sure all images are removed" + docker_image_remove: + name: "{{ item }}" + with_items: "{{ inames }}" + diff: false + - name: "Make sure all networks are removed" docker_network: name: "{{ item }}" diff --git a/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml b/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml index b30ca1f30..e13676301 100644 --- a/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml +++ b/tests/integration/targets/docker_compose_v2_build/tasks/tests/build.yml @@ -8,31 +8,32 @@ cname: "{{ name_prefix }}-cont" non_existing_image: does-not-exist:latest dockerfile_path: test-dockerfile - base_image: hello-world:latest + base_image: "{{ docker_test_image_hello_world }}" + image_name: "{{ name_prefix }}-image" project_src: "{{ remote_tmp_dir }}/{{ bname }}" test_service_non_existing: | - version: '3' services: {{ cname }}: image: {{ non_existing_image }} build: dockerfile: Dockerfile-does-not-exist test_service_simple: | - version: '3' services: {{ cname }}: - image: {{ docker_test_image_simple_1 }} + image: {{ image_name }} build: dockerfile: {{ dockerfile_path }} command: 10m stop_grace_period: 1s test_service_simple_dockerfile: | FROM {{ base_image }} + RUN [ "/hello" ] block: - name: Registering container name set_fact: - cnames: "{{ cnames + [bname ~ '-' ~ cname ~ '-1'] + [ base_image ]}}" + cnames: "{{ cnames + [bname ~ '-' ~ cname ~ '-1'] }}" + inames: "{{ inames + [ base_image, image_name ] }}" dnetworks: "{{ dnetworks + [bname ~ '_default'] }}" - name: Create project directory @@ -44,9 +45,13 @@ docker_image_remove: name: '{{ item }}' loop: - - '{{ base_image }}' - '{{ non_existing_image }}' - - '{{ docker_test_image_simple_1 }}' + - '{{ image_name }}' + + - name: Prune docker build cache + docker_prune: + builder_cache: true + builder_cache_all: true #################################################################### ## Image with missing dockerfile ################################### @@ -72,11 +77,13 @@ - assert: that: - - build_1_check is failed or build_1_check is changed - - build_1_check is changed or build_1_check.msg.startswith('Error when processing ') + - build_1_check is failed + - >- + build_1_check.msg | trim == "General error: failed to solve: failed to read dockerfile: open Dockerfile-does-not-exist: no such file or directory" - build_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 - build_1 is failed - - build_1.msg.startswith('Error when processing ') + - >- + build_1.msg | trim == "General error: failed to solve: failed to read dockerfile: open Dockerfile-does-not-exist: no such file or directory" - build_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 #################################################################### @@ -93,17 +100,48 @@ dest: '{{ project_src }}/{{ dockerfile_path }}' content: '{{ test_service_simple_dockerfile }}' + - docker_image_info: + name: "{{ image_name }}" + register: pre_image + - name: Build (check) docker_compose_v2_build: project_src: '{{ project_src }}' check_mode: true register: build_1_check + - docker_image_info: + name: "{{ image_name }}" + register: build_1_check_image + - name: Build docker_compose_v2_build: project_src: '{{ project_src }}' register: build_1 + - docker_image_info: + name: "{{ image_name }}" + register: build_1_image + + - name: Build (idempotent, check) + docker_compose_v2_build: + project_src: '{{ project_src }}' + check_mode: true + register: build_2_check + + - docker_image_info: + name: "{{ image_name }}" + register: build_2_check_image + + - name: Build (idempotent) + docker_compose_v2_build: + project_src: '{{ project_src }}' + register: build_2 + + - docker_image_info: + name: "{{ image_name }}" + register: build_2_image + - assert: that: - build_1_check is changed @@ -112,3 +150,10 @@ - build_1 is changed - (build_1.actions | selectattr('status', 'eq', 'Building') | first) is truthy - build_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 + + - build_2_check is not changed + - build_2_check.actions | selectattr('status', 'eq', 'Building') | length == 0 + - build_2_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0 + - build_2 is not changed + - build_2.actions | selectattr('status', 'eq', 'Building') | length == 0 + - build_2.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0