This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch v2-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v2-3-test by this push:
     new 254cb727e0 Refactor Breeze to group related methods and classes 
together (#23556)
254cb727e0 is described below

commit 254cb727e0641d6816e7c8b5978bcb96ac45a38f
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat May 7 15:56:34 2022 +0200

    Refactor Breeze to group related methods and classes together (#23556)
    
    This change refactors Breeze classes to more consistent approach.
    
    * The "commands" package only contains commands
    * All Parameters (BuildCi, BuildProd, BuildDoc, Shell) are now
      in "params" package
    * Required/Optional Build args are now members of the
      BuildCiParams, BuildProdParams which makes the params
      much more self-contained..
    * All utils are in "utils" package
    
    This helps with avoiding circular imports (all utios are now
    standalone and do not use any of the commands.
    
    Co-authored-by: eladkal <[email protected]>
    (cherry picked from commit 35b917396fa35e70c47e114da8f178b1afeca5bc)
---
 dev/breeze/src/airflow_breeze/breeze.py            |   5 +-
 .../src/airflow_breeze/build_image/ci/__init__.py  |  17 --
 .../build_image/ci/build_ci_image.py               | 207 --------------------
 .../airflow_breeze/build_image/prod/__init__.py    |  17 --
 .../build_image/prod/build_prod_image.py           | 193 ------------------
 .../{ci_image_tools.py => ci_image_commands.py}    | 215 ++++++++++++++++++++-
 ...y => configuration_and_maintenance_commands.py} |  10 +-
 .../airflow_breeze/commands/developer_commands.py  | 162 ++++++++++++----
 .../commands/{main.py => main_command.py}          |   4 +-
 ...image_tools.py => production_image_commands.py} | 144 +++++++++++++-
 ...anagement.py => release_management_commands.py} |  22 +--
 .../commands/{testing.py => testing_commands.py}   |  14 +-
 .../{commands => }/configure_rich_click.py         |  12 +-
 .../{build_image/ci => params}/build_ci_params.py  |  59 +++---
 .../prod => params}/build_prod_params.py           |  39 +++-
 .../src/airflow_breeze/params/doc_build_params.py  |  44 +++++
 .../{shell => params}/shell_params.py              |   2 +-
 dev/breeze/src/airflow_breeze/shell/__init__.py    |  17 --
 dev/breeze/src/airflow_breeze/shell/enter_shell.py | 132 -------------
 .../{commands => utils}/common_options.py          |  14 +-
 .../{commands => utils}/custom_param_types.py      |   0
 .../airflow_breeze/utils/docker_command_utils.py   |  22 +--
 .../mark_image_as_refreshed.py}                    |  13 +-
 dev/breeze/src/airflow_breeze/utils/pulll_image.py |   6 +-
 .../utils/rebuild_image_if_needed.py               |  58 ------
 dev/breeze/src/airflow_breeze/utils/registry.py    |   4 +-
 26 files changed, 641 insertions(+), 791 deletions(-)

diff --git a/dev/breeze/src/airflow_breeze/breeze.py 
b/dev/breeze/src/airflow_breeze/breeze.py
index 276021fd40..a596c2be49 100755
--- a/dev/breeze/src/airflow_breeze/breeze.py
+++ b/dev/breeze/src/airflow_breeze/breeze.py
@@ -15,9 +15,8 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
-from airflow_breeze.commands.configure_rich_click import click  # noqa
-from airflow_breeze.commands.main import main
+from airflow_breeze.configure_rich_click import click  # isort: skip # noqa
+from airflow_breeze.commands.main_command import main
 from airflow_breeze.utils.path_utils import 
find_airflow_sources_root_to_operate_on
 
 find_airflow_sources_root_to_operate_on()
