diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8de7a7f9b184..4b82120db2406 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1664,6 +1664,7 @@ ${{ hashFiles('.pre-commit-config.yaml') }}" fail-fast: false matrix: python-version: ${{ fromJson(needs.build-info.outputs.pythonVersions) }} + platform: ["linux/amd64", "linux/arm64"] env: RUNS_ON: ${{ fromJson(needs.build-info.outputs.runsOn) }} PYTHON_MAJOR_MINOR_VERSION: ${{ matrix.python-version }} @@ -1685,11 +1686,12 @@ ${{ hashFiles('.pre-commit-config.yaml') }}" run: breeze free-space - name: "Start ARM instance" run: ./scripts/ci/images/ci_start_arm_instance_and_connect_to_docker.sh + if: matrix.platform == 'linux/arm64' - name: "Build & Push CI image ${{ matrix.python-version }}:latest" run: > breeze build-image --prepare-buildx-cache - --platform linux/amd64,linux/arm64 + --platform ${{ matrix.platform }} env: PYTHON_MAJOR_MINOR_VERSION: ${{ matrix.python-version }} - name: > @@ -1722,12 +1724,12 @@ ${{ hashFiles('.pre-commit-config.yaml') }}" --install-packages-from-context --prepare-buildx-cache --disable-airflow-repo-cache - --platform linux/amd64,linux/arm64 + --platform ${{ matrix.platform }} env: PYTHON_MAJOR_MINOR_VERSION: ${{ matrix.python-version }} - name: "Stop ARM instance" run: ./scripts/ci/images/ci_stop_arm_instance.sh - if: always() + if: always() && matrix.platform == 'linux/arm64' - name: "Fix ownership" run: breeze fix-ownership if: always() diff --git a/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py index 08c39adb456f9..5934bc2358157 100644 --- a/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py +++ b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py @@ -71,7 +71,7 @@ build_cache, perform_environment_checks, prepare_docker_build_command, - prepare_empty_docker_build_command, + prepare_docker_build_from_input, ) from airflow_breeze.utils.image import run_pull_image, run_pull_in_parallel, tag_image_as_latest from airflow_breeze.utils.mark_image_as_refreshed import mark_image_as_refreshed @@ -142,13 +142,13 @@ { "name": "Preparing cache and push (for maintainers and CI)", "options": [ - "--platform", - "--prepare-buildx-cache", - "--push-image", - "--empty-image", "--github-token", "--github-username", + "--platform", "--login-to-github-registry", + "--push-image", + "--empty-image", + "--prepare-buildx-cache", ], }, ], @@ -457,54 +457,45 @@ def build_ci_image(verbose: bool, dry_run: bool, ci_image_params: BuildCiParams) return 0, f"Image build: {ci_image_params.python}" if ci_image_params.prepare_buildx_cache or ci_image_params.push_image: login_to_github_docker_registry(image_params=ci_image_params, dry_run=dry_run, verbose=verbose) - cmd = prepare_docker_build_command( - image_params=ci_image_params, - verbose=verbose, - ) - if ci_image_params.empty_image: - env = os.environ.copy() - env['DOCKER_BUILDKIT'] = "1" - get_console().print(f"\n[info]Building empty CI Image for Python {ci_image_params.python}\n") - cmd = prepare_empty_docker_build_command(image_params=ci_image_params) - build_command_result = run_command( - cmd, - input="FROM scratch\n", - verbose=verbose, - dry_run=dry_run, - cwd=AIRFLOW_SOURCES_ROOT, - text=True, - env=env, - ) + if ci_image_params.prepare_buildx_cache: + build_command_result = build_cache(image_params=ci_image_params, dry_run=dry_run, verbose=verbose) else: - get_console().print(f"\n[info]Building CI Image for Python {ci_image_params.python}\n") - build_command_result = run_command( - cmd, verbose=verbose, dry_run=dry_run, cwd=AIRFLOW_SOURCES_ROOT, text=True, check=False - ) - if build_command_result.returncode == 0: - if ci_image_params.prepare_buildx_cache: - build_command_result = build_cache( - image_params=ci_image_params, dry_run=dry_run, verbose=verbose - ) - - if not ci_image_params.prepare_buildx_cache: - if not dry_run: + if ci_image_params.empty_image: + env = os.environ.copy() + env['DOCKER_BUILDKIT'] = "1" + get_console().print(f"\n[info]Building empty CI Image for Python {ci_image_params.python}\n") + build_command_result = run_command( + prepare_docker_build_from_input(image_params=ci_image_params), + input="FROM scratch\n", + verbose=verbose, + dry_run=dry_run, + cwd=AIRFLOW_SOURCES_ROOT, + text=True, + env=env, + ) + else: + get_console().print(f"\n[info]Building CI Image for Python {ci_image_params.python}\n") + build_command_result = run_command( + prepare_docker_build_command( + image_params=ci_image_params, + verbose=verbose, + ), + verbose=verbose, + dry_run=dry_run, + cwd=AIRFLOW_SOURCES_ROOT, + text=True, + check=False, + ) if build_command_result.returncode == 0: if ci_image_params.tag_as_latest: build_command_result = tag_image_as_latest(ci_image_params, dry_run, verbose) - if ( - ci_image_params.airflow_image_name == ci_image_params.airflow_image_name_with_tag - or ci_image_params.tag_as_latest - and build_command_result.returncode == 0 - ): - mark_image_as_refreshed(ci_image_params) - else: - get_console().print("[error]Error when building image![/]") - return ( - build_command_result.returncode, - f"Image build: {ci_image_params.python}", - ) - else: - get_console().print("[info]Not updating build cache because we are in `dry_run` mode.[/]") + if ci_image_params.preparing_latest_image(): + if dry_run: + get_console().print( + "[info]Not updating build hash because we are in `dry_run` mode.[/]" + ) + else: + mark_image_as_refreshed(ci_image_params) return build_command_result.returncode, f"Image build: {ci_image_params.python}" diff --git a/dev/breeze/src/airflow_breeze/commands/production_image_commands.py b/dev/breeze/src/airflow_breeze/commands/production_image_commands.py index 32aed85d9ed14..e10d46cda4081 100644 --- a/dev/breeze/src/airflow_breeze/commands/production_image_commands.py +++ b/dev/breeze/src/airflow_breeze/commands/production_image_commands.py @@ -70,7 +70,7 @@ build_cache, perform_environment_checks, prepare_docker_build_command, - prepare_empty_docker_build_command, + prepare_docker_build_from_input, ) from airflow_breeze.utils.image import run_pull_image, run_pull_in_parallel, tag_image_as_latest from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT, DOCKER_CONTEXT_DIR @@ -148,11 +148,11 @@ "options": [ "--github-token", "--github-username", + "--platform", "--login-to-github-registry", "--push-image", - "--prepare-buildx-cache", - "--platform", "--empty-image", + "--prepare-buildx-cache", ], }, ], @@ -497,36 +497,36 @@ def build_production_image( if prod_image_params.prepare_buildx_cache or prod_image_params.push_image: login_to_github_docker_registry(image_params=prod_image_params, dry_run=dry_run, verbose=verbose) get_console().print(f"\n[info]Building PROD Image for Python {prod_image_params.python}\n") - if prod_image_params.empty_image: - env = os.environ.copy() - env['DOCKER_BUILDKIT'] = "1" - get_console().print(f"\n[info]Building empty PROD Image for Python {prod_image_params.python}\n") - cmd = prepare_empty_docker_build_command(image_params=prod_image_params) - build_command_result = run_command( - cmd, - input="FROM scratch\n", - verbose=verbose, - dry_run=dry_run, - cwd=AIRFLOW_SOURCES_ROOT, - check=False, - text=True, - env=env, - ) + if prod_image_params.prepare_buildx_cache: + build_command_result = build_cache(image_params=prod_image_params, dry_run=dry_run, verbose=verbose) else: - cmd = prepare_docker_build_command( - image_params=prod_image_params, - verbose=verbose, - ) - build_command_result = run_command( - cmd, verbose=verbose, dry_run=dry_run, cwd=AIRFLOW_SOURCES_ROOT, check=False, text=True - ) - if build_command_result.returncode == 0: - if prod_image_params.prepare_buildx_cache: - build_command_result = build_cache( - image_params=prod_image_params, dry_run=dry_run, verbose=verbose - ) - else: + if prod_image_params.empty_image: + env = os.environ.copy() + env['DOCKER_BUILDKIT'] = "1" + get_console().print(f"\n[info]Building empty PROD Image for Python {prod_image_params.python}\n") + build_command_result = run_command( + prepare_docker_build_from_input(image_params=prod_image_params), + input="FROM scratch\n", + verbose=verbose, + dry_run=dry_run, + cwd=AIRFLOW_SOURCES_ROOT, + check=False, + text=True, + env=env, + ) + else: + build_command_result = run_command( + prepare_docker_build_command( + image_params=prod_image_params, + verbose=verbose, + ), + verbose=verbose, + dry_run=dry_run, + cwd=AIRFLOW_SOURCES_ROOT, + check=False, + text=True, + ) + if build_command_result.returncode == 0: if prod_image_params.tag_as_latest: build_command_result = tag_image_as_latest(prod_image_params, dry_run, verbose) - return build_command_result.returncode, f"Image build: {prod_image_params.python}" diff --git a/dev/breeze/src/airflow_breeze/params/build_ci_params.py b/dev/breeze/src/airflow_breeze/params/build_ci_params.py index b012993519a92..aa9681fc955fe 100644 --- a/dev/breeze/src/airflow_breeze/params/build_ci_params.py +++ b/dev/breeze/src/airflow_breeze/params/build_ci_params.py @@ -20,13 +20,12 @@ from airflow_breeze.branch_defaults import DEFAULT_AIRFLOW_CONSTRAINTS_BRANCH from airflow_breeze.global_constants import get_airflow_version -from airflow_breeze.params._common_build_params import _CommonBuildParams -from airflow_breeze.utils.console import get_console +from airflow_breeze.params.common_build_params import CommonBuildParams from airflow_breeze.utils.path_utils import BUILD_CACHE_DIR @dataclass -class BuildCiParams(_CommonBuildParams): +class BuildCiParams(CommonBuildParams): """ CI build parameters. Those parameters are used to determine command issued to build CI image. """ @@ -96,6 +95,4 @@ def optional_image_args(self) -> List[str]: ] def __post_init__(self): - if self.prepare_buildx_cache: - get_console().print("[info]Forcing --push-image since we are preparing buildx cache[/]") - self.push_image = True + pass diff --git a/dev/breeze/src/airflow_breeze/params/build_prod_params.py b/dev/breeze/src/airflow_breeze/params/build_prod_params.py index c480570f52ef4..e81a5d6739ac8 100644 --- a/dev/breeze/src/airflow_breeze/params/build_prod_params.py +++ b/dev/breeze/src/airflow_breeze/params/build_prod_params.py @@ -30,12 +30,12 @@ get_airflow_extras, get_airflow_version, ) -from airflow_breeze.params._common_build_params import _CommonBuildParams +from airflow_breeze.params.common_build_params import CommonBuildParams from airflow_breeze.utils.console import get_console @dataclass -class BuildProdParams(_CommonBuildParams): +class BuildProdParams(CommonBuildParams): """ PROD build parameters. Those parameters are used to determine command issued to build PROD image. """ diff --git a/dev/breeze/src/airflow_breeze/params/_common_build_params.py b/dev/breeze/src/airflow_breeze/params/common_build_params.py similarity index 96% rename from dev/breeze/src/airflow_breeze/params/_common_build_params.py rename to dev/breeze/src/airflow_breeze/params/common_build_params.py index 4f2139d20e723..fd8a2b82e8c8c 100644 --- a/dev/breeze/src/airflow_breeze/params/_common_build_params.py +++ b/dev/breeze/src/airflow_breeze/params/common_build_params.py @@ -27,7 +27,7 @@ @dataclass -class _CommonBuildParams: +class CommonBuildParams: """ Common build parameters. Those parameters are common parameters for CI And PROD build. """ @@ -147,6 +147,9 @@ def get_cache(self, single_platform: str) -> str: def is_multi_platform(self) -> bool: return "," in self.platform + def preparing_latest_image(self) -> bool: + return self.tag_as_latest or self.airflow_image_name == self.airflow_image_name_with_tag + @property def platforms(self) -> List[str]: return self.platform.split(",") @@ -160,6 +163,4 @@ def optional_image_args(self) -> List[str]: raise NotImplementedError() def __post_init__(self): - if self.prepare_buildx_cache: - get_console().print("[info]Forcing --push-image since we are preparing buildx cache[/]") - self.push_image = True + pass diff --git a/dev/breeze/src/airflow_breeze/utils/common_options.py b/dev/breeze/src/airflow_breeze/utils/common_options.py index 545a6ec149912..deca1b4882a4e 100644 --- a/dev/breeze/src/airflow_breeze/utils/common_options.py +++ b/dev/breeze/src/airflow_breeze/utils/common_options.py @@ -284,8 +284,7 @@ ) option_prepare_buildx_cache = click.option( '--prepare-buildx-cache', - help='Prepares build cache additionally to building images (this is done as two separate steps after' - 'the images are build). Implies --push-image flag', + help='Prepares build cache (this is done as separate per-platform steps instead of building the image).', is_flag=True, envvar='PREPARE_BUILDX_CACHE', ) diff --git a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py index fc3f1b17fca73..e838e380f781d 100644 --- a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py +++ b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py @@ -24,9 +24,9 @@ from subprocess import DEVNULL, STDOUT, CalledProcessError, CompletedProcess from typing import Dict, List, Union -from airflow_breeze.params._common_build_params import _CommonBuildParams from airflow_breeze.params.build_ci_params import BuildCiParams from airflow_breeze.params.build_prod_params import BuildProdParams +from airflow_breeze.params.common_build_params import CommonBuildParams from airflow_breeze.params.shell_params import ShellParams from airflow_breeze.utils.host_info_utils import get_host_group_id, get_host_os, get_host_user_id from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT @@ -58,9 +58,8 @@ from airflow_breeze.utils.console import get_console from airflow_breeze.utils.run_utils import ( RunCommandResult, + check_if_buildx_plugin_installed, commit_sha, - prepare_base_build_command, - prepare_build_cache_command, run_command, ) @@ -313,7 +312,7 @@ def check_docker_compose_version(verbose: bool): ) -def get_env_variable_value(arg_name: str, params: Union[BuildCiParams, BuildProdParams, ShellParams]): +def get_env_variable_value(arg_name: str, params: Union[CommonBuildParams, ShellParams]): raw_value = getattr(params, arg_name, None) value = str(raw_value) if raw_value is not None else '' value = "true" if raw_value is True else value @@ -323,7 +322,7 @@ def get_env_variable_value(arg_name: str, params: Union[BuildCiParams, BuildProd return value -def prepare_arguments_for_docker_build_command(image_params: _CommonBuildParams) -> List[str]: +def prepare_arguments_for_docker_build_command(image_params: CommonBuildParams) -> List[str]: """ Constructs docker compose command arguments list based on parameters passed. Maps arguments to argument values. @@ -353,9 +352,7 @@ def prepare_arguments_for_docker_build_command(image_params: _CommonBuildParams) def prepare_docker_build_cache_command( - image_params: _CommonBuildParams, - dry_run: bool, - verbose: bool, + image_params: CommonBuildParams, ) -> List[str]: """ Constructs docker build_cache command based on the parameters passed. @@ -365,11 +362,10 @@ def prepare_docker_build_cache_command( :return: Command to run as list of string """ arguments = prepare_arguments_for_docker_build_command(image_params) - build_command = prepare_build_cache_command() build_flags = image_params.extra_docker_build_flags final_command = [] final_command.extend(["docker"]) - final_command.extend(build_command) + final_command.extend(["buildx", "build", "--builder", "airflow_cache", "--progress=tty"]) final_command.extend(build_flags) final_command.extend(["--pull"]) final_command.extend(arguments) @@ -381,16 +377,42 @@ def prepare_docker_build_cache_command( final_command.extend( [f"--cache-to=type=registry,ref={image_params.get_cache(image_params.platform)},mode=max"] ) - cmd = ['docker', 'buildx', 'inspect', 'airflow_cache'] - buildx_command_result = run_command(cmd, verbose=verbose, dry_run=dry_run, text=True) - if buildx_command_result and buildx_command_result.returncode != 0: - next_cmd = ['docker', 'buildx', 'create', '--name', 'airflow_cache'] - run_command(next_cmd, verbose=verbose, text=True, check=False) return final_command +def prepare_base_build_command(image_params: CommonBuildParams, verbose: bool) -> List[str]: + """ + Prepare build command for docker build. Depending on whether we have buildx plugin installed or not, + and whether we run cache preparation, there might be different results: + + * if buildx plugin is installed - `docker buildx` command is returned - using regular or cache builder + depending on whether we build regular image or cache + * if no buildx plugin is installed, and we do not prepare cache, regular docker `build` command is used. + * if no buildx plugin is installed, and we prepare cache - we fail. Cache can only be done with buildx + :param image_params: parameters of the image + :param verbose: print commands when running + :return: command to use as docker build command + """ + build_command_param = [] + is_buildx_available = check_if_buildx_plugin_installed(verbose=verbose) + if is_buildx_available: + build_command_param.extend( + [ + "buildx", + "build", + "--builder", + "default", + "--progress=tty", + "--push" if image_params.push_image else "--load", + ] + ) + else: + build_command_param.append("build") + return build_command_param + + def prepare_docker_build_command( - image_params: _CommonBuildParams, + image_params: CommonBuildParams, verbose: bool, ) -> List[str]: """ @@ -417,7 +439,7 @@ def prepare_docker_build_command( def construct_docker_push_command( - image_params: _CommonBuildParams, + image_params: CommonBuildParams, ) -> List[str]: """ Constructs docker push command based on the parameters passed. @@ -427,8 +449,8 @@ def construct_docker_push_command( return ["docker", "push", image_params.airflow_image_name_with_tag] -def prepare_empty_docker_build_command( - image_params: _CommonBuildParams, +def prepare_docker_build_from_input( + image_params: CommonBuildParams, ) -> List[str]: """ Constructs docker build empty image command based on the parameters passed. @@ -438,18 +460,21 @@ def prepare_empty_docker_build_command( return ["docker", "build", "-t", image_params.airflow_image_name_with_tag, "-"] -def build_cache(image_params: _CommonBuildParams, dry_run: bool, verbose: bool) -> RunCommandResult: +def build_cache(image_params: CommonBuildParams, dry_run: bool, verbose: bool) -> RunCommandResult: build_command_result: Union[CompletedProcess, CalledProcessError] = CompletedProcess( args=[], returncode=0 ) + cmd = ['docker', 'buildx', 'inspect', 'airflow_cache'] + buildx_command_result = run_command(cmd, verbose=verbose, dry_run=dry_run, text=True, check=False) + if buildx_command_result and buildx_command_result.returncode != 0: + next_cmd = ['docker', 'buildx', 'create', '--name', 'airflow_cache'] + run_command(next_cmd, verbose=verbose, text=True, check=False) for platform in image_params.platforms: platform_image_params = deepcopy(image_params) # override the platform in the copied params to only be single platform per run # as a workaround to https://github.com/docker/buildx/issues/1044 platform_image_params.platform = platform - cmd = prepare_docker_build_cache_command( - image_params=platform_image_params, dry_run=dry_run, verbose=verbose - ) + cmd = prepare_docker_build_cache_command(image_params=platform_image_params) build_command_result = run_command( cmd, verbose=verbose, dry_run=dry_run, cwd=AIRFLOW_SOURCES_ROOT, check=False, text=True ) diff --git a/dev/breeze/src/airflow_breeze/utils/image.py b/dev/breeze/src/airflow_breeze/utils/image.py index 1962951f949ea..80da7f75c5273 100644 --- a/dev/breeze/src/airflow_breeze/utils/image.py +++ b/dev/breeze/src/airflow_breeze/utils/image.py @@ -25,9 +25,9 @@ DEFAULT_PYTHON_MAJOR_MINOR_VERSION, MOUNT_ALL, ) -from airflow_breeze.params._common_build_params import _CommonBuildParams from airflow_breeze.params.build_ci_params import BuildCiParams from airflow_breeze.params.build_prod_params import BuildProdParams +from airflow_breeze.params.common_build_params import CommonBuildParams from airflow_breeze.params.shell_params import ShellParams from airflow_breeze.utils.console import get_console from airflow_breeze.utils.mark_image_as_refreshed import mark_image_as_refreshed @@ -82,7 +82,7 @@ def run_pull_in_parallel( def run_pull_image( - image_params: _CommonBuildParams, + image_params: CommonBuildParams, dry_run: bool, verbose: bool, wait_for_image: bool, @@ -156,7 +156,7 @@ def run_pull_image( return command_result.returncode, f"Image Python {image_params.python}" -def tag_image_as_latest(image_params: _CommonBuildParams, dry_run: bool, verbose: bool) -> RunCommandResult: +def tag_image_as_latest(image_params: CommonBuildParams, dry_run: bool, verbose: bool) -> RunCommandResult: if image_params.airflow_image_name_with_tag == image_params.airflow_image_name: get_console().print( f"[info]Skip tagging {image_params.airflow_image_name} as latest as it is already 'latest'[/]" @@ -177,7 +177,7 @@ def tag_image_as_latest(image_params: _CommonBuildParams, dry_run: bool, verbose def run_pull_and_verify_image( - image_params: _CommonBuildParams, + image_params: CommonBuildParams, dry_run: bool, verbose: bool, wait_for_image: bool, diff --git a/dev/breeze/src/airflow_breeze/utils/registry.py b/dev/breeze/src/airflow_breeze/utils/registry.py index bf01ec61b3aba..820ae9d2739da 100644 --- a/dev/breeze/src/airflow_breeze/utils/registry.py +++ b/dev/breeze/src/airflow_breeze/utils/registry.py @@ -18,13 +18,13 @@ import os from typing import Tuple -from airflow_breeze.params._common_build_params import _CommonBuildParams +from airflow_breeze.params.common_build_params import CommonBuildParams from airflow_breeze.utils.console import get_console from airflow_breeze.utils.run_utils import run_command def login_to_github_docker_registry( - image_params: _CommonBuildParams, dry_run: bool, verbose: bool + image_params: CommonBuildParams, dry_run: bool, verbose: bool ) -> Tuple[int, str]: """ In case of CI environment, we need to login to GitHub Registry. diff --git a/dev/breeze/src/airflow_breeze/utils/run_utils.py b/dev/breeze/src/airflow_breeze/utils/run_utils.py index 03f3c0532d205..f228cc657cf60 100644 --- a/dev/breeze/src/airflow_breeze/utils/run_utils.py +++ b/dev/breeze/src/airflow_breeze/utils/run_utils.py @@ -28,7 +28,6 @@ from typing import Dict, Generator, List, Mapping, Optional, Union from airflow_breeze.branch_defaults import AIRFLOW_BRANCH -from airflow_breeze.params._common_build_params import _CommonBuildParams from airflow_breeze.utils.ci_group import ci_group from airflow_breeze.utils.console import get_console from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT @@ -302,64 +301,6 @@ def check_if_buildx_plugin_installed(verbose: bool) -> bool: return False -def prepare_base_build_command(image_params: _CommonBuildParams, verbose: bool) -> List[str]: - """ - Prepare build command for docker build. Depending on whether we have buildx plugin installed or not, - and whether we run cache preparation, there might be different results: - - * if buildx plugin is installed - `docker buildx` command is returned - using regular or cache builder - depending on whether we build regular image or cache - * if no buildx plugin is installed, and we do not prepare cache, regular docker `build` command is used. - * if no buildx plugin is installed, and we prepare cache - we fail. Cache can only be done with buildx - :param image_params: parameters of the image - :param verbose: print commands when running - :return: command to use as docker build command - """ - build_command_param = [] - is_buildx_available = check_if_buildx_plugin_installed(verbose=verbose) - if is_buildx_available: - if image_params.prepare_buildx_cache: - build_command_param.extend( - ["buildx", "build", "--builder", "airflow_cache", "--progress=tty", "--push"] - ) - else: - build_command_param.extend( - [ - "buildx", - "build", - "--builder", - "default", - "--progress=tty", - "--push" if image_params.push_image else "--load", - ] - ) - else: - if image_params.prepare_buildx_cache or image_params.push_image: - get_console().print( - '\n[error] Buildx cli plugin is not available and you need it to prepare' - ' buildx cache or push image after build. \n' - ) - get_console().print( - '[error] Please install it following https://docs.docker.com/buildx/working-with-buildx/ \n' - ) - sys.exit(1) - build_command_param.append("build") - return build_command_param - - -def prepare_build_cache_command() -> List[str]: - """ - Prepare build cache command for docker build. We need to have buildx for that command. - This command is needed separately from the build image command because of the bug in multiplatform - support for buildx plugin https://github.com/docker/buildx/issues/1044 where when you run multiple - platform build, cache from one platform overrides cache for the other platform. - - :param verbose: print commands when running - :return: command to use as docker build command - """ - return ["buildx", "build", "--builder", "airflow_cache", "--progress=tty"] - - @lru_cache(maxsize=None) def commit_sha(): """Returns commit SHA of current repo. Cached for various usages.""" diff --git a/images/breeze/output-build-image.svg b/images/breeze/output-build-image.svg index 91028e25d8dde..6b555c27f3428 100644 --- a/images/breeze/output-build-image.svg +++ b/images/breeze/output-build-image.svg @@ -19,92 +19,92 @@ font-weight: 700; } - .terminal-1486275555-matrix { + .terminal-3295394084-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 26.400000000000002px; font-variant-east-asian: full-width; } - .terminal-1486275555-title { + .terminal-3295394084-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1486275555-r1 { fill: #c5c8c6;font-weight: bold } -.terminal-1486275555-r2 { fill: #c5c8c6 } -.terminal-1486275555-r3 { fill: #d0b344;font-weight: bold } -.terminal-1486275555-r4 { fill: #868887 } -.terminal-1486275555-r5 { fill: #68a0b3;font-weight: bold } -.terminal-1486275555-r6 { fill: #98a84b;font-weight: bold } -.terminal-1486275555-r7 { fill: #8d7b39 } + .terminal-3295394084-r1 { fill: #c5c8c6;font-weight: bold } +.terminal-3295394084-r2 { fill: #c5c8c6 } +.terminal-3295394084-r3 { fill: #d0b344;font-weight: bold } +.terminal-3295394084-r4 { fill: #868887 } +.terminal-3295394084-r5 { fill: #68a0b3;font-weight: bold } +.terminal-3295394084-r6 { fill: #98a84b;font-weight: bold } +.terminal-3295394084-r7 { fill: #8d7b39 } - Command: build-image + Command: build-image -                                                                                                                          - Usage: breeze build-image [OPTIONS]                                                                                     -                                                                                                                         - Build CI image. Include building multiple images for all python versions (sequentially).                                -                                                                                                                         -╭─ Basic usage ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -  --python                         -p  Python major/minor version used in Airflow image for images.                    -                                       (>3.7< | 3.8 | 3.9 | 3.10)                                                      -                                       [default: 3.7]                                                                  -  --upgrade-to-newer-dependencies  -u  When set, upgrade all PIP packages to latest.                                   -  --debian-version                     Debian version used for the image. (bullseye | buster) [default: bullseye]      -  --image-tag                      -t  Tag added to the default naming conventions of Airflow CI/PROD images. (TEXT)   -  --tag-as-latest                      Tags the image as latest and update checksum of all files after pulling.        -                                       Useful when you build or pull image with --image-tag.                           -  --docker-cache                   -c  Cache option for image used during the build. (registry | local | disabled)     -                                       [default: registry]                                                             -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Building multiple images ───────────────────────────────────────────────────────────────────────────────────────────╮ -  --build-multiple-images    Run the operation sequentially on all or selected subset of Python versions.              -  --python-versions          Space separated list of python versions used for build with multiple versions. (TEXT)     -                             [default: 3.7 3.8 3.9 3.10]                                                               -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Advanced options (for power users) ─────────────────────────────────────────────────────────────────────────────────╮ -  --install-providers-from-sources    Install providers from sources when installing.                                  -  --airflow-constraints-mode          Mode of constraints for CI image building                                        -                                      (constraints-source-providers | constraints | constraints-no-providers)          -                                      [default: constraints-source-providers]                                          -  --airflow-constraints-reference     Constraint reference to use when building the image. (TEXT)                      -  --additional-python-deps            Additional python dependencies to use when building the images. (TEXT)           -  --runtime-apt-deps                  Apt runtime dependencies to use when building the images. (TEXT)                 -  --runtime-apt-command               Command executed before runtime apt deps are installed. (TEXT)                   -  --additional-extras                 Additional extra package while installing Airflow in the image. (TEXT)           -  --additional-runtime-apt-deps       Additional apt runtime dependencies to use when building the images. (TEXT)      -  --additional-runtime-apt-env        Additional environment variables set when adding runtime dependencies. (TEXT)    -  --additional-runtime-apt-command    Additional command executed before runtime apt deps are installed. (TEXT)        -  --additional-dev-apt-deps           Additional apt dev dependencies to use when building the images. (TEXT)          -  --additional-dev-apt-env            Additional environment variables set when adding dev dependencies. (TEXT)        -  --additional-dev-apt-command        Additional command executed before dev apt deps are installed. (TEXT)            -  --dev-apt-deps                      Apt dev dependencies to use when building the images. (TEXT)                     -  --dev-apt-command                   Command executed before dev apt deps are installed. (TEXT)                       -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Preparing cache and push (for maintainers and CI) ──────────────────────────────────────────────────────────────────╮ -  --platform                Platform for Airflow image. (linux/amd64 | linux/arm64 | linux/amd64,linux/arm64)          -  --prepare-buildx-cache    Prepares build cache additionally to building images (this is done as two separate steps   -                            afterthe images are build). Implies --push-image flag                                      -  --push-image              Push image after building it.                                                              -  --empty-image             Prepare empty image tagged with the same name as the Airflow image.                        -  --github-token            The token used to authenticate to GitHub. (TEXT)                                           -  --github-username         The user name used to authenticate to GitHub. (TEXT)                                       -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -  --github-repository  -g  GitHub repository used to pull, push run images. (TEXT) [default: apache/airflow]           -  --verbose            -v  Print verbose information about performed steps.                                            -  --dry-run            -D  If dry-run is set, commands are only printed, not executed.                                 -  --answer             -a  Force answer to questions. (y | n | q | yes | no | quit)                                    -  --help               -h  Show this message and exit.                                                                 -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +                                                                                                                          + Usage: breeze build-image [OPTIONS]                                                                                     +                                                                                                                         + Build CI image. Include building multiple images for all python versions (sequentially).                                +                                                                                                                         +╭─ Basic usage ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +  --python                         -p  Python major/minor version used in Airflow image for images.                    +                                       (>3.7< | 3.8 | 3.9 | 3.10)                                                      +                                       [default: 3.7]                                                                  +  --upgrade-to-newer-dependencies  -u  When set, upgrade all PIP packages to latest.                                   +  --debian-version                     Debian version used for the image. (bullseye | buster) [default: bullseye]      +  --image-tag                      -t  Tag added to the default naming conventions of Airflow CI/PROD images. (TEXT)   +  --tag-as-latest                      Tags the image as latest and update checksum of all files after pulling.        +                                       Useful when you build or pull image with --image-tag.                           +  --docker-cache                   -c  Cache option for image used during the build. (registry | local | disabled)     +                                       [default: registry]                                                             +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Building multiple images ───────────────────────────────────────────────────────────────────────────────────────────╮ +  --build-multiple-images    Run the operation sequentially on all or selected subset of Python versions.              +  --python-versions          Space separated list of python versions used for build with multiple versions. (TEXT)     +                             [default: 3.7 3.8 3.9 3.10]                                                               +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Advanced options (for power users) ─────────────────────────────────────────────────────────────────────────────────╮ +  --install-providers-from-sources    Install providers from sources when installing.                                  +  --airflow-constraints-mode          Mode of constraints for CI image building                                        +                                      (constraints-source-providers | constraints | constraints-no-providers)          +                                      [default: constraints-source-providers]                                          +  --airflow-constraints-reference     Constraint reference to use when building the image. (TEXT)                      +  --additional-python-deps            Additional python dependencies to use when building the images. (TEXT)           +  --runtime-apt-deps                  Apt runtime dependencies to use when building the images. (TEXT)                 +  --runtime-apt-command               Command executed before runtime apt deps are installed. (TEXT)                   +  --additional-extras                 Additional extra package while installing Airflow in the image. (TEXT)           +  --additional-runtime-apt-deps       Additional apt runtime dependencies to use when building the images. (TEXT)      +  --additional-runtime-apt-env        Additional environment variables set when adding runtime dependencies. (TEXT)    +  --additional-runtime-apt-command    Additional command executed before runtime apt deps are installed. (TEXT)        +  --additional-dev-apt-deps           Additional apt dev dependencies to use when building the images. (TEXT)          +  --additional-dev-apt-env            Additional environment variables set when adding dev dependencies. (TEXT)        +  --additional-dev-apt-command        Additional command executed before dev apt deps are installed. (TEXT)            +  --dev-apt-deps                      Apt dev dependencies to use when building the images. (TEXT)                     +  --dev-apt-command                   Command executed before dev apt deps are installed. (TEXT)                       +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Preparing cache and push (for maintainers and CI) ──────────────────────────────────────────────────────────────────╮ +  --github-token            The token used to authenticate to GitHub. (TEXT)                                           +  --github-username         The user name used to authenticate to GitHub. (TEXT)                                       +  --platform                Platform for Airflow image. (linux/amd64 | linux/arm64 | linux/amd64,linux/arm64)          +  --push-image              Push image after building it.                                                              +  --empty-image             Prepare empty image tagged with the same name as the Airflow image.                        +  --prepare-buildx-cache    Prepares build cache (this is done as separate per-platform steps instead of building      +                            the image).                                                                                +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +  --github-repository  -g  GitHub repository used to pull, push run images. (TEXT) [default: apache/airflow]           +  --verbose            -v  Print verbose information about performed steps.                                            +  --dry-run            -D  If dry-run is set, commands are only printed, not executed.                                 +  --answer             -a  Force answer to questions. (y | n | q | yes | no | quit)                                    +  --help               -h  Show this message and exit.                                                                 +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ diff --git a/images/breeze/output-build-prod-image.svg b/images/breeze/output-build-prod-image.svg index d90abce791153..247461d55530c 100644 --- a/images/breeze/output-build-prod-image.svg +++ b/images/breeze/output-build-prod-image.svg @@ -19,110 +19,110 @@ font-weight: 700; } - .terminal-3989203328-matrix { + .terminal-997154497-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 26.400000000000002px; font-variant-east-asian: full-width; } - .terminal-3989203328-title { + .terminal-997154497-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3989203328-r1 { fill: #c5c8c6;font-weight: bold } -.terminal-3989203328-r2 { fill: #c5c8c6 } -.terminal-3989203328-r3 { fill: #d0b344;font-weight: bold } -.terminal-3989203328-r4 { fill: #868887 } -.terminal-3989203328-r5 { fill: #68a0b3;font-weight: bold } -.terminal-3989203328-r6 { fill: #98a84b;font-weight: bold } -.terminal-3989203328-r7 { fill: #8d7b39 } + .terminal-997154497-r1 { fill: #c5c8c6;font-weight: bold } +.terminal-997154497-r2 { fill: #c5c8c6 } +.terminal-997154497-r3 { fill: #d0b344;font-weight: bold } +.terminal-997154497-r4 { fill: #868887 } +.terminal-997154497-r5 { fill: #68a0b3;font-weight: bold } +.terminal-997154497-r6 { fill: #98a84b;font-weight: bold } +.terminal-997154497-r7 { fill: #8d7b39 } - Command: build-prod-image + Command: build-prod-image -                                                                                                                          - Usage: breeze build-prod-image [OPTIONS]                                                                                -                                                                                                                         - Build Production image. Include building multiple images for all or selected Python versions sequentially.              -                                                                                                                         -╭─ Basic usage ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -  --python                         -p  Python major/minor version used in Airflow image for images.                    -                                       (>3.7< | 3.8 | 3.9 | 3.10)                                                      -                                       [default: 3.7]                                                                  -  --install-airflow-version        -V  Install version of Airflow from PyPI. (TEXT)                                    -  --upgrade-to-newer-dependencies  -u  When set, upgrade all PIP packages to latest.                                   -  --debian-version                     Debian version used for the image. (bullseye | buster) [default: bullseye]      -  --image-tag                      -t  Tag added to the default naming conventions of Airflow CI/PROD images. (TEXT)   -  --tag-as-latest                      Tags the image as latest and update checksum of all files after pulling.        -                                       Useful when you build or pull image with --image-tag.                           -  --docker-cache                   -c  Cache option for image used during the build. (registry | local | disabled)     -                                       [default: registry]                                                             -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Building multiple images ───────────────────────────────────────────────────────────────────────────────────────────╮ -  --build-multiple-images    Run the operation sequentially on all or selected subset of Python versions.              -  --python-versions          Space separated list of python versions used for build with multiple versions. (TEXT)     -                             [default: 3.7 3.8 3.9 3.10]                                                               -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Options for customizing images ─────────────────────────────────────────────────────────────────────────────────────╮ -  --install-providers-from-sources    Install providers from sources when installing.                                  -  --airflow-extras                    Extras to install by default.                                                    -                                      (TEXT)                                                                           -                                      [default:                                                                        -                                      amazon,async,celery,cncf.kubernetes,dask,docker,elasticsearch,ftp,google,goog…   -  --airflow-constraints-mode          Mode of constraints for PROD image building                                      -                                      (constraints | constraints-no-providers | constraints-source-providers)          -                                      [default: constraints]                                                           -  --airflow-constraints-reference     Constraint reference to use when building the image. (TEXT)                      -  --additional-python-deps            Additional python dependencies to use when building the images. (TEXT)           -  --additional-extras                 Additional extra package while installing Airflow in the image. (TEXT)           -  --additional-runtime-apt-deps       Additional apt runtime dependencies to use when building the images. (TEXT)      -  --additional-runtime-apt-env        Additional environment variables set when adding runtime dependencies. (TEXT)    -  --additional-runtime-apt-command    Additional command executed before runtime apt deps are installed. (TEXT)        -  --additional-dev-apt-deps           Additional apt dev dependencies to use when building the images. (TEXT)          -  --additional-dev-apt-env            Additional environment variables set when adding dev dependencies. (TEXT)        -  --additional-dev-apt-command        Additional command executed before dev apt deps are installed. (TEXT)            -  --runtime-apt-deps                  Apt runtime dependencies to use when building the images. (TEXT)                 -  --runtime-apt-command               Command executed before runtime apt deps are installed. (TEXT)                   -  --dev-apt-deps                      Apt dev dependencies to use when building the images. (TEXT)                     -  --dev-apt-command                   Command executed before dev apt deps are installed. (TEXT)                       -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Customization options (for specific customization needs) ───────────────────────────────────────────────────────────╮ -  --install-packages-from-context           Install wheels from local docker-context-files when building image.        -  --airflow-is-in-context                   If set Airflow is installed from docker-context-files only rather than     -                                            from PyPI or sources.                                                      -  --cleanup-context                         Clean up docker context files before running build (cannot be used         -                                            together with --install-packages-from-context).                            -  --disable-mysql-client-installation       Do not install MySQL client.                                               -  --disable-mssql-client-installation       Do not install MsSQl client.                                               -  --disable-postgres-client-installation    Do not install Postgres client.                                            -  --disable-airflow-repo-cache              Disable cache from Airflow repository during building.                     -  --install-airflow-reference               Install Airflow using GitHub tag or branch. (TEXT)                         -  --installation-method                     Install Airflow from: sources or PyPI. (. | apache-airflow)                -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Preparing cache and push (for maintainers and CI) ──────────────────────────────────────────────────────────────────╮ -  --github-token            The token used to authenticate to GitHub. (TEXT)                                           -  --github-username         The user name used to authenticate to GitHub. (TEXT)                                       -  --push-image              Push image after building it.                                                              -  --prepare-buildx-cache    Prepares build cache additionally to building images (this is done as two separate steps   -                            afterthe images are build). Implies --push-image flag                                      -  --platform                Platform for Airflow image. (linux/amd64 | linux/arm64 | linux/amd64,linux/arm64)          -  --empty-image             Prepare empty image tagged with the same name as the Airflow image.                        -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -  --github-repository  -g  GitHub repository used to pull, push run images. (TEXT) [default: apache/airflow]           -  --answer             -a  Force answer to questions. (y | n | q | yes | no | quit)                                    -  --dry-run            -D  If dry-run is set, commands are only printed, not executed.                                 -  --verbose            -v  Print verbose information about performed steps.                                            -  --help               -h  Show this message and exit.                                                                 -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +                                                                                                                          + Usage: breeze build-prod-image [OPTIONS]                                                                                +                                                                                                                         + Build Production image. Include building multiple images for all or selected Python versions sequentially.              +                                                                                                                         +╭─ Basic usage ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +  --python                         -p  Python major/minor version used in Airflow image for images.                    +                                       (>3.7< | 3.8 | 3.9 | 3.10)                                                      +                                       [default: 3.7]                                                                  +  --install-airflow-version        -V  Install version of Airflow from PyPI. (TEXT)                                    +  --upgrade-to-newer-dependencies  -u  When set, upgrade all PIP packages to latest.                                   +  --debian-version                     Debian version used for the image. (bullseye | buster) [default: bullseye]      +  --image-tag                      -t  Tag added to the default naming conventions of Airflow CI/PROD images. (TEXT)   +  --tag-as-latest                      Tags the image as latest and update checksum of all files after pulling.        +                                       Useful when you build or pull image with --image-tag.                           +  --docker-cache                   -c  Cache option for image used during the build. (registry | local | disabled)     +                                       [default: registry]                                                             +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Building multiple images ───────────────────────────────────────────────────────────────────────────────────────────╮ +  --build-multiple-images    Run the operation sequentially on all or selected subset of Python versions.              +  --python-versions          Space separated list of python versions used for build with multiple versions. (TEXT)     +                             [default: 3.7 3.8 3.9 3.10]                                                               +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options for customizing images ─────────────────────────────────────────────────────────────────────────────────────╮ +  --install-providers-from-sources    Install providers from sources when installing.                                  +  --airflow-extras                    Extras to install by default.                                                    +                                      (TEXT)                                                                           +                                      [default:                                                                        +                                      amazon,async,celery,cncf.kubernetes,dask,docker,elasticsearch,ftp,google,goog…   +  --airflow-constraints-mode          Mode of constraints for PROD image building                                      +                                      (constraints | constraints-no-providers | constraints-source-providers)          +                                      [default: constraints]                                                           +  --airflow-constraints-reference     Constraint reference to use when building the image. (TEXT)                      +  --additional-python-deps            Additional python dependencies to use when building the images. (TEXT)           +  --additional-extras                 Additional extra package while installing Airflow in the image. (TEXT)           +  --additional-runtime-apt-deps       Additional apt runtime dependencies to use when building the images. (TEXT)      +  --additional-runtime-apt-env        Additional environment variables set when adding runtime dependencies. (TEXT)    +  --additional-runtime-apt-command    Additional command executed before runtime apt deps are installed. (TEXT)        +  --additional-dev-apt-deps           Additional apt dev dependencies to use when building the images. (TEXT)          +  --additional-dev-apt-env            Additional environment variables set when adding dev dependencies. (TEXT)        +  --additional-dev-apt-command        Additional command executed before dev apt deps are installed. (TEXT)            +  --runtime-apt-deps                  Apt runtime dependencies to use when building the images. (TEXT)                 +  --runtime-apt-command               Command executed before runtime apt deps are installed. (TEXT)                   +  --dev-apt-deps                      Apt dev dependencies to use when building the images. (TEXT)                     +  --dev-apt-command                   Command executed before dev apt deps are installed. (TEXT)                       +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Customization options (for specific customization needs) ───────────────────────────────────────────────────────────╮ +  --install-packages-from-context           Install wheels from local docker-context-files when building image.        +  --airflow-is-in-context                   If set Airflow is installed from docker-context-files only rather than     +                                            from PyPI or sources.                                                      +  --cleanup-context                         Clean up docker context files before running build (cannot be used         +                                            together with --install-packages-from-context).                            +  --disable-mysql-client-installation       Do not install MySQL client.                                               +  --disable-mssql-client-installation       Do not install MsSQl client.                                               +  --disable-postgres-client-installation    Do not install Postgres client.                                            +  --disable-airflow-repo-cache              Disable cache from Airflow repository during building.                     +  --install-airflow-reference               Install Airflow using GitHub tag or branch. (TEXT)                         +  --installation-method                     Install Airflow from: sources or PyPI. (. | apache-airflow)                +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Preparing cache and push (for maintainers and CI) ──────────────────────────────────────────────────────────────────╮ +  --github-token            The token used to authenticate to GitHub. (TEXT)                                           +  --github-username         The user name used to authenticate to GitHub. (TEXT)                                       +  --platform                Platform for Airflow image. (linux/amd64 | linux/arm64 | linux/amd64,linux/arm64)          +  --push-image              Push image after building it.                                                              +  --empty-image             Prepare empty image tagged with the same name as the Airflow image.                        +  --prepare-buildx-cache    Prepares build cache (this is done as separate per-platform steps instead of building      +                            the image).                                                                                +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +  --github-repository  -g  GitHub repository used to pull, push run images. (TEXT) [default: apache/airflow]           +  --answer             -a  Force answer to questions. (y | n | q | yes | no | quit)                                    +  --dry-run            -D  If dry-run is set, commands are only printed, not executed.                                 +  --verbose            -v  Print verbose information about performed steps.                                            +  --help               -h  Show this message and exit.                                                                 +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ diff --git a/images/breeze/output-commands-hash.txt b/images/breeze/output-commands-hash.txt index 4d58a2969e4be..60451263b17ab 100644 --- a/images/breeze/output-commands-hash.txt +++ b/images/breeze/output-commands-hash.txt @@ -1 +1 @@ -969f1b4101773456e832c03b8d4e89ef +4b9bda7e1b32565b73e604c969dbdc86