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

kaxilnaik pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new ee83c05a367 Allow forwarding ports using "breeze run" command (#61544)
ee83c05a367 is described below

commit ee83c05a3671961c2f48149354bc6e0c45dc6d5a
Author: Kaxil Naik <[email protected]>
AuthorDate: Fri Feb 6 17:44:44 2026 +0000

    Allow forwarding ports using "breeze run" command (#61544)
    
    On many occassions, I find myself just running "breeze run airflow 
standalone" to check something basic where I don't need "breeze start-airflow", 
this will allow optionally forwarding ports
---
 dev/breeze/doc/images/output_run.svg                 | 20 ++++++++++++--------
 dev/breeze/doc/images/output_run.txt                 |  2 +-
 .../src/airflow_breeze/commands/common_options.py    |  6 ++++++
 .../airflow_breeze/commands/developer_commands.py    |  4 ++++
 .../commands/developer_commands_config.py            |  1 +
 .../src/airflow_breeze/utils/docker_command_utils.py | 12 +++++++++---
 6 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/dev/breeze/doc/images/output_run.svg 
b/dev/breeze/doc/images/output_run.svg
index 7a7760674f0..24ba497e4e8 100644
--- a/dev/breeze/doc/images/output_run.svg
+++ b/dev/breeze/doc/images/output_run.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 1270.0" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 1294.3999999999999" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -43,7 +43,7 @@
 
     <defs>
     <clipPath id="breeze-run-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="1219.0" />
+      <rect x="0" y="0" width="1463.0" height="1243.3999999999999" />
     </clipPath>
     <clipPath id="breeze-run-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -192,9 +192,12 @@
 <clipPath id="breeze-run-line-48">
     <rect x="0" y="1172.7" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-run-line-49">
+    <rect x="0" y="1197.1" width="1464" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="1268" rx="8"/><text class="breeze-run-title" 
fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command:&#160;run</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="1292.4" rx="8"/><text class="breeze-run-title" 
fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command:&#160;run</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -249,11 +252,12 @@
 </text><text class="breeze-run-r5" x="0" y="1044.8" textLength="12.2" 
clip-path="url(#breeze-run-line-42)">│</text><text class="breeze-run-r4" 
x="24.4" y="1044.8" textLength="317.2" 
clip-path="url(#breeze-run-line-42)">--skip-image-upgrade-check</text><text 
class="breeze-run-r1" x="414.8" y="1044.8" textLength="536.8" 
clip-path="url(#breeze-run-line-42)">Skip&#160;checking&#160;if&#160;the&#160;CI&#160;image&#160;is&#160;up&#160;to&#160;date.</text><text
 class="breeze-run-r5" x="1451.8"  [...]
 </text><text class="breeze-run-r5" x="0" y="1069.2" textLength="1464" 
clip-path="url(#breeze-run-line-43)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-run-r1" x="1464" y="1069.2" textLength="12.2" 
clip-path="url(#breeze-run-line-43)">
 </text><text class="breeze-run-r5" x="0" y="1093.6" textLength="24.4" 
clip-path="url(#breeze-run-line-44)">╭─</text><text class="breeze-run-r5" 
x="24.4" y="1093.6" textLength="195.2" 
clip-path="url(#breeze-run-line-44)">&#160;Common&#160;options&#160;</text><text
 class="breeze-run-r5" x="219.6" y="1093.6" textLength="1220" 
clip-path="url(#breeze-run-line-44)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
 class="breeze-run [...]
-</text><text class="breeze-run-r5" x="0" y="1118" textLength="12.2" 
clip-path="url(#breeze-run-line-45)">│</text><text class="breeze-run-r4" 
x="24.4" y="1118" textLength="97.6" 
clip-path="url(#breeze-run-line-45)">--answer</text><text class="breeze-run-r6" 
x="158.6" y="1118" textLength="24.4" 
clip-path="url(#breeze-run-line-45)">-a</text><text class="breeze-run-r1" 
x="207.4" y="1118" textLength="317.2" 
clip-path="url(#breeze-run-line-45)">Force&#160;answer&#160;to&#160;questions.</text><
 [...]
-</text><text class="breeze-run-r5" x="0" y="1142.4" textLength="12.2" 
clip-path="url(#breeze-run-line-46)">│</text><text class="breeze-run-r4" 
x="24.4" y="1142.4" textLength="109.8" 
clip-path="url(#breeze-run-line-46)">--dry-run</text><text 
class="breeze-run-r6" x="158.6" y="1142.4" textLength="24.4" 
clip-path="url(#breeze-run-line-46)">-D</text><text class="breeze-run-r1" 
x="207.4" y="1142.4" textLength="719.8" 
clip-path="url(#breeze-run-line-46)">If&#160;dry-run&#160;is&#160;set,&#160; 
[...]
-</text><text class="breeze-run-r5" x="0" y="1166.8" textLength="12.2" 
clip-path="url(#breeze-run-line-47)">│</text><text class="breeze-run-r4" 
x="24.4" y="1166.8" textLength="109.8" 
clip-path="url(#breeze-run-line-47)">--verbose</text><text 
class="breeze-run-r6" x="158.6" y="1166.8" textLength="24.4" 
clip-path="url(#breeze-run-line-47)">-v</text><text class="breeze-run-r1" 
x="207.4" y="1166.8" textLength="585.6" 
clip-path="url(#breeze-run-line-47)">Print&#160;verbose&#160;information&#16 
[...]
-</text><text class="breeze-run-r5" x="0" y="1191.2" textLength="12.2" 
clip-path="url(#breeze-run-line-48)">│</text><text class="breeze-run-r4" 
x="24.4" y="1191.2" textLength="73.2" 
clip-path="url(#breeze-run-line-48)">--help</text><text class="breeze-run-r6" 
x="158.6" y="1191.2" textLength="24.4" 
clip-path="url(#breeze-run-line-48)">-h</text><text class="breeze-run-r1" 
x="207.4" y="1191.2" textLength="329.4" 
clip-path="url(#breeze-run-line-48)">Show&#160;this&#160;message&#160;and&#160;e
 [...]
-</text><text class="breeze-run-r5" x="0" y="1215.6" textLength="1464" 
clip-path="url(#breeze-run-line-49)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-run-r1" x="1464" y="1215.6" textLength="12.2" 
clip-path="url(#breeze-run-line-49)">
+</text><text class="breeze-run-r5" x="0" y="1118" textLength="12.2" 
clip-path="url(#breeze-run-line-45)">│</text><text class="breeze-run-r4" 
x="24.4" y="1118" textLength="97.6" 
clip-path="url(#breeze-run-line-45)">--answer</text><text class="breeze-run-r6" 
x="231.8" y="1118" textLength="24.4" 
clip-path="url(#breeze-run-line-45)">-a</text><text class="breeze-run-r1" 
x="280.6" y="1118" textLength="317.2" 
clip-path="url(#breeze-run-line-45)">Force&#160;answer&#160;to&#160;questions.</text><
 [...]
+</text><text class="breeze-run-r5" x="0" y="1142.4" textLength="12.2" 
clip-path="url(#breeze-run-line-46)">│</text><text class="breeze-run-r4" 
x="24.4" y="1142.4" textLength="109.8" 
clip-path="url(#breeze-run-line-46)">--dry-run</text><text 
class="breeze-run-r6" x="231.8" y="1142.4" textLength="24.4" 
clip-path="url(#breeze-run-line-46)">-D</text><text class="breeze-run-r1" 
x="280.6" y="1142.4" textLength="719.8" 
clip-path="url(#breeze-run-line-46)">If&#160;dry-run&#160;is&#160;set,&#160; 
[...]
+</text><text class="breeze-run-r5" x="0" y="1166.8" textLength="12.2" 
clip-path="url(#breeze-run-line-47)">│</text><text class="breeze-run-r4" 
x="24.4" y="1166.8" textLength="183" 
clip-path="url(#breeze-run-line-47)">--forward-ports</text><text 
class="breeze-run-r1" x="280.6" y="1166.8" textLength="866.2" 
clip-path="url(#breeze-run-line-47)">Forward&#160;ports&#160;to&#160;host&#160;(for&#160;accessing&#160;Airflow&#160;UI/API&#160;from&#160;host&#160;machine).</text><text
 class="breeze- [...]
+</text><text class="breeze-run-r5" x="0" y="1191.2" textLength="12.2" 
clip-path="url(#breeze-run-line-48)">│</text><text class="breeze-run-r4" 
x="24.4" y="1191.2" textLength="109.8" 
clip-path="url(#breeze-run-line-48)">--verbose</text><text 
class="breeze-run-r6" x="231.8" y="1191.2" textLength="24.4" 
clip-path="url(#breeze-run-line-48)">-v</text><text class="breeze-run-r1" 
x="280.6" y="1191.2" textLength="585.6" 
clip-path="url(#breeze-run-line-48)">Print&#160;verbose&#160;information&#16 
[...]
+</text><text class="breeze-run-r5" x="0" y="1215.6" textLength="12.2" 
clip-path="url(#breeze-run-line-49)">│</text><text class="breeze-run-r4" 
x="24.4" y="1215.6" textLength="73.2" 
clip-path="url(#breeze-run-line-49)">--help</text><text class="breeze-run-r6" 
x="231.8" y="1215.6" textLength="24.4" 
clip-path="url(#breeze-run-line-49)">-h</text><text class="breeze-run-r1" 
x="280.6" y="1215.6" textLength="329.4" 
clip-path="url(#breeze-run-line-49)">Show&#160;this&#160;message&#160;and&#160;e
 [...]
+</text><text class="breeze-run-r5" x="0" y="1240" textLength="1464" 
clip-path="url(#breeze-run-line-50)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-run-r1" x="1464" y="1240" textLength="12.2" 
clip-path="url(#breeze-run-line-50)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_run.txt 
b/dev/breeze/doc/images/output_run.txt
index b94f7717712..0f882d41568 100644
--- a/dev/breeze/doc/images/output_run.txt
+++ b/dev/breeze/doc/images/output_run.txt
@@ -1 +1 @@
-102bff8e1fa8eb11f76db9289c4e1e97
+d688a631859f6a913f3dd7dd5a7b52d3
diff --git a/dev/breeze/src/airflow_breeze/commands/common_options.py 
b/dev/breeze/src/airflow_breeze/commands/common_options.py
index d76feb7129c..1fda79b50ea 100644
--- a/dev/breeze/src/airflow_breeze/commands/common_options.py
+++ b/dev/breeze/src/airflow_breeze/commands/common_options.py
@@ -185,6 +185,12 @@ option_dry_run = click.option(
 option_forward_credentials = click.option(
     "-f", "--forward-credentials", help="Forward local credentials to 
container when running.", is_flag=True
 )
+option_forward_ports = click.option(
+    "--forward-ports",
+    is_flag=True,
+    default=False,
+    help="Forward ports to host (for accessing Airflow UI/API from host 
machine).",
+)
 option_excluded_providers = click.option(
     "--excluded-providers",
     help="JSON-string of dictionary containing excluded providers per python 
version ({'3.12': ['provider']})",
diff --git a/dev/breeze/src/airflow_breeze/commands/developer_commands.py 
b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
index 31a2eafc4d6..f75d9bb4690 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
@@ -51,6 +51,7 @@ from airflow_breeze.commands.common_options import (
     option_excluded_providers,
     option_force_lowest_dependencies,
     option_forward_credentials,
+    option_forward_ports,
     option_github_repository,
     option_include_not_ready_providers,
     option_include_removed_providers,
@@ -1067,6 +1068,7 @@ def doctor(ctx):
 @option_dry_run
 @option_force_build
 @option_forward_credentials
+@option_forward_ports
 @option_github_repository
 @option_mysql_version
 @option_platform_single
@@ -1086,6 +1088,7 @@ def run(
     docker_host: str | None,
     force_build: bool,
     forward_credentials: bool,
+    forward_ports: bool,
     github_repository: str,
     mysql_version: str,
     platform: str | None,
@@ -1181,6 +1184,7 @@ def run(
             command=full_command,
             # Always preserve the backend specified by user (or resolved from 
default)
             preserve_backend=True,
+            forward_ports=forward_ports,
         )
 
     # Clean up ownership
diff --git 
a/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py 
b/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py
index 84422305cce..3dcb43962d5 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py
@@ -296,6 +296,7 @@ DEVELOPER_PARAMETERS: dict[str, list[dict[str, str | 
list[str]]]] = {
                 "--backend",
                 "--postgres-version",
                 "--mysql-version",
+                "--forward-ports",
                 "--tty",
             ],
         },
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 dc0bf13d0cf..8e01428c0fa 100644
--- a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
+++ b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
@@ -772,6 +772,7 @@ def execute_command_in_shell(
     output: Output | None = None,
     signal_error: bool = True,
     preserve_backend: bool = False,
+    forward_ports: bool = False,
 ) -> RunCommandResult:
     """Executes command in shell.
 
@@ -781,7 +782,7 @@ def execute_command_in_shell(
     * backend - to force sqlite backend (unless preserve_backend=True)
     * clean_sql_db=True - to clean the sqlite DB
     * forward_ports=False - to avoid forwarding ports from the container to 
the host - again that will
-      allow to avoid clashes with other commands and opened breeze shell
+      allow to avoid clashes with other commands and opened breeze shell 
(unless forward_ports=True is passed)
     * project_name - to avoid name clashes with default "breeze" project name 
used
     * quiet=True - avoid displaying all "interactive" parts of Breeze: 
ASCIIART, CHEATSHEET, some diagnostics
     * skip_environment_initialization - to avoid initializing interactive 
environment
@@ -797,10 +798,12 @@ def execute_command_in_shell(
     :param output: output configuration
     :param signal_error: whether to signal error
     :param preserve_backend: if True, preserve the backend specified in 
shell_params instead of forcing sqlite
+    :param forward_ports: if True, keep port forwarding enabled (for accessing 
services from host)
     """
     if not preserve_backend:
         shell_params.backend = "sqlite"
-    shell_params.forward_ports = False
+    if not forward_ports:
+        shell_params.forward_ports = False
     shell_params.project_name = project_name
     shell_params.quiet = True
     shell_params.skip_environment_initialization = True
@@ -810,7 +813,10 @@ def execute_command_in_shell(
             get_console().print("[warning]Sqlite DB is cleaned[/]")
         else:
             get_console().print(f"[info]Using backend: 
{shell_params.backend}[/]")
-        get_console().print("[warning]Disabled port forwarding[/]")
+        if forward_ports:
+            get_console().print("[info]Port forwarding enabled[/]")
+        else:
+            get_console().print("[warning]Disabled port forwarding[/]")
         get_console().print(f"[warning]Project name set to: {project_name}[/]")
         get_console().print("[warning]Forced quiet mode[/]")
         get_console().print("[warning]Forced skipping environment 
initialization[/]")

Reply via email to