diff --git a/dev/breeze/src/airflow_breeze/build_image/ci/__init__.py 
b/dev/breeze/src/airflow_breeze/build_image/ci/__init__.py
deleted file mode 100644
index 7a633ffa0b..0000000000
--- a/dev/breeze/src/airflow_breeze/build_image/ci/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Building CI image."""
diff --git a/dev/breeze/src/airflow_breeze/build_image/ci/build_ci_image.py 
b/dev/breeze/src/airflow_breeze/build_image/ci/build_ci_image.py
deleted file mode 100644
index c962027ddf..0000000000
--- a/dev/breeze/src/airflow_breeze/build_image/ci/build_ci_image.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import multiprocessing as mp
-import os
-import sys
-from typing import List, Tuple
-
-from airflow_breeze.build_image.ci.build_ci_params import (
-    OPTIONAL_CI_IMAGE_ARGS,
-    REQUIRED_CI_IMAGE_ARGS,
-    BuildCiParams,
-)
-from airflow_breeze.utils.cache import touch_cache_file
-from airflow_breeze.utils.confirm import STANDARD_TIMEOUT, Answer, user_confirm
-from airflow_breeze.utils.console import get_console
-from airflow_breeze.utils.docker_command_utils import (
-    construct_docker_build_command,
-    construct_empty_docker_build_command,
-    tag_and_push_image,
-)
-from airflow_breeze.utils.md5_build_check import (
-    calculate_md5_checksum_for_files,
-    md5sum_check_if_build_is_needed,
-)
-from airflow_breeze.utils.parallel import check_async_run_results
-from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT, 
BUILD_CACHE_DIR
-from airflow_breeze.utils.registry import login_to_docker_registry
-from airflow_breeze.utils.run_utils import (
-    fix_group_permissions,
-    instruct_build_image,
-    is_repo_rebased,
-    run_command,
-)
-
-
-def should_we_run_the_build(build_ci_params: BuildCiParams, verbose: bool) -> 
bool:
-    """
-    Check if we should run the build based on what files have been modified 
since last build and answer from
-    the user.
-
-    * If build is needed, the user is asked for confirmation
-    * If the branch is not rebased it warns the user to rebase (to make sure 
latest remote cache is useful)
-    * Builds Image/Skips/Quits depending on the answer
-
-    :param build_ci_params: parameters for the build
-    :param verbose: should we get verbose information
-    """
-    # We import those locally so that click autocomplete works
-    from inputimeout import TimeoutOccurred
-
-    if not md5sum_check_if_build_is_needed(
-        md5sum_cache_dir=build_ci_params.md5sum_cache_dir, verbose=verbose
-    ):
-        return False
-    try:
-        answer = user_confirm(
-            message="Do you want to build the image?", 
timeout=STANDARD_TIMEOUT, default_answer=Answer.NO
-        )
-        if answer == answer.YES:
-            if is_repo_rebased(build_ci_params.github_repository, 
build_ci_params.airflow_branch):
-                return True
-            else:
-                get_console().print(
-                    "\n[warning]This might take a lot of time, we think you 
should rebase first.[/]\n"
-                )
-                answer = user_confirm(
-                    "But if you really, really want - you can do it. Are you 
really sure?",
-                    timeout=STANDARD_TIMEOUT,
-                    default_answer=Answer.NO,
-                )
-                if answer == Answer.YES:
-                    return True
-                else:
-                    get_console().print(
-                        "[info]Please rebase your code before continuing.[/]\n"
-                        "Check this link to know more "
-                        
"https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst#id15\n";
-                    )
-                    get_console().print('[error]Exiting the process[/]\n')
-                    sys.exit(1)
-        elif answer == Answer.NO:
-            instruct_build_image(build_ci_params.python)
-            return False
-        else:  # users_status == Answer.QUIT:
-            get_console().print('\n[warning]Quitting the process[/]\n')
-            sys.exit()
-    except TimeoutOccurred:
-        get_console().print('\nTimeout. Considering your response as No\n')
-        instruct_build_image(build_ci_params.python)
-        return False
-    except Exception as e:
-        get_console().print(f'\nTerminating the process on {e}')
-        sys.exit(1)
-
-
-def build_ci_image(verbose: bool, dry_run: bool, ci_image_params: 
BuildCiParams) -> Tuple[int, str]:
-    """
-    Builds CI image:
-
-      * fixes group permissions for files (to improve caching when umask is 
002)
-      * converts all the parameters received via kwargs into BuildCIParams 
(including cache)
-      * prints info about the image to build
-      * logs int to docker registry on CI if build cache is being executed
-      * removes "tag" for previously build image so that inline cache uses 
only remote image
-      * constructs docker-compose command to run based on parameters passed
-      * run the build command
-      * update cached information that the build completed and saves checksums 
of all files
-        for quick future check if the build is needed
-
-    :param verbose: print commands when running
-    :param dry_run: do not execute "write" commands - just print what would 
happen
-    :param ci_image_params: CI image parameters
-    """
-    fix_group_permissions(verbose=verbose)
-    if verbose or dry_run:
-        get_console().print(
-            f"\n[info]Building CI image of airflow from {AIRFLOW_SOURCES_ROOT} 
"
-            f"python version: {ci_image_params.python}[/]\n"
-        )
-    if not ci_image_params.force_build and not 
ci_image_params.upgrade_to_newer_dependencies:
-        if not should_we_run_the_build(build_ci_params=ci_image_params, 
verbose=verbose):
-            return 0, f"Image build: {ci_image_params.python}"
-    run_command(
-        ["docker", "rmi", "--no-prune", "--force", 
ci_image_params.airflow_image_name],
-        verbose=verbose,
-        dry_run=dry_run,
-        cwd=AIRFLOW_SOURCES_ROOT,
-        text=True,
-        check=False,
-    )
-    if ci_image_params.prepare_buildx_cache:
-        login_to_docker_registry(ci_image_params, dry_run=dry_run)
-    cmd = construct_docker_build_command(
-        image_params=ci_image_params,
-        verbose=verbose,
-        required_args=REQUIRED_CI_IMAGE_ARGS,
-        optional_args=OPTIONAL_CI_IMAGE_ARGS,
-        production_image=False,
-    )
-    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 = 
construct_empty_docker_build_command(image_params=ci_image_params)
-        build_result = run_command(
-            cmd,
-            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_result = run_command(
-            cmd, verbose=verbose, dry_run=dry_run, cwd=AIRFLOW_SOURCES_ROOT, 
text=True, check=False
-        )
-    if not dry_run:
-        if build_result.returncode == 0:
-            mark_image_as_refreshed(ci_image_params)
-        else:
-            get_console().print("[error]Error when building image![/]")
-            return (
-                build_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.push_image:
-        return tag_and_push_image(image_params=ci_image_params, 
dry_run=dry_run, verbose=verbose)
-    return build_result.returncode, f"Image build: {ci_image_params.python}"
-
-
-def mark_image_as_refreshed(ci_image_params: BuildCiParams):
-    ci_image_cache_dir = BUILD_CACHE_DIR / ci_image_params.airflow_branch
-    ci_image_cache_dir.mkdir(parents=True, exist_ok=True)
-    touch_cache_file(f"built_{ci_image_params.python}", 
root_dir=ci_image_cache_dir)
-    calculate_md5_checksum_for_files(ci_image_params.md5sum_cache_dir, 
update=True)
-
-
-def build_ci_image_in_parallel(
-    verbose: bool, dry_run: bool, parallelism: int, python_version_list: 
List[str], **kwargs
-):
-    """Run CI image builds in parallel."""
-    get_console().print(
-        f"\n[info]Running with parallelism = {parallelism} for the images: 
{python_version_list}:"
-    )
-    pool = mp.Pool(parallelism)
-    results = [pool.apply_async(build_ci_image, args=(verbose, dry_run, 
False), kwds=kwargs)]
-    check_async_run_results(results)
-    pool.close()
diff --git a/dev/breeze/src/airflow_breeze/build_image/prod/__init__.py 
b/dev/breeze/src/airflow_breeze/build_image/prod/__init__.py
deleted file mode 100644
index 141dd81f21..0000000000
--- a/dev/breeze/src/airflow_breeze/build_image/prod/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Building PROD Image."""
diff --git a/dev/breeze/src/airflow_breeze/build_image/prod/build_prod_image.py 
b/dev/breeze/src/airflow_breeze/build_image/prod/build_prod_image.py
deleted file mode 100644
index abdc873628..0000000000
--- a/dev/breeze/src/airflow_breeze/build_image/prod/build_prod_image.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Command to build PROD image."""
-import contextlib
-import os
-import sys
-from typing import Tuple
-
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
-from airflow_breeze.utils.console import get_console
-from airflow_breeze.utils.docker_command_utils import (
-    construct_docker_build_command,
-    construct_empty_docker_build_command,
-    tag_and_push_image,
-)
-from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT, 
DOCKER_CONTEXT_DIR
-from airflow_breeze.utils.registry import login_to_docker_registry
-from airflow_breeze.utils.run_utils import fix_group_permissions, run_command
-
-REQUIRED_PROD_IMAGE_ARGS = [
-    "python_base_image",
-    "install_mysql_client",
-    "install_mssql_client",
-    "install_postgres_client",
-    "airflow_version",
-    "airflow_branch",
-    "airflow_extras",
-    "airflow_pre_cached_pip_packages",
-    "docker_context_files",
-    "extras",
-    "additional_airflow_extras",
-    "additional_python_deps",
-    "additional_dev_apt_command",
-    "additional_dev_apt_deps",
-    "additional_dev_apt_env",
-    "additional_runtime_apt_command",
-    "additional_runtime_apt_deps",
-    "additional_runtime_apt_env",
-    "upgrade_to_newer_dependencies",
-    "constraints_github_repository",
-    "airflow_constraints",
-    "airflow_image_repository",
-    "airflow_image_date_created",
-    "build_id",
-    "airflow_image_readme_url",
-    "install_providers_from_sources",
-    "airflow_is_in_context",
-    "install_packages_from_context",
-]
-
-OPTIONAL_PROD_IMAGE_ARGS = [
-    "dev_apt_command",
-    "dev_apt_deps",
-    "runtime_apt_command",
-    "runtime_apt_deps",
-]
-
-
-def clean_docker_context_files(verbose: bool, dry_run: bool):
-    """
-    Cleans up docker context files folder - leaving only .README.md there.
-    """
-    if verbose or dry_run:
-        get_console().print("[info]Cleaning docker-context-files[/]")
-    if dry_run:
-        return
-    with contextlib.suppress(FileNotFoundError):
-        context_files_to_delete = DOCKER_CONTEXT_DIR.glob('**/*')
-        for file_to_delete in context_files_to_delete:
-            if file_to_delete.name != '.README.md':
-                file_to_delete.unlink()
-
-
-def check_docker_context_files(install_packages_from_context: bool):
-    """
-    Sanity check - if we want to install from docker-context-files we expect 
some packages there but if
-    we don't - we don't expect them, and they might invalidate Docker cache.
-
-    This method exits with an error if what we see is unexpected for given 
operation.
-
-    :param install_packages_from_context: whether we want to install from 
docker-context-files
-    """
-    context_file = DOCKER_CONTEXT_DIR.glob('**/*')
-    number_of_context_files = len(
-        [context for context in context_file if context.is_file() and 
context.name != '.README.md']
-    )
-    if number_of_context_files == 0:
-        if install_packages_from_context:
-            get_console().print('[warning]\nERROR! You want to install 
packages from docker-context-files')
-            get_console().print('[warning]\n but there are no packages to 
install in this folder.')
-            sys.exit(1)
-    else:
-        if not install_packages_from_context:
-            get_console().print(
-                '[warning]\n ERROR! There are some extra files in 
docker-context-files except README.md'
-            )
-            get_console().print('[warning]\nAnd you did not choose 
--install-packages-from-context flag')
-            get_console().print(
-                '[warning]\nThis might result in unnecessary cache 
invalidation and long build times'
-            )
-            get_console().print(
-                '[warning]\nExiting now \
-                    - please restart the command with --cleanup-context switch'
-            )
-            sys.exit(1)
-
-
-def build_production_image(
-    verbose: bool, dry_run: bool, prod_image_params: BuildProdParams
-) -> Tuple[int, str]:
-    """
-    Builds PROD image:
-
-      * fixes group permissions for files (to improve caching when umask is 
002)
-      * converts all the parameters received via kwargs into BuildProdParams 
(including cache)
-      * prints info about the image to build
-      * removes docker-context-files if requested
-      * performs sanity check if the files are present in docker-context-files 
if expected
-      * logs int to docker registry on CI if build cache is being executed
-      * removes "tag" for previously build image so that inline cache uses 
only remote image
-      * constructs docker-compose command to run based on parameters passed
-      * run the build command
-      * update cached information that the build completed and saves checksums 
of all files
-        for quick future check if the build is needed
-
-    :param verbose: print commands when running
-    :param dry_run: do not execute "write" commands - just print what would 
happen
-    :param prod_image_params: PROD image parameters
-    """
-    fix_group_permissions(verbose=verbose)
-    if verbose or dry_run:
-        get_console().print(
-            f"\n[info]Building PROD image of airflow from 
{AIRFLOW_SOURCES_ROOT} "
-            f"python version: {prod_image_params.python}[/]\n"
-        )
-    if prod_image_params.cleanup_context:
-        clean_docker_context_files(verbose=verbose, dry_run=dry_run)
-    check_docker_context_files(prod_image_params.install_packages_from_context)
-    if prod_image_params.prepare_buildx_cache:
-        login_to_docker_registry(prod_image_params, dry_run=dry_run)
-    run_command(
-        ["docker", "rmi", "--no-prune", "--force", 
prod_image_params.airflow_image_name],
-        verbose=verbose,
-        dry_run=dry_run,
-        cwd=AIRFLOW_SOURCES_ROOT,
-        text=True,
-        check=False,
-    )
-    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 = 
construct_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,
-        )
-    else:
-        cmd = construct_docker_build_command(
-            image_params=prod_image_params,
-            verbose=verbose,
-            required_args=REQUIRED_PROD_IMAGE_ARGS,
-            optional_args=OPTIONAL_PROD_IMAGE_ARGS,
-            production_image=True,
-        )
-        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.push_image:
-            return tag_and_push_image(image_params=prod_image_params, 
dry_run=dry_run, verbose=verbose)
-    return build_command_result.returncode, f"Image build: 
{prod_image_params.python}"
diff --git a/dev/breeze/src/airflow_breeze/commands/ci_image_tools.py 
b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py
similarity index 52%
rename from dev/breeze/src/airflow_breeze/commands/ci_image_tools.py
rename to dev/breeze/src/airflow_breeze/commands/ci_image_commands.py
index f6f01d3dba..bfdc695dc9 100644
--- a/dev/breeze/src/airflow_breeze/commands/ci_image_tools.py
+++ b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py
@@ -14,15 +14,18 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
+import multiprocessing as mp
+import os
 import sys
-from typing import Optional, Tuple
+from pathlib import Path
+from typing import List, Optional, Tuple, Union
 
 import click
 
-from airflow_breeze.build_image.ci.build_ci_image import build_ci_image
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.params.shell_params import ShellParams
+from airflow_breeze.utils.common_options import (
     option_additional_dev_apt_command,
     option_additional_dev_apt_deps,
     option_additional_dev_apt_env,
@@ -62,12 +65,31 @@ from airflow_breeze.commands.common_options import (
     option_verify_image,
     option_wait_for_image,
 )
-from airflow_breeze.commands.main import main
+from airflow_breeze.utils.confirm import STANDARD_TIMEOUT, Answer, user_confirm
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.docker_command_utils import (
+    check_docker_compose_version,
+    check_docker_resources,
+    check_docker_version,
+    construct_docker_build_command,
+    construct_empty_docker_build_command,
+    tag_and_push_image,
+)
+from airflow_breeze.utils.mark_image_as_refreshed import 
mark_image_as_refreshed
+from airflow_breeze.utils.md5_build_check import 
md5sum_check_if_build_is_needed
+from airflow_breeze.utils.parallel import check_async_run_results
+from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT, 
BUILD_CACHE_DIR
 from airflow_breeze.utils.pulll_image import run_pull_image, 
run_pull_in_parallel
 from airflow_breeze.utils.python_versions import get_python_version_list
+from airflow_breeze.utils.registry import login_to_docker_registry
 from airflow_breeze.utils.run_tests import verify_an_image
-from airflow_breeze.utils.run_utils import filter_out_none
+from airflow_breeze.utils.run_utils import (
+    filter_out_none,
+    fix_group_permissions,
+    instruct_build_image,
+    is_repo_rebased,
+    run_command,
+)
 
 CI_IMAGE_TOOLS_COMMANDS = {
     "name": "CI Image tools",
@@ -321,3 +343,182 @@ def verify_image(
         extra_pytest_args=extra_pytest_args,
     )
     sys.exit(return_code)
+
+
+def should_we_run_the_build(build_ci_params: BuildCiParams, verbose: bool) -> 
bool:
+    """
+    Check if we should run the build based on what files have been modified 
since last build and answer from
+    the user.
+
+    * If build is needed, the user is asked for confirmation
+    * If the branch is not rebased it warns the user to rebase (to make sure 
latest remote cache is useful)
+    * Builds Image/Skips/Quits depending on the answer
+
+    :param build_ci_params: parameters for the build
+    :param verbose: should we get verbose information
+    """
+    # We import those locally so that click autocomplete works
+    from inputimeout import TimeoutOccurred
+
+    if not md5sum_check_if_build_is_needed(
+        md5sum_cache_dir=build_ci_params.md5sum_cache_dir, verbose=verbose
+    ):
+        return False
+    try:
+        answer = user_confirm(
+            message="Do you want to build the image?", 
timeout=STANDARD_TIMEOUT, default_answer=Answer.NO
+        )
+        if answer == answer.YES:
+            if is_repo_rebased(build_ci_params.github_repository, 
build_ci_params.airflow_branch):
+                return True
+            else:
+                get_console().print(
+                    "\n[warning]This might take a lot of time, we think you 
should rebase first.[/]\n"
+                )
+                answer = user_confirm(
+                    "But if you really, really want - you can do it. Are you 
really sure?",
+                    timeout=STANDARD_TIMEOUT,
+                    default_answer=Answer.NO,
+                )
+                if answer == Answer.YES:
+                    return True
+                else:
+                    get_console().print(
+                        "[info]Please rebase your code before continuing.[/]\n"
+                        "Check this link to know more "
+                        
"https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst#id15\n";
+                    )
+                    get_console().print('[error]Exiting the process[/]\n')
+                    sys.exit(1)
+        elif answer == Answer.NO:
+            instruct_build_image(build_ci_params.python)
+            return False
+        else:  # users_status == Answer.QUIT:
+            get_console().print('\n[warning]Quitting the process[/]\n')
+            sys.exit()
+    except TimeoutOccurred:
+        get_console().print('\nTimeout. Considering your response as No\n')
+        instruct_build_image(build_ci_params.python)
+        return False
+    except Exception as e:
+        get_console().print(f'\nTerminating the process on {e}')
+        sys.exit(1)
+
+
+def build_ci_image(verbose: bool, dry_run: bool, ci_image_params: 
BuildCiParams) -> Tuple[int, str]:
+    """
+    Builds CI image:
+
+      * fixes group permissions for files (to improve caching when umask is 
002)
+      * converts all the parameters received via kwargs into BuildCIParams 
(including cache)
+      * prints info about the image to build
+      * logs int to docker registry on CI if build cache is being executed
+      * removes "tag" for previously build image so that inline cache uses 
only remote image
+      * constructs docker-compose command to run based on parameters passed
+      * run the build command
+      * update cached information that the build completed and saves checksums 
of all files
+        for quick future check if the build is needed
+
+    :param verbose: print commands when running
+    :param dry_run: do not execute "write" commands - just print what would 
happen
+    :param ci_image_params: CI image parameters
+    """
+
+    fix_group_permissions(verbose=verbose)
+    if verbose or dry_run:
+        get_console().print(
+            f"\n[info]Building CI image of airflow from {AIRFLOW_SOURCES_ROOT} 
"
+            f"python version: {ci_image_params.python}[/]\n"
+        )
+    if not ci_image_params.force_build and not 
ci_image_params.upgrade_to_newer_dependencies:
+        if not should_we_run_the_build(build_ci_params=ci_image_params, 
verbose=verbose):
+            return 0, f"Image build: {ci_image_params.python}"
+    run_command(
+        ["docker", "rmi", "--no-prune", "--force", 
ci_image_params.airflow_image_name],
+        verbose=verbose,
+        dry_run=dry_run,
+        cwd=AIRFLOW_SOURCES_ROOT,
+        text=True,
+        check=False,
+    )
+    if ci_image_params.prepare_buildx_cache:
+        login_to_docker_registry(ci_image_params, dry_run=dry_run)
+    cmd = construct_docker_build_command(
+        image_params=ci_image_params,
+        verbose=verbose,
+        production_image=False,
+    )
+    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 = 
construct_empty_docker_build_command(image_params=ci_image_params)
+        build_result = run_command(
+            cmd,
+            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_result = run_command(
+            cmd, verbose=verbose, dry_run=dry_run, cwd=AIRFLOW_SOURCES_ROOT, 
text=True, check=False
+        )
+    if not dry_run:
+        if build_result.returncode == 0:
+            mark_image_as_refreshed(ci_image_params)
+        else:
+            get_console().print("[error]Error when building image![/]")
+            return (
+                build_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.push_image:
+        return tag_and_push_image(image_params=ci_image_params, 
dry_run=dry_run, verbose=verbose)
+    return build_result.returncode, f"Image build: {ci_image_params.python}"
+
+
+def build_ci_image_in_parallel(
+    verbose: bool, dry_run: bool, parallelism: int, python_version_list: 
List[str], **kwargs
+):
+    """Run CI image builds in parallel."""
+    get_console().print(
+        f"\n[info]Running with parallelism = {parallelism} for the images: 
{python_version_list}:"
+    )
+    pool = mp.Pool(parallelism)
+    results = [pool.apply_async(build_ci_image, args=(verbose, dry_run, 
False), kwds=kwargs)]
+    check_async_run_results(results)
+    pool.close()
+
+
+def rebuild_ci_image_if_needed(
+    build_params: Union[ShellParams, BuildCiParams], dry_run: bool, verbose: 
bool
+) -> None:
+    """
+    Rebuilds CI image if needed and user confirms it.
+
+    :param build_params: parameters of the shell
+    :param dry_run: whether it's a dry_run
+    :param verbose: should we print verbose messages
+    """
+    check_docker_version(verbose=verbose)
+    check_docker_compose_version(verbose=verbose)
+    check_docker_resources(build_params.airflow_image_name, verbose=verbose, 
dry_run=dry_run)
+    build_ci_image_check_cache = Path(
+        BUILD_CACHE_DIR, build_params.airflow_branch, 
f".built_{build_params.python}"
+    )
+    ci_image_params = BuildCiParams(python=build_params.python, 
upgrade_to_newer_dependencies=False)
+    if build_ci_image_check_cache.exists():
+        if verbose:
+            get_console().print(f'[info]{build_params.the_image_type} image 
already built locally.[/]')
+    else:
+        get_console().print(
+            f'[warning]{build_params.the_image_type} image not built locally. 
Forcing build.[/]'
+        )
+        ci_image_params.force_build = True
+    build_ci_image(verbose, dry_run=dry_run, ci_image_params=ci_image_params)
diff --git 
a/dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance.py 
b/dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance_commands.py
similarity index 99%
rename from 
dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance.py
rename to 
dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance_commands.py
index 39b150b8f4..4c32f6b3b9 100644
--- a/dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance.py
+++ 
b/dev/breeze/src/airflow_breeze/commands/configuration_and_maintenance_commands.py
@@ -25,7 +25,11 @@ from typing import Optional
 import click
 
 from airflow_breeze import NAME, VERSION
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.global_constants import 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION, MOUNT_ALL
+from airflow_breeze.params.shell_params import ShellParams
+from airflow_breeze.utils.cache import check_if_cache_exists, delete_cache, 
touch_cache_file
+from airflow_breeze.utils.common_options import (
     option_answer,
     option_backend,
     option_dry_run,
@@ -36,10 +40,6 @@ from airflow_breeze.commands.common_options import (
     option_python,
     option_verbose,
 )
-from airflow_breeze.commands.main import main
-from airflow_breeze.global_constants import 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION, MOUNT_ALL
-from airflow_breeze.shell.shell_params import ShellParams
-from airflow_breeze.utils.cache import check_if_cache_exists, delete_cache, 
touch_cache_file
 from airflow_breeze.utils.confirm import STANDARD_TIMEOUT, Answer, user_confirm
 from airflow_breeze.utils.console import get_console
 from airflow_breeze.utils.docker_command_utils import (
diff --git a/dev/breeze/src/airflow_breeze/commands/developer_commands.py 
b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
index 8404337f62..2e36a422ec 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
@@ -16,14 +16,25 @@
 # under the License.
 
 import os
+import subprocess
 import sys
-from dataclasses import dataclass
-from typing import List, Optional, Tuple
+from typing import Optional, Tuple, Union
 
 import rich_click as click
 
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.ci_image_commands import 
rebuild_ci_image_if_needed
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.global_constants import (
+    DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
+    MOUNT_SELECTED,
+    get_available_packages,
+)
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.params.doc_build_params import DocBuildParams
+from airflow_breeze.params.shell_params import ShellParams
+from airflow_breeze.pre_commit_ids import PRE_COMMIT_LIST
+from airflow_breeze.utils.cache import read_from_cache_file
+from airflow_breeze.utils.common_options import (
     option_airflow_constraints_reference,
     option_airflow_extras,
     option_answer,
@@ -47,24 +58,19 @@ from airflow_breeze.commands.common_options import (
     option_use_packages_from_dist,
     option_verbose,
 )
-from airflow_breeze.commands.custom_param_types import BetterChoice
-from airflow_breeze.commands.main import main
-from airflow_breeze.global_constants import (
-    DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
-    MOUNT_SELECTED,
-    get_available_packages,
-)
-from airflow_breeze.pre_commit_ids import PRE_COMMIT_LIST
-from airflow_breeze.shell.enter_shell import enter_shell, 
find_airflow_container
-from airflow_breeze.shell.shell_params import ShellParams
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.custom_param_types import BetterChoice
 from airflow_breeze.utils.docker_command_utils import (
+    check_docker_compose_version,
+    check_docker_is_running,
+    check_docker_resources,
+    check_docker_version,
     get_env_variables_for_docker_commands,
     get_extra_docker_flags,
 )
 from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT
-from airflow_breeze.utils.rebuild_image_if_needed import 
rebuild_ci_image_if_needed
-from airflow_breeze.utils.run_utils import assert_pre_commit_installed, 
run_command
+from airflow_breeze.utils.run_utils import assert_pre_commit_installed, 
filter_out_none, run_command
+from airflow_breeze.utils.visuals import ASCIIART, ASCIIART_STYLE, CHEATSHEET, 
CHEATSHEET_STYLE
 
 DEVELOPER_COMMANDS = {
     "name": "Developer tools",
@@ -207,6 +213,8 @@ DEVELOPER_PARAMETERS = {
 # Is used for a shorthand of shell and except the extra
 # Args it should have the same parameters.
 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
 @main.command()
 @option_verbose
 @option_dry_run
@@ -516,30 +524,6 @@ def stop(verbose: bool, dry_run: bool, preserve_volumes: 
bool):
     run_command(command_to_execute, verbose=verbose, dry_run=dry_run, 
env=env_variables)
 
 
-@dataclass
-class DocBuildParams:
-    package_filter: Tuple[str]
-    docs_only: bool
-    spellcheck_only: bool
-    for_production: bool
-    skip_environment_initialization: bool = False
-    github_actions = os.environ.get('GITHUB_ACTIONS', "false")
-
-    @property
-    def args_doc_builder(self) -> List[str]:
-        doc_args = []
-        if self.docs_only:
-            doc_args.append("--docs-only")
-        if self.spellcheck_only:
-            doc_args.append("--spellcheck-only")
-        if self.for_production:
-            doc_args.append("--for-production")
-        if self.package_filter and len(self.package_filter) > 0:
-            for single_filter in self.package_filter:
-                doc_args.extend(["--package-filter", single_filter])
-        return doc_args
-
-
 @main.command(name='exec', help='Joins the interactive shell of running 
airflow container')
 @option_verbose
 @option_dry_run
@@ -567,3 +551,101 @@ def exec(verbose: bool, dry_run: bool, exec_args: Tuple):
         if not process:
             sys.exit(1)
         sys.exit(process.returncode)
+
+
+def enter_shell(**kwargs) -> Union[subprocess.CompletedProcess, 
subprocess.CalledProcessError]:
+    """
+    Executes entering shell using the parameters passed as kwargs:
+
+    * checks if docker version is good
+    * checks if docker-compose version is good
+    * updates kwargs with cached parameters
+    * displays ASCIIART and CHEATSHEET unless disabled
+    * build ShellParams from the updated kwargs
+    * executes the command to drop the user to Breeze shell
+
+    """
+    verbose = kwargs['verbose']
+    dry_run = kwargs['dry_run']
+    check_docker_is_running(verbose)
+    check_docker_version(verbose)
+    check_docker_compose_version(verbose)
+    if read_from_cache_file('suppress_asciiart') is None:
+        get_console().print(ASCIIART, style=ASCIIART_STYLE)
+    if read_from_cache_file('suppress_cheatsheet') is None:
+        get_console().print(CHEATSHEET, style=CHEATSHEET_STYLE)
+    enter_shell_params = ShellParams(**filter_out_none(**kwargs))
+    return run_shell_with_build_image_checks(verbose, dry_run, 
enter_shell_params)
+
+
+def run_shell_with_build_image_checks(
+    verbose: bool, dry_run: bool, shell_params: ShellParams
+) -> Union[subprocess.CompletedProcess, subprocess.CalledProcessError]:
+    """
+    Executes a shell command built from params passed, checking if build is 
not needed.
+    * checks if there are enough resources to run shell
+    * checks if image was built at least once (if not - forces the build)
+    * if not forces, checks if build is needed and asks the user if so
+    * builds the image if needed
+    * prints information about the build
+    * constructs docker compose command to enter shell
+    * executes it
+
+    :param verbose: print commands when running
+    :param dry_run: do not execute "write" commands - just print what would 
happen
+    :param shell_params: parameters of the execution
+    """
+    rebuild_ci_image_if_needed(build_params=shell_params, dry_run=dry_run, 
verbose=verbose)
+    shell_params.print_badge_info()
+    cmd = ['docker-compose', 'run', '--service-ports', "-e", "BREEZE", '--rm', 
'airflow']
+    cmd_added = shell_params.command_passed
+    env_variables = get_env_variables_for_docker_commands(shell_params)
+    if cmd_added is not None:
+        cmd.extend(['-c', cmd_added])
+
+    command_result = run_command(
+        cmd, verbose=verbose, dry_run=dry_run, env=env_variables, text=True, 
check=False
+    )
+    if command_result.returncode == 0:
+        return command_result
+    else:
+        get_console().print(f"[red]Error {command_result.returncode} 
returned[/]")
+        if verbose:
+            get_console().print(command_result.stderr)
+        return command_result
+
+
+def stop_exec_on_error(returncode: int):
+    get_console().print('\n[error]ERROR in finding the airflow docker-compose 
process id[/]\n')
+    sys.exit(returncode)
+
+
+def find_airflow_container(verbose, dry_run) -> Optional[str]:
+    exec_shell_params = ShellParams(verbose=verbose, dry_run=dry_run)
+    check_docker_resources(exec_shell_params.airflow_image_name, 
verbose=verbose, dry_run=dry_run)
+    exec_shell_params.print_badge_info()
+    env_variables = get_env_variables_for_docker_commands(exec_shell_params)
+    cmd = ['docker-compose', 'ps', '--all', '--filter', 'status=running', 
'airflow']
+    docker_compose_ps_command = run_command(
+        cmd, verbose=verbose, dry_run=dry_run, text=True, capture_output=True, 
env=env_variables, check=False
+    )
+    if dry_run:
+        return "CONTAINER_ID"
+    if docker_compose_ps_command.returncode != 0:
+        if verbose:
+            get_console().print(docker_compose_ps_command.stdout)
+            get_console().print(docker_compose_ps_command.stderr)
+        stop_exec_on_error(docker_compose_ps_command.returncode)
+        return None
+
+    output = docker_compose_ps_command.stdout
+    container_info = output.strip().split('\n')
+    if container_info:
+        container_running = container_info[-1].split(' ')[0]
+        if container_running.startswith('-'):
+            # On docker-compose v1 we get '--------' as output here
+            stop_exec_on_error(docker_compose_ps_command.returncode)
+        return container_running
+    else:
+        stop_exec_on_error(1)
+        return None
diff --git a/dev/breeze/src/airflow_breeze/commands/main.py 
b/dev/breeze/src/airflow_breeze/commands/main_command.py
similarity index 94%
rename from dev/breeze/src/airflow_breeze/commands/main.py
rename to dev/breeze/src/airflow_breeze/commands/main_command.py
index 10a12359be..4d805e7b7a 100644
--- a/dev/breeze/src/airflow_breeze/commands/main.py
+++ b/dev/breeze/src/airflow_breeze/commands/main_command.py
@@ -15,7 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.configure_rich_click import click
+from airflow_breeze.utils.common_options import (
     option_airflow_extras,
     option_answer,
     option_backend,
@@ -36,7 +37,6 @@ from airflow_breeze.commands.common_options import (
     option_use_packages_from_dist,
     option_verbose,
 )
-from airflow_breeze.commands.configure_rich_click import click
 from airflow_breeze.utils.path_utils import create_directories
 
 
diff --git a/dev/breeze/src/airflow_breeze/commands/production_image_tools.py 
b/dev/breeze/src/airflow_breeze/commands/production_image_commands.py
similarity index 66%
rename from dev/breeze/src/airflow_breeze/commands/production_image_tools.py
rename to dev/breeze/src/airflow_breeze/commands/production_image_commands.py
index ac5bc7fe94..56a4a07dce 100644
--- a/dev/breeze/src/airflow_breeze/commands/production_image_tools.py
+++ b/dev/breeze/src/airflow_breeze/commands/production_image_commands.py
@@ -14,15 +14,17 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
+import contextlib
+import os
 import sys
 from typing import Optional, Tuple
 
 import click
 
-from airflow_breeze.build_image.prod.build_prod_image import 
build_production_image
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.global_constants import ALLOWED_INSTALLATION_METHODS, 
DEFAULT_EXTRAS
+from airflow_breeze.params.build_prod_params import BuildProdParams
+from airflow_breeze.utils.common_options import (
     option_additional_dev_apt_command,
     option_additional_dev_apt_deps,
     option_additional_dev_apt_env,
@@ -61,14 +63,19 @@ from airflow_breeze.commands.common_options import (
     option_verify_image,
     option_wait_for_image,
 )
-from airflow_breeze.commands.custom_param_types import BetterChoice
-from airflow_breeze.commands.main import main
-from airflow_breeze.global_constants import ALLOWED_INSTALLATION_METHODS, 
DEFAULT_EXTRAS
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.custom_param_types import BetterChoice
+from airflow_breeze.utils.docker_command_utils import (
+    construct_docker_build_command,
+    construct_empty_docker_build_command,
+    tag_and_push_image,
+)
+from airflow_breeze.utils.path_utils import AIRFLOW_SOURCES_ROOT, 
DOCKER_CONTEXT_DIR
 from airflow_breeze.utils.pulll_image import run_pull_image, 
run_pull_in_parallel
 from airflow_breeze.utils.python_versions import get_python_version_list
+from airflow_breeze.utils.registry import login_to_docker_registry
 from airflow_breeze.utils.run_tests import verify_an_image
-from airflow_breeze.utils.run_utils import filter_out_none
+from airflow_breeze.utils.run_utils import filter_out_none, 
fix_group_permissions, run_command
 
 PRODUCTION_IMAGE_TOOLS_COMMANDS = {
     "name": "Production Image tools",
@@ -379,3 +386,124 @@ def verify_prod_image(
         extra_pytest_args=extra_pytest_args,
     )
     sys.exit(return_code)
+
+
+def clean_docker_context_files(verbose: bool, dry_run: bool):
+    """
+    Cleans up docker context files folder - leaving only .README.md there.
+    """
+    if verbose or dry_run:
+        get_console().print("[info]Cleaning docker-context-files[/]")
+    if dry_run:
+        return
+    with contextlib.suppress(FileNotFoundError):
+        context_files_to_delete = DOCKER_CONTEXT_DIR.glob('**/*')
+        for file_to_delete in context_files_to_delete:
+            if file_to_delete.name != '.README.md':
+                file_to_delete.unlink()
+
+
+def check_docker_context_files(install_packages_from_context: bool):
+    """
+    Sanity check - if we want to install from docker-context-files we expect 
some packages there but if
+    we don't - we don't expect them, and they might invalidate Docker cache.
+
+    This method exits with an error if what we see is unexpected for given 
operation.
+
+    :param install_packages_from_context: whether we want to install from 
docker-context-files
+    """
+    context_file = DOCKER_CONTEXT_DIR.glob('**/*')
+    number_of_context_files = len(
+        [context for context in context_file if context.is_file() and 
context.name != '.README.md']
+    )
+    if number_of_context_files == 0:
+        if install_packages_from_context:
+            get_console().print('[warning]\nERROR! You want to install 
packages from docker-context-files')
+            get_console().print('[warning]\n but there are no packages to 
install in this folder.')
+            sys.exit(1)
+    else:
+        if not install_packages_from_context:
+            get_console().print(
+                '[warning]\n ERROR! There are some extra files in 
docker-context-files except README.md'
+            )
+            get_console().print('[warning]\nAnd you did not choose 
--install-packages-from-context flag')
+            get_console().print(
+                '[warning]\nThis might result in unnecessary cache 
invalidation and long build times'
+            )
+            get_console().print(
+                '[warning]\nExiting now \
+                    - please restart the command with --cleanup-context switch'
+            )
+            sys.exit(1)
+
+
+def build_production_image(
+    verbose: bool, dry_run: bool, prod_image_params: BuildProdParams
+) -> Tuple[int, str]:
+    """
+    Builds PROD image:
+
+      * fixes group permissions for files (to improve caching when umask is 
002)
+      * converts all the parameters received via kwargs into BuildProdParams 
(including cache)
+      * prints info about the image to build
+      * removes docker-context-files if requested
+      * performs sanity check if the files are present in docker-context-files 
if expected
+      * logs int to docker registry on CI if build cache is being executed
+      * removes "tag" for previously build image so that inline cache uses 
only remote image
+      * constructs docker-compose command to run based on parameters passed
+      * run the build command
+      * update cached information that the build completed and saves checksums 
of all files
+        for quick future check if the build is needed
+
+    :param verbose: print commands when running
+    :param dry_run: do not execute "write" commands - just print what would 
happen
+    :param prod_image_params: PROD image parameters
+    """
+    fix_group_permissions(verbose=verbose)
+    if verbose or dry_run:
+        get_console().print(
+            f"\n[info]Building PROD image of airflow from 
{AIRFLOW_SOURCES_ROOT} "
+            f"python version: {prod_image_params.python}[/]\n"
+        )
+    if prod_image_params.cleanup_context:
+        clean_docker_context_files(verbose=verbose, dry_run=dry_run)
+    check_docker_context_files(prod_image_params.install_packages_from_context)
+    if prod_image_params.prepare_buildx_cache:
+        login_to_docker_registry(prod_image_params, dry_run=dry_run)
+    run_command(
+        ["docker", "rmi", "--no-prune", "--force", 
prod_image_params.airflow_image_name],
+        verbose=verbose,
+        dry_run=dry_run,
+        cwd=AIRFLOW_SOURCES_ROOT,
+        text=True,
+        check=False,
+    )
+    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 = 
construct_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,
+        )
+    else:
+        cmd = construct_docker_build_command(
+            image_params=prod_image_params,
+            verbose=verbose,
+            production_image=True,
+        )
+        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.push_image:
+            return tag_and_push_image(image_params=prod_image_params, 
dry_run=dry_run, verbose=verbose)
+    return build_command_result.returncode, f"Image build: 
{prod_image_params.python}"
diff --git a/dev/breeze/src/airflow_breeze/commands/release_management.py 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
similarity index 98%
rename from dev/breeze/src/airflow_breeze/commands/release_management.py
rename to dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index 842759693f..1b4602c450 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -22,7 +22,16 @@ from typing import IO, List, Optional, Tuple, Union
 
 import click
 
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.global_constants import (
+    ALLOWED_GENERATE_CONSTRAINTS_MODES,
+    DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
+    MOUNT_ALL,
+    MOUNT_NONE,
+    MOUNT_SELECTED,
+)
+from airflow_breeze.params.shell_params import ShellParams
+from airflow_breeze.utils.common_options import (
     argument_packages,
     option_airflow_constraints_reference,
     option_airflow_extras,
@@ -44,18 +53,9 @@ from airflow_breeze.commands.common_options import (
     option_verbose,
     option_version_suffix_for_pypi,
 )
-from airflow_breeze.commands.custom_param_types import BetterChoice
-from airflow_breeze.commands.main import main
-from airflow_breeze.global_constants import (
-    ALLOWED_GENERATE_CONSTRAINTS_MODES,
-    DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
-    MOUNT_ALL,
-    MOUNT_NONE,
-    MOUNT_SELECTED,
-)
-from airflow_breeze.shell.shell_params import ShellParams
 from airflow_breeze.utils.confirm import Answer, user_confirm
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.custom_param_types import BetterChoice
 from airflow_breeze.utils.docker_command_utils import (
     get_env_variables_for_docker_commands,
     get_extra_docker_flags,
diff --git a/dev/breeze/src/airflow_breeze/commands/testing.py 
b/dev/breeze/src/airflow_breeze/commands/testing_commands.py
similarity index 91%
rename from dev/breeze/src/airflow_breeze/commands/testing.py
rename to dev/breeze/src/airflow_breeze/commands/testing_commands.py
index d51093ef72..9115ee8896 100644
--- a/dev/breeze/src/airflow_breeze/commands/testing.py
+++ b/dev/breeze/src/airflow_breeze/commands/testing_commands.py
@@ -21,8 +21,12 @@ from typing import Tuple
 
 import click
 
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
-from airflow_breeze.commands.common_options import (
+from airflow_breeze.commands.developer_commands import 
check_docker_is_running, check_docker_resources
+from airflow_breeze.commands.main_command import main
+from airflow_breeze.global_constants import ALLOWED_TEST_TYPES
+from airflow_breeze.params.build_prod_params import BuildProdParams
+from airflow_breeze.params.shell_params import ShellParams
+from airflow_breeze.utils.common_options import (
     option_db_reset,
     option_dry_run,
     option_github_repository,
@@ -32,12 +36,8 @@ from airflow_breeze.commands.common_options import (
     option_python,
     option_verbose,
 )
-from airflow_breeze.commands.custom_param_types import BetterChoice
-from airflow_breeze.commands.main import main
-from airflow_breeze.global_constants import ALLOWED_TEST_TYPES
-from airflow_breeze.shell.enter_shell import check_docker_is_running, 
check_docker_resources
-from airflow_breeze.shell.shell_params import ShellParams
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.custom_param_types import BetterChoice
 from airflow_breeze.utils.docker_command_utils import 
get_env_variables_for_docker_commands
 from airflow_breeze.utils.run_tests import run_docker_compose_tests
 from airflow_breeze.utils.run_utils import run_command
diff --git a/dev/breeze/src/airflow_breeze/commands/configure_rich_click.py 
b/dev/breeze/src/airflow_breeze/configure_rich_click.py
similarity index 85%
rename from dev/breeze/src/airflow_breeze/commands/configure_rich_click.py
rename to dev/breeze/src/airflow_breeze/configure_rich_click.py
index 1807ad3fb0..f9010dd28f 100644
--- a/dev/breeze/src/airflow_breeze/commands/configure_rich_click.py
+++ b/dev/breeze/src/airflow_breeze/configure_rich_click.py
@@ -22,21 +22,21 @@ try:
     # We handle ImportError so that click autocomplete works
     import rich_click as click
 
-    from airflow_breeze.commands.ci_image_tools import 
CI_IMAGE_TOOLS_COMMANDS, CI_IMAGE_TOOLS_PARAMETERS
-    from airflow_breeze.commands.configuration_and_maintenance import (
+    from airflow_breeze.commands.ci_image_commands import 
CI_IMAGE_TOOLS_COMMANDS, CI_IMAGE_TOOLS_PARAMETERS
+    from airflow_breeze.commands.configuration_and_maintenance_commands import 
(
         CONFIGURATION_AND_MAINTENANCE_COMMANDS,
         CONFIGURATION_AND_MAINTENANCE_PARAMETERS,
     )
     from airflow_breeze.commands.developer_commands import DEVELOPER_COMMANDS, 
DEVELOPER_PARAMETERS
-    from airflow_breeze.commands.production_image_tools import (
+    from airflow_breeze.commands.production_image_commands import (
         PRODUCTION_IMAGE_TOOLS_COMMANDS,
         PRODUCTION_IMAGE_TOOLS_PARAMETERS,
     )
-    from airflow_breeze.commands.release_management import (
+    from airflow_breeze.commands.release_management_commands import (
         RELEASE_MANAGEMENT_COMMANDS,
         RELEASE_MANAGEMENT_PARAMETERS,
     )
-    from airflow_breeze.commands.testing import TESTING_COMMANDS, 
TESTING_PARAMETERS
+    from airflow_breeze.commands.testing_commands import TESTING_COMMANDS, 
TESTING_PARAMETERS
 
     click.rich_click.SHOW_METAVARS_COLUMN = False
     click.rich_click.SHOW_ARGUMENTS = False
@@ -64,7 +64,5 @@ try:
             RELEASE_MANAGEMENT_COMMANDS,
         ]
     }
-
-
 except ImportError:
     import click  # type: ignore[no-redef]
diff --git a/dev/breeze/src/airflow_breeze/build_image/ci/build_ci_params.py 
b/dev/breeze/src/airflow_breeze/params/build_ci_params.py
similarity index 86%
rename from dev/breeze/src/airflow_breeze/build_image/ci/build_ci_params.py
rename to dev/breeze/src/airflow_breeze/params/build_ci_params.py
index b744ce288d..ce244162fc 100644
--- a/dev/breeze/src/airflow_breeze/build_image/ci/build_ci_params.py
+++ b/dev/breeze/src/airflow_breeze/params/build_ci_params.py
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""Parameters for Build CI Image."""
+
 import os
 from dataclasses import dataclass
 from datetime import datetime
@@ -148,32 +148,31 @@ class BuildCiParams:
     def md5sum_cache_dir(self) -> Path:
         return Path(BUILD_CACHE_DIR, self.airflow_branch, self.python, "CI")
 
-
-REQUIRED_CI_IMAGE_ARGS = [
-    "python_base_image",
-    "airflow_version",
-    "airflow_branch",
-    "airflow_extras",
-    "airflow_pre_cached_pip_packages",
-    "additional_airflow_extras",
-    "additional_python_deps",
-    "additional_dev_apt_command",
-    "additional_dev_apt_deps",
-    "additional_dev_apt_env",
-    "additional_runtime_apt_command",
-    "additional_runtime_apt_deps",
-    "additional_runtime_apt_env",
-    "upgrade_to_newer_dependencies",
-    "constraints_github_repository",
-    "airflow_constraints_reference",
-    "airflow_constraints",
-    "airflow_image_repository",
-    "airflow_image_date_created",
-    "build_id",
-]
-OPTIONAL_CI_IMAGE_ARGS = [
-    "dev_apt_command",
-    "dev_apt_deps",
-    "runtime_apt_command",
-    "runtime_apt_deps",
-]
+    required_image_args = [
+        "python_base_image",
+        "airflow_version",
+        "airflow_branch",
+        "airflow_extras",
+        "airflow_pre_cached_pip_packages",
+        "additional_airflow_extras",
+        "additional_python_deps",
+        "additional_dev_apt_command",
+        "additional_dev_apt_deps",
+        "additional_dev_apt_env",
+        "additional_runtime_apt_command",
+        "additional_runtime_apt_deps",
+        "additional_runtime_apt_env",
+        "upgrade_to_newer_dependencies",
+        "constraints_github_repository",
+        "airflow_constraints_reference",
+        "airflow_constraints",
+        "airflow_image_repository",
+        "airflow_image_date_created",
+        "build_id",
+    ]
+    optional_image_args = [
+        "dev_apt_command",
+        "dev_apt_deps",
+        "runtime_apt_command",
+        "runtime_apt_deps",
+    ]
diff --git 
a/dev/breeze/src/airflow_breeze/build_image/prod/build_prod_params.py 
b/dev/breeze/src/airflow_breeze/params/build_prod_params.py
similarity index 91%
rename from dev/breeze/src/airflow_breeze/build_image/prod/build_prod_params.py
rename to dev/breeze/src/airflow_breeze/params/build_prod_params.py
index 8372af7233..9fe08e35fa 100644
--- a/dev/breeze/src/airflow_breeze/build_image/prod/build_prod_params.py
+++ b/dev/breeze/src/airflow_breeze/params/build_prod_params.py
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""Parameters to build PROD image."""
+
 import json
 import os
 import re
@@ -311,3 +311,40 @@ class BuildProdParams:
         """Construct PROD image link"""
         image = 
f'{self.airflow_base_image_name}/{self.airflow_branch}/prod/python{self.python}'
         return image if self.image_tag is None else image + 
f":{self.image_tag}"
+
+    required_image_args = [
+        "python_base_image",
+        "install_mysql_client",
+        "install_mssql_client",
+        "install_postgres_client",
+        "airflow_version",
+        "airflow_branch",
+        "airflow_extras",
+        "airflow_pre_cached_pip_packages",
+        "docker_context_files",
+        "extras",
+        "additional_airflow_extras",
+        "additional_python_deps",
+        "additional_dev_apt_command",
+        "additional_dev_apt_deps",
+        "additional_dev_apt_env",
+        "additional_runtime_apt_command",
+        "additional_runtime_apt_deps",
+        "additional_runtime_apt_env",
+        "upgrade_to_newer_dependencies",
+        "constraints_github_repository",
+        "airflow_constraints",
+        "airflow_image_repository",
+        "airflow_image_date_created",
+        "build_id",
+        "airflow_image_readme_url",
+        "install_providers_from_sources",
+        "airflow_is_in_context",
+        "install_packages_from_context",
+    ]
+    optional_image_args = [
+        "dev_apt_command",
+        "dev_apt_deps",
+        "runtime_apt_command",
+        "runtime_apt_deps",
+    ]
diff --git a/dev/breeze/src/airflow_breeze/params/doc_build_params.py 
b/dev/breeze/src/airflow_breeze/params/doc_build_params.py
new file mode 100644
index 0000000000..25350db93b
--- /dev/null
+++ b/dev/breeze/src/airflow_breeze/params/doc_build_params.py
@@ -0,0 +1,44 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import os
+from dataclasses import dataclass
+from typing import List, Tuple
+
+
+@dataclass
+class DocBuildParams:
+    package_filter: Tuple[str]
+    docs_only: bool
+    spellcheck_only: bool
+    for_production: bool
+    skip_environment_initialization: bool = False
+    github_actions = os.environ.get('GITHUB_ACTIONS', "false")
+
+    @property
+    def args_doc_builder(self) -> List[str]:
+        doc_args = []
+        if self.docs_only:
+            doc_args.append("--docs-only")
+        if self.spellcheck_only:
+            doc_args.append("--spellcheck-only")
+        if self.for_production:
+            doc_args.append("--for-production")
+        if self.package_filter and len(self.package_filter) > 0:
+            for single_filter in self.package_filter:
+                doc_args.extend(["--package-filter", single_filter])
+        return doc_args
diff --git a/dev/breeze/src/airflow_breeze/shell/shell_params.py 
b/dev/breeze/src/airflow_breeze/params/shell_params.py
similarity index 99%
rename from dev/breeze/src/airflow_breeze/shell/shell_params.py
rename to dev/breeze/src/airflow_breeze/params/shell_params.py
index e5a20fb232..61eb576b60 100644
--- a/dev/breeze/src/airflow_breeze/shell/shell_params.py
+++ b/dev/breeze/src/airflow_breeze/params/shell_params.py
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""Breeze shell paameters."""
+
 import os
 from dataclasses import dataclass
 from pathlib import Path
diff --git a/dev/breeze/src/airflow_breeze/shell/__init__.py 
b/dev/breeze/src/airflow_breeze/shell/__init__.py
deleted file mode 100644
index 209c3a09f9..0000000000
--- a/dev/breeze/src/airflow_breeze/shell/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Entering Shell"""
diff --git a/dev/breeze/src/airflow_breeze/shell/enter_shell.py 
b/dev/breeze/src/airflow_breeze/shell/enter_shell.py
deleted file mode 100644
index 2249356e8f..0000000000
--- a/dev/breeze/src/airflow_breeze/shell/enter_shell.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Command to enter container shell for Breeze."""
-import subprocess
-import sys
-from typing import Optional, Union
-
-from airflow_breeze.shell.shell_params import ShellParams
-from airflow_breeze.utils.cache import read_from_cache_file
-from airflow_breeze.utils.console import get_console
-from airflow_breeze.utils.docker_command_utils import (
-    check_docker_compose_version,
-    check_docker_is_running,
-    check_docker_resources,
-    check_docker_version,
-    get_env_variables_for_docker_commands,
-)
-from airflow_breeze.utils.rebuild_image_if_needed import 
rebuild_ci_image_if_needed
-from airflow_breeze.utils.run_utils import filter_out_none, run_command
-from airflow_breeze.utils.visuals import ASCIIART, ASCIIART_STYLE, CHEATSHEET, 
CHEATSHEET_STYLE
-
-
-def enter_shell(**kwargs) -> Union[subprocess.CompletedProcess, 
subprocess.CalledProcessError]:
-    """
-    Executes entering shell using the parameters passed as kwargs:
-
-    * checks if docker version is good
-    * checks if docker-compose version is good
-    * updates kwargs with cached parameters
-    * displays ASCIIART and CHEATSHEET unless disabled
-    * build ShellParams from the updated kwargs
-    * executes the command to drop the user to Breeze shell
-
-    """
-    verbose = kwargs['verbose']
-    dry_run = kwargs['dry_run']
-    check_docker_is_running(verbose)
-    check_docker_version(verbose)
-    check_docker_compose_version(verbose)
-    if read_from_cache_file('suppress_asciiart') is None:
-        get_console().print(ASCIIART, style=ASCIIART_STYLE)
-    if read_from_cache_file('suppress_cheatsheet') is None:
-        get_console().print(CHEATSHEET, style=CHEATSHEET_STYLE)
-    enter_shell_params = ShellParams(**filter_out_none(**kwargs))
-    return run_shell_with_build_image_checks(verbose, dry_run, 
enter_shell_params)
-
-
-def run_shell_with_build_image_checks(
-    verbose: bool, dry_run: bool, shell_params: ShellParams
-) -> Union[subprocess.CompletedProcess, subprocess.CalledProcessError]:
-    """
-    Executes a shell command built from params passed, checking if build is 
not needed.
-    * checks if there are enough resources to run shell
-    * checks if image was built at least once (if not - forces the build)
-    * if not forces, checks if build is needed and asks the user if so
-    * builds the image if needed
-    * prints information about the build
-    * constructs docker compose command to enter shell
-    * executes it
-
-    :param verbose: print commands when running
-    :param dry_run: do not execute "write" commands - just print what would 
happen
-    :param shell_params: parameters of the execution
-    """
-    rebuild_ci_image_if_needed(build_params=shell_params, dry_run=dry_run, 
verbose=verbose)
-    shell_params.print_badge_info()
-    cmd = ['docker-compose', 'run', '--service-ports', "-e", "BREEZE", '--rm', 
'airflow']
-    cmd_added = shell_params.command_passed
-    env_variables = get_env_variables_for_docker_commands(shell_params)
-    if cmd_added is not None:
-        cmd.extend(['-c', cmd_added])
-
-    command_result = run_command(
-        cmd, verbose=verbose, dry_run=dry_run, env=env_variables, text=True, 
check=False
-    )
-    if command_result.returncode == 0:
-        return command_result
-    else:
-        get_console().print(f"[red]Error {command_result.returncode} 
returned[/]")
-        if verbose:
-            get_console().print(command_result.stderr)
-        return command_result
-
-
-def stop_exec_on_error(returncode: int):
-    get_console().print('\n[error]ERROR in finding the airflow docker-compose 
process id[/]\n')
-    sys.exit(returncode)
-
-
-def find_airflow_container(verbose, dry_run) -> Optional[str]:
-    exec_shell_params = ShellParams(verbose=verbose, dry_run=dry_run)
-    check_docker_resources(exec_shell_params.airflow_image_name, 
verbose=verbose, dry_run=dry_run)
-    exec_shell_params.print_badge_info()
-    env_variables = get_env_variables_for_docker_commands(exec_shell_params)
-    cmd = ['docker-compose', 'ps', '--all', '--filter', 'status=running', 
'airflow']
-    docker_compose_ps_command = run_command(
-        cmd, verbose=verbose, dry_run=dry_run, text=True, capture_output=True, 
env=env_variables, check=False
-    )
-    if dry_run:
-        return "CONTAINER_ID"
-    if docker_compose_ps_command.returncode != 0:
-        if verbose:
-            get_console().print(docker_compose_ps_command.stdout)
-            get_console().print(docker_compose_ps_command.stderr)
-        stop_exec_on_error(docker_compose_ps_command.returncode)
-        return None
-
-    output = docker_compose_ps_command.stdout
-    container_info = output.strip().split('\n')
-    if container_info:
-        container_running = container_info[-1].split(' ')[0]
-        if container_running.startswith('-'):
-            # On docker-compose v1 we get '--------' as output here
-            stop_exec_on_error(docker_compose_ps_command.returncode)
-        return container_running
-    else:
-        stop_exec_on_error(1)
-        return None
diff --git a/dev/breeze/src/airflow_breeze/commands/common_options.py 
b/dev/breeze/src/airflow_breeze/utils/common_options.py
similarity index 99%
rename from dev/breeze/src/airflow_breeze/commands/common_options.py
rename to dev/breeze/src/airflow_breeze/utils/common_options.py
index 474ba81b1c..0857a1f71e 100644
--- a/dev/breeze/src/airflow_breeze/commands/common_options.py
+++ b/dev/breeze/src/airflow_breeze/utils/common_options.py
@@ -20,13 +20,6 @@ import multiprocessing as mp
 import click
 
 from airflow_breeze.branch_defaults import DEFAULT_AIRFLOW_CONSTRAINTS_BRANCH
-from airflow_breeze.commands.custom_param_types import (
-    AnswerChoice,
-    BetterChoice,
-    CacheableChoice,
-    CacheableDefault,
-    UseAirflowVersionType,
-)
 from airflow_breeze.global_constants import (
     ALLOWED_BACKENDS,
     ALLOWED_BUILD_CACHE,
@@ -44,6 +37,13 @@ from airflow_breeze.global_constants import (
     ALLOWED_USE_AIRFLOW_VERSIONS,
     get_available_packages,
 )
+from airflow_breeze.utils.custom_param_types import (
+    AnswerChoice,
+    BetterChoice,
+    CacheableChoice,
+    CacheableDefault,
+    UseAirflowVersionType,
+)
 from airflow_breeze.utils.recording import output_file_for_recording
 
 option_verbose = click.option(
diff --git a/dev/breeze/src/airflow_breeze/commands/custom_param_types.py 
b/dev/breeze/src/airflow_breeze/utils/custom_param_types.py
similarity index 100%
rename from dev/breeze/src/airflow_breeze/commands/custom_param_types.py
rename to dev/breeze/src/airflow_breeze/utils/custom_param_types.py
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 069c0a3c9d..0478a2f323 100644
--- a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
+++ b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
@@ -22,9 +22,9 @@ import sys
 from random import randint
 from typing import Dict, List, Tuple, Union
 
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
-from airflow_breeze.shell.shell_params import ShellParams
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.params.build_prod_params import BuildProdParams
+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
 from airflow_breeze.utils.registry import login_to_docker_registry
@@ -297,7 +297,7 @@ def get_env_variable_value(arg_name: str, params: 
Union[BuildCiParams, BuildProd
 
 
 def construct_arguments_for_docker_build_command(
-    image_params: Union[BuildCiParams, BuildProdParams], required_args: 
List[str], optional_args: List[str]
+    image_params: Union[BuildCiParams, BuildProdParams]
 ) -> List[str]:
     """
     Constructs docker compose command arguments list based on parameters 
passed. Maps arguments to
@@ -309,18 +309,16 @@ def construct_arguments_for_docker_build_command(
       for the need of always triggering upgrade for docker build.
 
     :param image_params: parameters of the image
-    :param required_args: build argument that are required
-    :param optional_args: build arguments that are optional (should not be 
used if missing or empty)
     :return: list of `--build-arg` commands to use for the parameters passed
     """
 
     args_command = []
-    for required_arg in required_args:
+    for required_arg in image_params.required_image_args:
         args_command.append("--build-arg")
         args_command.append(
             required_arg.upper() + "=" + 
get_env_variable_value(arg_name=required_arg, params=image_params)
         )
-    for optional_arg in optional_args:
+    for optional_arg in image_params.optional_image_args:
         param_value = get_env_variable_value(optional_arg, params=image_params)
         if len(param_value) > 0:
             args_command.append("--build-arg")
@@ -332,22 +330,16 @@ def construct_arguments_for_docker_build_command(
 def construct_docker_build_command(
     image_params: Union[BuildProdParams, BuildCiParams],
     verbose: bool,
-    required_args: List[str],
-    optional_args: List[str],
     production_image: bool,
 ) -> List[str]:
     """
     Constructs docker build command based on the parameters passed.
     :param image_params: parameters of the image
     :param verbose: print commands when running
-    :param required_args: build argument that are required
-    :param optional_args: build arguments that are optional (should not be 
used if missing or empty)
     :param production_image: whether this is production image or ci image
     :return: Command to run as list of string
     """
-    arguments = construct_arguments_for_docker_build_command(
-        image_params, required_args=required_args, optional_args=optional_args
-    )
+    arguments = construct_arguments_for_docker_build_command(image_params)
     build_command = prepare_build_command(
         prepare_buildx_cache=image_params.prepare_buildx_cache, verbose=verbose
     )
diff --git a/dev/breeze/src/airflow_breeze/build_image/__init__.py 
b/dev/breeze/src/airflow_breeze/utils/mark_image_as_refreshed.py
similarity index 55%
rename from dev/breeze/src/airflow_breeze/build_image/__init__.py
rename to dev/breeze/src/airflow_breeze/utils/mark_image_as_refreshed.py
index 7b70ea1ee8..b62880841d 100644
--- a/dev/breeze/src/airflow_breeze/build_image/__init__.py
+++ b/dev/breeze/src/airflow_breeze/utils/mark_image_as_refreshed.py
@@ -14,4 +14,15 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""Build CI and PROD images."""
+
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.utils.cache import touch_cache_file
+from airflow_breeze.utils.md5_build_check import 
calculate_md5_checksum_for_files
+from airflow_breeze.utils.path_utils import BUILD_CACHE_DIR
+
+
+def mark_image_as_refreshed(ci_image_params: BuildCiParams):
+    ci_image_cache_dir = BUILD_CACHE_DIR / ci_image_params.airflow_branch
+    ci_image_cache_dir.mkdir(parents=True, exist_ok=True)
+    touch_cache_file(f"built_{ci_image_params.python}", 
root_dir=ci_image_cache_dir)
+    calculate_md5_checksum_for_files(ci_image_params.md5sum_cache_dir, 
update=True)
diff --git a/dev/breeze/src/airflow_breeze/utils/pulll_image.py 
b/dev/breeze/src/airflow_breeze/utils/pulll_image.py
index adc1ecfb38..e752463ac4 100644
--- a/dev/breeze/src/airflow_breeze/utils/pulll_image.py
+++ b/dev/breeze/src/airflow_breeze/utils/pulll_image.py
@@ -19,10 +19,10 @@ import multiprocessing as mp
 import time
 from typing import List, Tuple, Union
 
-from airflow_breeze.build_image.ci.build_ci_image import 
mark_image_as_refreshed
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.params.build_prod_params import BuildProdParams
 from airflow_breeze.utils.console import get_console
+from airflow_breeze.utils.mark_image_as_refreshed import 
mark_image_as_refreshed
 from airflow_breeze.utils.parallel import check_async_run_results
 from airflow_breeze.utils.run_tests import verify_an_image
 from airflow_breeze.utils.run_utils import run_command
diff --git a/dev/breeze/src/airflow_breeze/utils/rebuild_image_if_needed.py 
b/dev/breeze/src/airflow_breeze/utils/rebuild_image_if_needed.py
deleted file mode 100644
index 158361997a..0000000000
--- a/dev/breeze/src/airflow_breeze/utils/rebuild_image_if_needed.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from pathlib import Path
-from typing import Union
-
-from airflow_breeze.build_image.ci.build_ci_image import build_ci_image
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.shell.shell_params import ShellParams
-from airflow_breeze.utils.console import get_console
-from airflow_breeze.utils.docker_command_utils import (
-    check_docker_compose_version,
-    check_docker_resources,
-    check_docker_version,
-)
-from airflow_breeze.utils.path_utils import BUILD_CACHE_DIR
-
-
-def rebuild_ci_image_if_needed(
-    build_params: Union[ShellParams, BuildCiParams], dry_run: bool, verbose: 
bool
-) -> None:
-    """
-    Rebuilds CI image if needed and user confirms it.
-
-    :param build_params: parameters of the shell
-    :param dry_run: whether it's a dry_run
-    :param verbose: should we print verbose messages
-    """
-    check_docker_version(verbose=verbose)
-    check_docker_compose_version(verbose=verbose)
-    check_docker_resources(build_params.airflow_image_name, verbose=verbose, 
dry_run=dry_run)
-    build_ci_image_check_cache = Path(
-        BUILD_CACHE_DIR, build_params.airflow_branch, 
f".built_{build_params.python}"
-    )
-    ci_image_params = BuildCiParams(python=build_params.python, 
upgrade_to_newer_dependencies=False)
-    if build_ci_image_check_cache.exists():
-        if verbose:
-            get_console().print(f'[info]{build_params.the_image_type} image 
already built locally.[/]')
-    else:
-        get_console().print(
-            f'[warning]{build_params.the_image_type} image not built locally. 
Forcing build.[/]'
-        )
-        ci_image_params.force_build = True
-    build_ci_image(verbose, dry_run=dry_run, ci_image_params=ci_image_params)
diff --git a/dev/breeze/src/airflow_breeze/utils/registry.py 
b/dev/breeze/src/airflow_breeze/utils/registry.py
index 7c2e8a6fdb..af5126934e 100644
--- a/dev/breeze/src/airflow_breeze/utils/registry.py
+++ b/dev/breeze/src/airflow_breeze/utils/registry.py
@@ -18,8 +18,8 @@
 import os
 from typing import Tuple, Union
 
-from airflow_breeze.build_image.ci.build_ci_params import BuildCiParams
-from airflow_breeze.build_image.prod.build_prod_params import BuildProdParams
+from airflow_breeze.params.build_ci_params import BuildCiParams
+from airflow_breeze.params.build_prod_params import BuildProdParams
 from airflow_breeze.utils.console import get_console
 from airflow_breeze.utils.run_utils import run_command
 

Reply via email to