This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new a6bd360998d Breeze: make `breeze down` discover and stop every compose
project (#66311) (#66928)
a6bd360998d is described below
commit a6bd360998d3bb8c1dd77ce7ff614aeb2901a904
Author: Rahul Vats <[email protected]>
AuthorDate: Fri May 15 10:16:21 2026 +0530
Breeze: make `breeze down` discover and stop every compose project (#66311)
(#66928)
Discovers running docker compose projects via the
`com.docker.compose.project` label and brings down every one matching
a known breeze prefix (`breeze`, `prek`, `docker-compose`, `docs`, `db`,
`providers`, plus `breeze-`, `airflow-test`, `constraints-`, `providers-`).
One `breeze down` now leaves the host clean regardless of which breeze
commands, prek hooks, or CI steps were running.
Two new flags: `--all-projects` also catches compose projects that don't
match any known breeze prefix (off by default to avoid wiping unrelated
host projects); `--project-name <name>` restricts cleanup to a single
project (useful in CI).
Single-use cleanup gaps fixed:
- `run_command_via_breeze_shell` (used by every breeze-shell-based prek
hook) now wraps the subprocess in try/finally and passes `--volumes`
to the down call, so prek runs no longer leak DB volumes between hooks
and KeyboardInterrupt no longer skips cleanup.
- `breeze registry extract-data` and `breeze registry backfill-data`
added try/finally cleanup of their unique compose projects (previously
leaked `breeze-registry-*` and `breeze-backfill-*` on every invocation).
- `breeze doctor` now uses the same label-based cleanup.
(cherry picked from commit 263549590ecbb0c489fb0166caefa456dfe9b8d4)
Co-authored-by: Jarek Potiuk <[email protected]>
---
dev/breeze/doc/03_developer_tasks.rst | 13 +++
dev/breeze/doc/images/output-commands.svg | 84 ++++++++++---------
dev/breeze/doc/images/output_down.svg | 67 +++++++++++----
dev/breeze/doc/images/output_down.txt | 2 +-
.../airflow_breeze/commands/developer_commands.py | 59 +++++++++++--
.../commands/developer_commands_config.py | 7 ++
.../airflow_breeze/commands/registry_commands.py | 30 ++++---
dev/breeze/src/airflow_breeze/global_constants.py | 22 +++++
.../airflow_breeze/utils/docker_command_utils.py | 79 ++++++++++++++++++
dev/breeze/tests/test_docker_command_utils.py | 97 ++++++++++++++++++++++
scripts/ci/prek/common_prek_utils.py | 31 ++++---
11 files changed, 405 insertions(+), 86 deletions(-)
diff --git a/dev/breeze/doc/03_developer_tasks.rst
b/dev/breeze/doc/03_developer_tasks.rst
index 37dff9045ad..cc951edb1f2 100644
--- a/dev/breeze/doc/03_developer_tasks.rst
+++ b/dev/breeze/doc/03_developer_tasks.rst
@@ -694,6 +694,19 @@ You can always stop it via:
breeze down
+``breeze down`` discovers every running docker compose project that breeze
knows
+about — ``breeze shell``, ``breeze testing``, ``breeze build-docs``, ``breeze
db``,
+release-management, registry, ``breeze run``, and prek-hook compose projects —
by
+reading the ``com.docker.compose.project`` label that compose sets on every
container
+it creates. Each matching project is brought down with ``--remove-orphans`` and
+``--volumes`` (unless ``--preserve-volumes`` is passed). One ``breeze down`` is
+enough to leave the host clean.
+
+If you have an unrelated docker compose project running on the host that does
not
+match any breeze prefix, it is left alone by default. Pass ``--all-projects``
to
+also bring those down. To restrict the cleanup to a single named project
(useful
+in CI steps), pass ``--project-name <name>``.
+
These are all available flags of ``down`` command:
.. image:: ./images/output_down.svg
diff --git a/dev/breeze/doc/images/output-commands.svg
b/dev/breeze/doc/images/output-commands.svg
index d6cd7ad2284..e5762bf56c8 100644
--- a/dev/breeze/doc/images/output-commands.svg
+++ b/dev/breeze/doc/images/output-commands.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 2538.7999999999997"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 2563.2"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -43,7 +43,7 @@
<defs>
<clipPath id="breeze-help-clip-terminal">
- <rect x="0" y="0" width="1463.0" height="2487.7999999999997" />
+ <rect x="0" y="0" width="1463.0" height="2512.2" />
</clipPath>
<clipPath id="breeze-help-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -348,9 +348,12 @@
<clipPath id="breeze-help-line-100">
<rect x="0" y="2441.5" width="1464" height="24.65"/>
</clipPath>
+<clipPath id="breeze-help-line-101">
+ <rect x="0" y="2465.9" 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="2536.8" rx="8"/><text
class="breeze-help-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Breeze commands</text>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="2561.2" rx="8"/><text
class="breeze-help-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Breeze commands</text>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -423,45 +426,46 @@
</text><text class="breeze-help-r5" x="0" y="1484" textLength="12.2"
clip-path="url(#breeze-help-line-60)">│</text><text class="breeze-help-r4"
x="24.4" y="1484" textLength="280.6"
clip-path="url(#breeze-help-line-60)">start-airflow          </text><text
class="breeze-help-r1" x="329.4" y="1484" textLength="1110.2"
clip-path="url(#breeze-help-line-60)">Enter breeze environment and starts all Airflow comp
[...]
</text><text class="breeze-help-r5" x="0" y="1508.4" textLength="12.2"
clip-path="url(#breeze-help-line-61)">│</text><text class="breeze-help-r1"
x="329.4" y="1508.4" textLength="1110.2"
clip-path="url(#breeze-help-line-61)">Compile assets if contents of www directory changed.                              
[...]
</text><text class="breeze-help-r5" x="0" y="1532.8" textLength="12.2"
clip-path="url(#breeze-help-line-62)">│</text><text class="breeze-help-r4"
x="24.4" y="1532.8" textLength="280.6"
clip-path="url(#breeze-help-line-62)">build-docs             </text><text
class="breeze-help-r1" x="329.4" y="1532.8" textLength="1110.2"
clip-path="url(#breeze-help-line-62)">Build documents.       
[...]
-</text><text class="breeze-help-r5" x="0" y="1557.2" textLength="12.2"
clip-path="url(#breeze-help-line-63)">│</text><text class="breeze-help-r4"
x="24.4" y="1557.2" textLength="280.6"
clip-path="url(#breeze-help-line-63)">down                   </text><text
class="breeze-help-r1" x="329.4" y="1557.2" textLength="1110.2"
clip-path="url(#breeze-help-line-63)">Stop running breeze e [...]
-</text><text class="breeze-help-r5" x="0" y="1581.6" textLength="12.2"
clip-path="url(#breeze-help-line-64)">│</text><text class="breeze-help-r4"
x="24.4" y="1581.6" textLength="280.6"
clip-path="url(#breeze-help-line-64)">shell                  </text><text
class="breeze-help-r1" x="329.4" y="1581.6" textLength="1110.2"
clip-path="url(#breeze-help-line-64)">Enter breeze environment. 
[...]
-</text><text class="breeze-help-r5" x="0" y="1606" textLength="12.2"
clip-path="url(#breeze-help-line-65)">│</text><text class="breeze-help-r4"
x="24.4" y="1606" textLength="280.6"
clip-path="url(#breeze-help-line-65)">exec                   </text><text
class="breeze-help-r1" x="329.4" y="1606" textLength="1110.2"
clip-path="url(#breeze-help-line-65)">Joins the interactive shell
[...]
-</text><text class="breeze-help-r5" x="0" y="1630.4" textLength="12.2"
clip-path="url(#breeze-help-line-66)">│</text><text class="breeze-help-r4"
x="24.4" y="1630.4" textLength="280.6"
clip-path="url(#breeze-help-line-66)">run                    </text><text
class="breeze-help-r1" x="329.4" y="1630.4" textLength="1110.2"
clip-path="url(#breeze-help-line-66)">Run a command in [...]
-</text><text class="breeze-help-r5" x="0" y="1654.8" textLength="12.2"
clip-path="url(#breeze-help-line-67)">│</text><text class="breeze-help-r4"
x="24.4" y="1654.8" textLength="280.6"
clip-path="url(#breeze-help-line-67)">cleanup                </text><text
class="breeze-help-r1" x="329.4" y="1654.8" textLength="1110.2"
clip-path="url(#breeze-help-line-67)">Cleans the cache of parameters,
[...]
-</text><text class="breeze-help-r5" x="0" y="1679.2" textLength="12.2"
clip-path="url(#breeze-help-line-68)">│</text><text class="breeze-help-r4"
x="24.4" y="1679.2" textLength="280.6"
clip-path="url(#breeze-help-line-68)">generate-migration-file</text><text
class="breeze-help-r1" x="329.4" y="1679.2" textLength="1110.2"
clip-path="url(#breeze-help-line-68)">Autogenerate the alembic migration file for the ORM changes.     &
[...]
-</text><text class="breeze-help-r5" x="0" y="1703.6" textLength="12.2"
clip-path="url(#breeze-help-line-69)">│</text><text class="breeze-help-r4"
x="24.4" y="1703.6" textLength="280.6"
clip-path="url(#breeze-help-line-69)">doctor                 </text><text
class="breeze-help-r1" x="329.4" y="1703.6" textLength="1110.2"
clip-path="url(#breeze-help-line-69)">Auto-healing of breeze  &#
[...]
-</text><text class="breeze-help-r5" x="0" y="1728" textLength="1464"
clip-path="url(#breeze-help-line-70)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="1728" textLength="12.2"
clip-path="url(#breeze-help-line-70)">
-</text><text class="breeze-help-r5" x="0" y="1752.4" textLength="24.4"
clip-path="url(#breeze-help-line-71)">╭─</text><text class="breeze-help-r5"
x="24.4" y="1752.4" textLength="219.6"
clip-path="url(#breeze-help-line-71)"> Testing commands </text><text
class="breeze-help-r5" x="244" y="1752.4" textLength="1195.6"
clip-path="url(#breeze-help-line-71)">──────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
-</text><text class="breeze-help-r5" x="0" y="1776.8" textLength="12.2"
clip-path="url(#breeze-help-line-72)">│</text><text class="breeze-help-r4"
x="24.4" y="1776.8" textLength="183"
clip-path="url(#breeze-help-line-72)">testing        </text><text
class="breeze-help-r1" x="231.8" y="1776.8" textLength="1207.8"
clip-path="url(#breeze-help-line-72)">Tools that developers can use to run tests    
[...]
-</text><text class="breeze-help-r5" x="0" y="1801.2" textLength="12.2"
clip-path="url(#breeze-help-line-73)">│</text><text class="breeze-help-r4"
x="24.4" y="1801.2" textLength="183"
clip-path="url(#breeze-help-line-73)">k8s            </text><text
class="breeze-help-r1" x="231.8" y="1801.2" textLength="1207.8"
clip-path="url(#breeze-help-line-73)">Tools that developers use to run Kubernetes te
[...]
+</text><text class="breeze-help-r5" x="0" y="1557.2" textLength="12.2"
clip-path="url(#breeze-help-line-63)">│</text><text class="breeze-help-r4"
x="24.4" y="1557.2" textLength="280.6"
clip-path="url(#breeze-help-line-63)">down                   </text><text
class="breeze-help-r1" x="329.4" y="1557.2" textLength="1110.2"
clip-path="url(#breeze-help-line-63)">Stop every docker com [...]
+</text><text class="breeze-help-r5" x="0" y="1581.6" textLength="12.2"
clip-path="url(#breeze-help-line-64)">│</text><text class="breeze-help-r1"
x="329.4" y="1581.6" textLength="805.2"
clip-path="url(#breeze-help-line-64)">`com.docker.compose.project` label and brings each one down with `</text><text
class="breeze-help-r4" x="1134.6" y="1581.6" textLength="195.2"
clip-path="url(#breeze-help-line-64)">--remove-orphans</text><text
class="breeze-help [...]
+</text><text class="breeze-help-r5" x="0" y="1606" textLength="12.2"
clip-path="url(#breeze-help-line-65)">│</text><text class="breeze-help-r1"
x="329.4" y="1606" textLength="12.2"
clip-path="url(#breeze-help-line-65)">`</text><text class="breeze-help-r4"
x="341.6" y="1606" textLength="109.8"
clip-path="url(#breeze-help-line-65)">--volumes</text><text
class="breeze-help-r1" x="451.4" y="1606" textLength="122"
clip-path="url(#breeze-help-line-65)">` unless `</text><text class="b
[...]
+</text><text class="breeze-help-r5" x="0" y="1630.4" textLength="12.2"
clip-path="url(#breeze-help-line-66)">│</text><text class="breeze-help-r1"
x="329.4" y="1630.4" textLength="1110.2"
clip-path="url(#breeze-help-line-66)">testing`, `breeze build-docs`, `breeze db`, release-management, registry, and prek-hook    </text><text
class="breeze-help-r5" x="1451.8" y="1630.4" textLength="12.2"
clip-path="url(#breeze-help-line-66)">│< [...]
+</text><text class="breeze-help-r5" x="0" y="1654.8" textLength="12.2"
clip-path="url(#breeze-help-line-67)">│</text><text class="breeze-help-r1"
x="329.4" y="1654.8" textLength="1110.2"
clip-path="url(#breeze-help-line-67)">compose projects in a single command.                                  &
[...]
+</text><text class="breeze-help-r5" x="0" y="1679.2" textLength="12.2"
clip-path="url(#breeze-help-line-68)">│</text><text class="breeze-help-r4"
x="24.4" y="1679.2" textLength="280.6"
clip-path="url(#breeze-help-line-68)">shell                  </text><text
class="breeze-help-r1" x="329.4" y="1679.2" textLength="1110.2"
clip-path="url(#breeze-help-line-68)">Enter breeze environment. 
[...]
+</text><text class="breeze-help-r5" x="0" y="1703.6" textLength="12.2"
clip-path="url(#breeze-help-line-69)">│</text><text class="breeze-help-r4"
x="24.4" y="1703.6" textLength="280.6"
clip-path="url(#breeze-help-line-69)">exec                   </text><text
class="breeze-help-r1" x="329.4" y="1703.6" textLength="1110.2"
clip-path="url(#breeze-help-line-69)">Joins the interactive  [...]
+</text><text class="breeze-help-r5" x="0" y="1728" textLength="12.2"
clip-path="url(#breeze-help-line-70)">│</text><text class="breeze-help-r4"
x="24.4" y="1728" textLength="280.6"
clip-path="url(#breeze-help-line-70)">run                    </text><text
class="breeze-help-r1" x="329.4" y="1728" textLength="1110.2"
clip-path="url(#breeze-help-line-70)">Run a command in 
[...]
+</text><text class="breeze-help-r5" x="0" y="1752.4" textLength="12.2"
clip-path="url(#breeze-help-line-71)">│</text><text class="breeze-help-r4"
x="24.4" y="1752.4" textLength="280.6"
clip-path="url(#breeze-help-line-71)">cleanup                </text><text
class="breeze-help-r1" x="329.4" y="1752.4" textLength="1110.2"
clip-path="url(#breeze-help-line-71)">Cleans the cache of parameters,
[...]
+</text><text class="breeze-help-r5" x="0" y="1776.8" textLength="12.2"
clip-path="url(#breeze-help-line-72)">│</text><text class="breeze-help-r4"
x="24.4" y="1776.8" textLength="280.6"
clip-path="url(#breeze-help-line-72)">generate-migration-file</text><text
class="breeze-help-r1" x="329.4" y="1776.8" textLength="1110.2"
clip-path="url(#breeze-help-line-72)">Autogenerate the alembic migration file for the ORM changes.     &
[...]
+</text><text class="breeze-help-r5" x="0" y="1801.2" textLength="12.2"
clip-path="url(#breeze-help-line-73)">│</text><text class="breeze-help-r4"
x="24.4" y="1801.2" textLength="280.6"
clip-path="url(#breeze-help-line-73)">doctor                 </text><text
class="breeze-help-r1" x="329.4" y="1801.2" textLength="1110.2"
clip-path="url(#breeze-help-line-73)">Auto-healing of breeze  &#
[...]
</text><text class="breeze-help-r5" x="0" y="1825.6" textLength="1464"
clip-path="url(#breeze-help-line-74)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="1825.6" textLength="12.2"
clip-path="url(#breeze-help-line-74)">
-</text><text class="breeze-help-r5" x="0" y="1850" textLength="24.4"
clip-path="url(#breeze-help-line-75)">╭─</text><text class="breeze-help-r5"
x="24.4" y="1850" textLength="195.2"
clip-path="url(#breeze-help-line-75)"> Image commands </text><text
class="breeze-help-r5" x="219.6" y="1850" textLength="1220"
clip-path="url(#breeze-help-line-75)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-hel [...]
-</text><text class="breeze-help-r5" x="0" y="1874.4" textLength="12.2"
clip-path="url(#breeze-help-line-76)">│</text><text class="breeze-help-r4"
x="24.4" y="1874.4" textLength="207.4"
clip-path="url(#breeze-help-line-76)">ci-image         </text><text
class="breeze-help-r1" x="256.2" y="1874.4" textLength="1183.4"
clip-path="url(#breeze-help-line-76)">Tools that developers can use to manually manage CI&
[...]
-</text><text class="breeze-help-r5" x="0" y="1898.8" textLength="12.2"
clip-path="url(#breeze-help-line-77)">│</text><text class="breeze-help-r4"
x="24.4" y="1898.8" textLength="207.4"
clip-path="url(#breeze-help-line-77)">prod-image       </text><text
class="breeze-help-r1" x="256.2" y="1898.8" textLength="1183.4"
clip-path="url(#breeze-help-line-77)">Tools that developers can use to manually manage PROD ima
[...]
+</text><text class="breeze-help-r5" x="0" y="1850" textLength="24.4"
clip-path="url(#breeze-help-line-75)">╭─</text><text class="breeze-help-r5"
x="24.4" y="1850" textLength="219.6"
clip-path="url(#breeze-help-line-75)"> Testing commands </text><text
class="breeze-help-r5" x="244" y="1850" textLength="1195.6"
clip-path="url(#breeze-help-line-75)">──────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-hel [...]
+</text><text class="breeze-help-r5" x="0" y="1874.4" textLength="12.2"
clip-path="url(#breeze-help-line-76)">│</text><text class="breeze-help-r4"
x="24.4" y="1874.4" textLength="183"
clip-path="url(#breeze-help-line-76)">testing        </text><text
class="breeze-help-r1" x="231.8" y="1874.4" textLength="1207.8"
clip-path="url(#breeze-help-line-76)">Tools that developers can use to run tests    
[...]
+</text><text class="breeze-help-r5" x="0" y="1898.8" textLength="12.2"
clip-path="url(#breeze-help-line-77)">│</text><text class="breeze-help-r4"
x="24.4" y="1898.8" textLength="183"
clip-path="url(#breeze-help-line-77)">k8s            </text><text
class="breeze-help-r1" x="231.8" y="1898.8" textLength="1207.8"
clip-path="url(#breeze-help-line-77)">Tools that developers use to run Kubernetes te
[...]
</text><text class="breeze-help-r5" x="0" y="1923.2" textLength="1464"
clip-path="url(#breeze-help-line-78)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="1923.2" textLength="12.2"
clip-path="url(#breeze-help-line-78)">
-</text><text class="breeze-help-r5" x="0" y="1947.6" textLength="24.4"
clip-path="url(#breeze-help-line-79)">╭─</text><text class="breeze-help-r5"
x="24.4" y="1947.6" textLength="353.8"
clip-path="url(#breeze-help-line-79)"> Release management commands </text><text
class="breeze-help-r5" x="378.2" y="1947.6" textLength="1061.4"
clip-path="url(#breeze-help-line-79)">───────────────────────────────────────────────────────────────────────────────────────</text><text
clas [...]
-</text><text class="breeze-help-r5" x="0" y="1972" textLength="12.2"
clip-path="url(#breeze-help-line-80)">│</text><text class="breeze-help-r4"
x="24.4" y="1972" textLength="280.6"
clip-path="url(#breeze-help-line-80)">release-management     </text><text
class="breeze-help-r1" x="329.4" y="1972" textLength="1110.2"
clip-path="url(#breeze-help-line-80)">Tools that release managers can use to prepare and manage Airf
[...]
-</text><text class="breeze-help-r5" x="0" y="1996.4" textLength="12.2"
clip-path="url(#breeze-help-line-81)">│</text><text class="breeze-help-r4"
x="24.4" y="1996.4" textLength="280.6"
clip-path="url(#breeze-help-line-81)">sbom                   </text><text
class="breeze-help-r1" x="329.4" y="1996.4" textLength="1110.2"
clip-path="url(#breeze-help-line-81)">Tools that release ma [...]
-</text><text class="breeze-help-r5" x="0" y="2020.8" textLength="12.2"
clip-path="url(#breeze-help-line-82)">│</text><text class="breeze-help-r4"
x="24.4" y="2020.8" textLength="280.6"
clip-path="url(#breeze-help-line-82)">workflow-run           </text><text
class="breeze-help-r1" x="329.4" y="2020.8" textLength="1110.2"
clip-path="url(#breeze-help-line-82)">Tools to manage Airflow repository workflows &
[...]
-</text><text class="breeze-help-r5" x="0" y="2045.2" textLength="1464"
clip-path="url(#breeze-help-line-83)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2045.2" textLength="12.2"
clip-path="url(#breeze-help-line-83)">
-</text><text class="breeze-help-r5" x="0" y="2069.6" textLength="24.4"
clip-path="url(#breeze-help-line-84)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2069.6" textLength="158.6"
clip-path="url(#breeze-help-line-84)"> CI commands </text><text
class="breeze-help-r5" x="183" y="2069.6" textLength="1256.6"
clip-path="url(#breeze-help-line-84)">───────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
-</text><text class="breeze-help-r5" x="0" y="2094" textLength="12.2"
clip-path="url(#breeze-help-line-85)">│</text><text class="breeze-help-r4"
x="24.4" y="2094" textLength="61"
clip-path="url(#breeze-help-line-85)">ci   </text><text
class="breeze-help-r1" x="109.8" y="2094" textLength="1329.8"
clip-path="url(#breeze-help-line-85)">Tools that CI workflows use to cleanup/manage CI environment       &
[...]
-</text><text class="breeze-help-r5" x="0" y="2118.4" textLength="1464"
clip-path="url(#breeze-help-line-86)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2118.4" textLength="12.2"
clip-path="url(#breeze-help-line-86)">
-</text><text class="breeze-help-r5" x="0" y="2142.8" textLength="24.4"
clip-path="url(#breeze-help-line-87)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2142.8" textLength="231.8"
clip-path="url(#breeze-help-line-87)"> Registry commands </text><text
class="breeze-help-r5" x="256.2" y="2142.8" textLength="1183.4"
clip-path="url(#breeze-help-line-87)">─────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="br [...]
-</text><text class="breeze-help-r5" x="0" y="2167.2" textLength="12.2"
clip-path="url(#breeze-help-line-88)">│</text><text class="breeze-help-r4"
x="24.4" y="2167.2" textLength="256.2"
clip-path="url(#breeze-help-line-88)">registry             </text><text
class="breeze-help-r1" x="305" y="2167.2" textLength="1134.6"
clip-path="url(#breeze-help-line-88)">Tools for the Airflow Provider Registry 
[...]
-</text><text class="breeze-help-r5" x="0" y="2191.6" textLength="1464"
clip-path="url(#breeze-help-line-89)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2191.6" textLength="12.2"
clip-path="url(#breeze-help-line-89)">
-</text><text class="breeze-help-r5" x="0" y="2216" textLength="24.4"
clip-path="url(#breeze-help-line-90)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2216" textLength="158.6"
clip-path="url(#breeze-help-line-90)"> UI commands </text><text
class="breeze-help-r5" x="183" y="2216" textLength="1256.6"
clip-path="url(#breeze-help-line-90)">───────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-hel [...]
-</text><text class="breeze-help-r5" x="0" y="2240.4" textLength="12.2"
clip-path="url(#breeze-help-line-91)">│</text><text class="breeze-help-r4"
x="24.4" y="2240.4" textLength="85.4"
clip-path="url(#breeze-help-line-91)">ui     </text><text
class="breeze-help-r1" x="134.2" y="2240.4" textLength="1305.4"
clip-path="url(#breeze-help-line-91)">Tools for UI development and maintenance         
[...]
-</text><text class="breeze-help-r5" x="0" y="2264.8" textLength="1464"
clip-path="url(#breeze-help-line-92)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2264.8" textLength="12.2"
clip-path="url(#breeze-help-line-92)">
-</text><text class="breeze-help-r5" x="0" y="2289.2" textLength="24.4"
clip-path="url(#breeze-help-line-93)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2289.2" textLength="207.4"
clip-path="url(#breeze-help-line-93)"> Issues commands </text><text
class="breeze-help-r5" x="231.8" y="2289.2" textLength="1207.8"
clip-path="url(#breeze-help-line-93)">───────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="br [...]
-</text><text class="breeze-help-r5" x="0" y="2313.6" textLength="12.2"
clip-path="url(#breeze-help-line-94)">│</text><text class="breeze-help-r4"
x="24.4" y="2313.6" textLength="231.8"
clip-path="url(#breeze-help-line-94)">issues             </text><text
class="breeze-help-r1" x="280.6" y="2313.6" textLength="1159"
clip-path="url(#breeze-help-line-94)">Tools for managing GitHub issues.   &
[...]
-</text><text class="breeze-help-r5" x="0" y="2338" textLength="1464"
clip-path="url(#breeze-help-line-95)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2338" textLength="12.2"
clip-path="url(#breeze-help-line-95)">
-</text><text class="breeze-help-r5" x="0" y="2362.4" textLength="24.4"
clip-path="url(#breeze-help-line-96)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2362.4" textLength="158.6"
clip-path="url(#breeze-help-line-96)"> PR commands </text><text
class="breeze-help-r5" x="183" y="2362.4" textLength="1256.6"
clip-path="url(#breeze-help-line-96)">───────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
-</text><text class="breeze-help-r5" x="0" y="2386.8" textLength="12.2"
clip-path="url(#breeze-help-line-97)">│</text><text class="breeze-help-r4"
x="24.4" y="2386.8" textLength="85.4"
clip-path="url(#breeze-help-line-97)">pr     </text><text
class="breeze-help-r1" x="134.2" y="2386.8" textLength="1305.4"
clip-path="url(#breeze-help-line-97)">Tools for managing GitHub pull requests.         
[...]
-</text><text class="breeze-help-r5" x="0" y="2411.2" textLength="1464"
clip-path="url(#breeze-help-line-98)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2411.2" textLength="12.2"
clip-path="url(#breeze-help-line-98)">
-</text><text class="breeze-help-r5" x="0" y="2435.6" textLength="24.4"
clip-path="url(#breeze-help-line-99)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2435.6" textLength="195.2"
clip-path="url(#breeze-help-line-99)"> Setup commands </text><text
class="breeze-help-r5" x="219.6" y="2435.6" textLength="1220"
clip-path="url(#breeze-help-line-99)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
-</text><text class="breeze-help-r5" x="0" y="2460" textLength="12.2"
clip-path="url(#breeze-help-line-100)">│</text><text class="breeze-help-r4"
x="24.4" y="2460" textLength="146.4"
clip-path="url(#breeze-help-line-100)">setup       </text><text
class="breeze-help-r1" x="195.2" y="2460" textLength="1244.4"
clip-path="url(#breeze-help-line-100)">Tools that developers can use to configure Breeze    &#
[...]
-</text><text class="breeze-help-r5" x="0" y="2484.4" textLength="1464"
clip-path="url(#breeze-help-line-101)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2484.4" textLength="12.2"
clip-path="url(#breeze-help-line-101)">
+</text><text class="breeze-help-r5" x="0" y="1947.6" textLength="24.4"
clip-path="url(#breeze-help-line-79)">╭─</text><text class="breeze-help-r5"
x="24.4" y="1947.6" textLength="195.2"
clip-path="url(#breeze-help-line-79)"> Image commands </text><text
class="breeze-help-r5" x="219.6" y="1947.6" textLength="1220"
clip-path="url(#breeze-help-line-79)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
+</text><text class="breeze-help-r5" x="0" y="1972" textLength="12.2"
clip-path="url(#breeze-help-line-80)">│</text><text class="breeze-help-r4"
x="24.4" y="1972" textLength="207.4"
clip-path="url(#breeze-help-line-80)">ci-image         </text><text
class="breeze-help-r1" x="256.2" y="1972" textLength="1183.4"
clip-path="url(#breeze-help-line-80)">Tools that developers can use to manually manage CI i
[...]
+</text><text class="breeze-help-r5" x="0" y="1996.4" textLength="12.2"
clip-path="url(#breeze-help-line-81)">│</text><text class="breeze-help-r4"
x="24.4" y="1996.4" textLength="207.4"
clip-path="url(#breeze-help-line-81)">prod-image       </text><text
class="breeze-help-r1" x="256.2" y="1996.4" textLength="1183.4"
clip-path="url(#breeze-help-line-81)">Tools that developers can use to manually manage PROD ima
[...]
+</text><text class="breeze-help-r5" x="0" y="2020.8" textLength="1464"
clip-path="url(#breeze-help-line-82)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2020.8" textLength="12.2"
clip-path="url(#breeze-help-line-82)">
+</text><text class="breeze-help-r5" x="0" y="2045.2" textLength="24.4"
clip-path="url(#breeze-help-line-83)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2045.2" textLength="353.8"
clip-path="url(#breeze-help-line-83)"> Release management commands </text><text
class="breeze-help-r5" x="378.2" y="2045.2" textLength="1061.4"
clip-path="url(#breeze-help-line-83)">───────────────────────────────────────────────────────────────────────────────────────</text><text
clas [...]
+</text><text class="breeze-help-r5" x="0" y="2069.6" textLength="12.2"
clip-path="url(#breeze-help-line-84)">│</text><text class="breeze-help-r4"
x="24.4" y="2069.6" textLength="280.6"
clip-path="url(#breeze-help-line-84)">release-management     </text><text
class="breeze-help-r1" x="329.4" y="2069.6" textLength="1110.2"
clip-path="url(#breeze-help-line-84)">Tools that release managers can use to prepare and manage
[...]
+</text><text class="breeze-help-r5" x="0" y="2094" textLength="12.2"
clip-path="url(#breeze-help-line-85)">│</text><text class="breeze-help-r4"
x="24.4" y="2094" textLength="280.6"
clip-path="url(#breeze-help-line-85)">sbom                   </text><text
class="breeze-help-r1" x="329.4" y="2094" textLength="1110.2"
clip-path="url(#breeze-help-line-85)">Tools that release managers
[...]
+</text><text class="breeze-help-r5" x="0" y="2118.4" textLength="12.2"
clip-path="url(#breeze-help-line-86)">│</text><text class="breeze-help-r4"
x="24.4" y="2118.4" textLength="280.6"
clip-path="url(#breeze-help-line-86)">workflow-run           </text><text
class="breeze-help-r1" x="329.4" y="2118.4" textLength="1110.2"
clip-path="url(#breeze-help-line-86)">Tools to manage Airflow repository workflows &
[...]
+</text><text class="breeze-help-r5" x="0" y="2142.8" textLength="1464"
clip-path="url(#breeze-help-line-87)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2142.8" textLength="12.2"
clip-path="url(#breeze-help-line-87)">
+</text><text class="breeze-help-r5" x="0" y="2167.2" textLength="24.4"
clip-path="url(#breeze-help-line-88)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2167.2" textLength="158.6"
clip-path="url(#breeze-help-line-88)"> CI commands </text><text
class="breeze-help-r5" x="183" y="2167.2" textLength="1256.6"
clip-path="url(#breeze-help-line-88)">───────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
+</text><text class="breeze-help-r5" x="0" y="2191.6" textLength="12.2"
clip-path="url(#breeze-help-line-89)">│</text><text class="breeze-help-r4"
x="24.4" y="2191.6" textLength="61"
clip-path="url(#breeze-help-line-89)">ci   </text><text
class="breeze-help-r1" x="109.8" y="2191.6" textLength="1329.8"
clip-path="url(#breeze-help-line-89)">Tools that CI workflows use to cleanup/manage CI environment      &
[...]
+</text><text class="breeze-help-r5" x="0" y="2216" textLength="1464"
clip-path="url(#breeze-help-line-90)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2216" textLength="12.2"
clip-path="url(#breeze-help-line-90)">
+</text><text class="breeze-help-r5" x="0" y="2240.4" textLength="24.4"
clip-path="url(#breeze-help-line-91)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2240.4" textLength="231.8"
clip-path="url(#breeze-help-line-91)"> Registry commands </text><text
class="breeze-help-r5" x="256.2" y="2240.4" textLength="1183.4"
clip-path="url(#breeze-help-line-91)">─────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="br [...]
+</text><text class="breeze-help-r5" x="0" y="2264.8" textLength="12.2"
clip-path="url(#breeze-help-line-92)">│</text><text class="breeze-help-r4"
x="24.4" y="2264.8" textLength="256.2"
clip-path="url(#breeze-help-line-92)">registry             </text><text
class="breeze-help-r1" x="305" y="2264.8" textLength="1134.6"
clip-path="url(#breeze-help-line-92)">Tools for the Airflow Provider Registry 
[...]
+</text><text class="breeze-help-r5" x="0" y="2289.2" textLength="1464"
clip-path="url(#breeze-help-line-93)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2289.2" textLength="12.2"
clip-path="url(#breeze-help-line-93)">
+</text><text class="breeze-help-r5" x="0" y="2313.6" textLength="24.4"
clip-path="url(#breeze-help-line-94)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2313.6" textLength="158.6"
clip-path="url(#breeze-help-line-94)"> UI commands </text><text
class="breeze-help-r5" x="183" y="2313.6" textLength="1256.6"
clip-path="url(#breeze-help-line-94)">───────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="bree [...]
+</text><text class="breeze-help-r5" x="0" y="2338" textLength="12.2"
clip-path="url(#breeze-help-line-95)">│</text><text class="breeze-help-r4"
x="24.4" y="2338" textLength="85.4"
clip-path="url(#breeze-help-line-95)">ui     </text><text
class="breeze-help-r1" x="134.2" y="2338" textLength="1305.4"
clip-path="url(#breeze-help-line-95)">Tools for UI development and maintenance          
[...]
+</text><text class="breeze-help-r5" x="0" y="2362.4" textLength="1464"
clip-path="url(#breeze-help-line-96)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2362.4" textLength="12.2"
clip-path="url(#breeze-help-line-96)">
+</text><text class="breeze-help-r5" x="0" y="2386.8" textLength="24.4"
clip-path="url(#breeze-help-line-97)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2386.8" textLength="207.4"
clip-path="url(#breeze-help-line-97)"> Issues commands </text><text
class="breeze-help-r5" x="231.8" y="2386.8" textLength="1207.8"
clip-path="url(#breeze-help-line-97)">───────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="br [...]
+</text><text class="breeze-help-r5" x="0" y="2411.2" textLength="12.2"
clip-path="url(#breeze-help-line-98)">│</text><text class="breeze-help-r4"
x="24.4" y="2411.2" textLength="231.8"
clip-path="url(#breeze-help-line-98)">issues             </text><text
class="breeze-help-r1" x="280.6" y="2411.2" textLength="1159"
clip-path="url(#breeze-help-line-98)">Tools for managing GitHub issues.   &
[...]
+</text><text class="breeze-help-r5" x="0" y="2435.6" textLength="1464"
clip-path="url(#breeze-help-line-99)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2435.6" textLength="12.2"
clip-path="url(#breeze-help-line-99)">
+</text><text class="breeze-help-r5" x="0" y="2460" textLength="24.4"
clip-path="url(#breeze-help-line-100)">╭─</text><text class="breeze-help-r5"
x="24.4" y="2460" textLength="195.2"
clip-path="url(#breeze-help-line-100)"> Setup commands </text><text
class="breeze-help-r5" x="219.6" y="2460" textLength="1220"
clip-path="url(#breeze-help-line-100)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze- [...]
+</text><text class="breeze-help-r5" x="0" y="2484.4" textLength="12.2"
clip-path="url(#breeze-help-line-101)">│</text><text class="breeze-help-r4"
x="24.4" y="2484.4" textLength="146.4"
clip-path="url(#breeze-help-line-101)">setup       </text><text
class="breeze-help-r1" x="195.2" y="2484.4" textLength="1244.4"
clip-path="url(#breeze-help-line-101)">Tools that developers can use to configure Breeze   &#
[...]
+</text><text class="breeze-help-r5" x="0" y="2508.8" textLength="1464"
clip-path="url(#breeze-help-line-102)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-help-r1" x="1464" y="2508.8" textLength="12.2"
clip-path="url(#breeze-help-line-102)">
</text>
</g>
</g>
diff --git a/dev/breeze/doc/images/output_down.svg
b/dev/breeze/doc/images/output_down.svg
index 4375f66f8db..84dd611651f 100644
--- a/dev/breeze/doc/images/output_down.svg
+++ b/dev/breeze/doc/images/output_down.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 416.0"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 635.5999999999999"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -38,11 +38,12 @@
.breeze-down-r4 { fill: #68a0b3;font-weight: bold }
.breeze-down-r5 { fill: #868887 }
.breeze-down-r6 { fill: #98a84b;font-weight: bold }
+.breeze-down-r7 { fill: #8d7b39 }
</style>
<defs>
<clipPath id="breeze-down-clip-terminal">
- <rect x="0" y="0" width="1463.0" height="365.0" />
+ <rect x="0" y="0" width="1463.0" height="584.5999999999999" />
</clipPath>
<clipPath id="breeze-down-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -86,9 +87,36 @@
<clipPath id="breeze-down-line-13">
<rect x="0" y="318.7" width="1464" height="24.65"/>
</clipPath>
+<clipPath id="breeze-down-line-14">
+ <rect x="0" y="343.1" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-15">
+ <rect x="0" y="367.5" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-16">
+ <rect x="0" y="391.9" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-17">
+ <rect x="0" y="416.3" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-18">
+ <rect x="0" y="440.7" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-19">
+ <rect x="0" y="465.1" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-20">
+ <rect x="0" y="489.5" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-21">
+ <rect x="0" y="513.9" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-down-line-22">
+ <rect x="0" y="538.3" 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="414" rx="8"/><text class="breeze-down-title"
fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: down</text>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="633.6" rx="8"/><text class="breeze-down-title"
fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: down</text>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -101,18 +129,27 @@
<text class="breeze-down-r1" x="1464" y="20" textLength="12.2"
clip-path="url(#breeze-down-line-0)">
</text><text class="breeze-down-r2" x="12.2" y="44.4" textLength="73.2"
clip-path="url(#breeze-down-line-1)">Usage:</text><text class="breeze-down-r3"
x="97.6" y="44.4" textLength="134.2"
clip-path="url(#breeze-down-line-1)">breeze down</text><text
class="breeze-down-r1" x="244" y="44.4" textLength="12.2"
clip-path="url(#breeze-down-line-1)">[</text><text class="breeze-down-r4"
x="256.2" y="44.4" textLength="85.4"
clip-path="url(#breeze-down-line-1)">OPTIONS</text><text class="breez [...]
</text><text class="breeze-down-r1" x="1464" y="68.8" textLength="12.2"
clip-path="url(#breeze-down-line-2)">
-</text><text class="breeze-down-r1" x="12.2" y="93.2" textLength="390.4"
clip-path="url(#breeze-down-line-3)">Stop running breeze environment.</text><text
class="breeze-down-r1" x="1464" y="93.2" textLength="12.2"
clip-path="url(#breeze-down-line-3)">
-</text><text class="breeze-down-r1" x="1464" y="117.6" textLength="12.2"
clip-path="url(#breeze-down-line-4)">
-</text><text class="breeze-down-r5" x="0" y="142" textLength="24.4"
clip-path="url(#breeze-down-line-5)">╭─</text><text class="breeze-down-r5"
x="24.4" y="142" textLength="146.4"
clip-path="url(#breeze-down-line-5)"> Down flags </text><text
class="breeze-down-r5" x="170.8" y="142" textLength="1268.8"
clip-path="url(#breeze-down-line-5)">────────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-down-r5 [...]
-</text><text class="breeze-down-r5" x="0" y="166.4" textLength="12.2"
clip-path="url(#breeze-down-line-6)">│</text><text class="breeze-down-r4"
x="24.4" y="166.4" textLength="256.2"
clip-path="url(#breeze-down-line-6)">--preserve-volumes   </text><text
class="breeze-down-r6" x="305" y="166.4" textLength="24.4"
clip-path="url(#breeze-down-line-6)">-p</text><text class="breeze-down-r1"
x="353.8" y="166.4" textLength="634.4"
clip-path="url(#breeze-down-line-6)">Skip remo [...]
-</text><text class="breeze-down-r5" x="0" y="190.8" textLength="12.2"
clip-path="url(#breeze-down-line-7)">│</text><text class="breeze-down-r4"
x="24.4" y="190.8" textLength="256.2"
clip-path="url(#breeze-down-line-7)">--cleanup-mypy-cache </text><text
class="breeze-down-r6" x="305" y="190.8" textLength="24.4"
clip-path="url(#breeze-down-line-7)">-c</text><text class="breeze-down-r1"
x="353.8" y="190.8" textLength="390.4"
clip-path="url(#breeze-down-line-7)">Additionally cleanu [...]
-</text><text class="breeze-down-r5" x="0" y="215.2" textLength="12.2"
clip-path="url(#breeze-down-line-8)">│</text><text class="breeze-down-r4"
x="24.4" y="215.2" textLength="256.2"
clip-path="url(#breeze-down-line-8)">--cleanup-build-cache</text><text
class="breeze-down-r6" x="305" y="215.2" textLength="24.4"
clip-path="url(#breeze-down-line-8)">-b</text><text class="breeze-down-r1"
x="353.8" y="215.2" textLength="512.4"
clip-path="url(#breeze-down-line-8)">Additionally cleanup [...]
-</text><text class="breeze-down-r5" x="0" y="239.6" textLength="1464"
clip-path="url(#breeze-down-line-9)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-down-r1" x="1464" y="239.6" textLength="12.2"
clip-path="url(#breeze-down-line-9)">
-</text><text class="breeze-down-r5" x="0" y="264" textLength="24.4"
clip-path="url(#breeze-down-line-10)">╭─</text><text class="breeze-down-r5"
x="24.4" y="264" textLength="195.2"
clip-path="url(#breeze-down-line-10)"> Common options </text><text
class="breeze-down-r5" x="219.6" y="264" textLength="1220"
clip-path="url(#breeze-down-line-10)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-down-r [...]
-</text><text class="breeze-down-r5" x="0" y="288.4" textLength="12.2"
clip-path="url(#breeze-down-line-11)">│</text><text class="breeze-down-r4"
x="24.4" y="288.4" textLength="109.8"
clip-path="url(#breeze-down-line-11)">--verbose</text><text
class="breeze-down-r6" x="158.6" y="288.4" textLength="24.4"
clip-path="url(#breeze-down-line-11)">-v</text><text class="breeze-down-r1"
x="207.4" y="288.4" textLength="585.6"
clip-path="url(#breeze-down-line-11)">Print verbose information [...]
-</text><text class="breeze-down-r5" x="0" y="312.8" textLength="12.2"
clip-path="url(#breeze-down-line-12)">│</text><text class="breeze-down-r4"
x="24.4" y="312.8" textLength="109.8"
clip-path="url(#breeze-down-line-12)">--dry-run</text><text
class="breeze-down-r6" x="158.6" y="312.8" textLength="24.4"
clip-path="url(#breeze-down-line-12)">-D</text><text class="breeze-down-r1"
x="207.4" y="312.8" textLength="719.8"
clip-path="url(#breeze-down-line-12)">If dry-run is set,&# [...]
-</text><text class="breeze-down-r5" x="0" y="337.2" textLength="12.2"
clip-path="url(#breeze-down-line-13)">│</text><text class="breeze-down-r4"
x="24.4" y="337.2" textLength="109.8"
clip-path="url(#breeze-down-line-13)">--help   </text><text
class="breeze-down-r6" x="158.6" y="337.2" textLength="24.4"
clip-path="url(#breeze-down-line-13)">-h</text><text class="breeze-down-r1"
x="207.4" y="337.2" textLength="329.4"
clip-path="url(#breeze-down-line-13)">Show this  [...]
-</text><text class="breeze-down-r5" x="0" y="361.6" textLength="1464"
clip-path="url(#breeze-down-line-14)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-down-r1" x="1464" y="361.6" textLength="12.2"
clip-path="url(#breeze-down-line-14)">
+</text><text class="breeze-down-r1" x="12.2" y="93.2" textLength="1439.6"
clip-path="url(#breeze-down-line-3)">Stop every docker compose project breeze knows about. Discovers running projects via the `com.docker.compose.project` </text><text
class="breeze-down-r1" x="1464" y="93.2" textLength="12.2"
clip-path="url(#breeze-down-line-3)">
+</text><text class="breeze-down-r1" x="12.2" y="117.6" textLength="451.4"
clip-path="url(#breeze-down-line-4)">label and brings each one down with `</text><text
class="breeze-down-r4" x="463.6" y="117.6" textLength="195.2"
clip-path="url(#breeze-down-line-4)">--remove-orphans</text><text
class="breeze-down-r1" x="658.8" y="117.6" textLength="97.6"
clip-path="url(#breeze-down-line-4)">` (and `</text><text
class="breeze-down-r4" x="756.4" y="117 [...]
+</text><text class="breeze-down-r1" x="12.2" y="142" textLength="1366.4"
clip-path="url(#breeze-down-line-5)">`breeze shell`, `breeze testing`, `breeze build-docs`, `breeze db`, release-management, registry, and prek-hook </text><text
class="breeze-down-r1" x="1464" y="142" textLength="12.2"
clip-path="url(#breeze-down-line-5)">
+</text><text class="breeze-down-r1" x="12.2" y="166.4" textLength="451.4"
clip-path="url(#breeze-down-line-6)">compose projects in a single command.</text><text
class="breeze-down-r1" x="1464" y="166.4" textLength="12.2"
clip-path="url(#breeze-down-line-6)">
+</text><text class="breeze-down-r1" x="1464" y="190.8" textLength="12.2"
clip-path="url(#breeze-down-line-7)">
+</text><text class="breeze-down-r5" x="0" y="215.2" textLength="24.4"
clip-path="url(#breeze-down-line-8)">╭─</text><text class="breeze-down-r5"
x="24.4" y="215.2" textLength="146.4"
clip-path="url(#breeze-down-line-8)"> Down flags </text><text
class="breeze-down-r5" x="170.8" y="215.2" textLength="1268.8"
clip-path="url(#breeze-down-line-8)">────────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze-d [...]
+</text><text class="breeze-down-r5" x="0" y="239.6" textLength="12.2"
clip-path="url(#breeze-down-line-9)">│</text><text class="breeze-down-r4"
x="24.4" y="239.6" textLength="256.2"
clip-path="url(#breeze-down-line-9)">--preserve-volumes   </text><text
class="breeze-down-r6" x="305" y="239.6" textLength="24.4"
clip-path="url(#breeze-down-line-9)">-p</text><text class="breeze-down-r1"
x="353.8" y="239.6" textLength="634.4"
clip-path="url(#breeze-down-line-9)">Skip remo [...]
+</text><text class="breeze-down-r5" x="0" y="264" textLength="12.2"
clip-path="url(#breeze-down-line-10)">│</text><text class="breeze-down-r4"
x="24.4" y="264" textLength="256.2"
clip-path="url(#breeze-down-line-10)">--cleanup-mypy-cache </text><text
class="breeze-down-r6" x="305" y="264" textLength="24.4"
clip-path="url(#breeze-down-line-10)">-c</text><text class="breeze-down-r1"
x="353.8" y="264" textLength="390.4"
clip-path="url(#breeze-down-line-10)">Additionally cleanup [...]
+</text><text class="breeze-down-r5" x="0" y="288.4" textLength="12.2"
clip-path="url(#breeze-down-line-11)">│</text><text class="breeze-down-r4"
x="24.4" y="288.4" textLength="256.2"
clip-path="url(#breeze-down-line-11)">--cleanup-build-cache</text><text
class="breeze-down-r6" x="305" y="288.4" textLength="24.4"
clip-path="url(#breeze-down-line-11)">-b</text><text class="breeze-down-r1"
x="353.8" y="288.4" textLength="512.4"
clip-path="url(#breeze-down-line-11)">Additionally cleanup [...]
+</text><text class="breeze-down-r5" x="0" y="312.8" textLength="1464"
clip-path="url(#breeze-down-line-12)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-down-r1" x="1464" y="312.8" textLength="12.2"
clip-path="url(#breeze-down-line-12)">
+</text><text class="breeze-down-r5" x="0" y="337.2" textLength="24.4"
clip-path="url(#breeze-down-line-13)">╭─</text><text class="breeze-down-r5"
x="24.4" y="337.2" textLength="231.8"
clip-path="url(#breeze-down-line-13)"> Project selection </text><text
class="breeze-down-r5" x="256.2" y="337.2" textLength="1183.4"
clip-path="url(#breeze-down-line-13)">─────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breez [...]
+</text><text class="breeze-down-r5" x="0" y="361.6" textLength="12.2"
clip-path="url(#breeze-down-line-14)">│</text><text class="breeze-down-r4"
x="24.4" y="361.6" textLength="170.8"
clip-path="url(#breeze-down-line-14)">--all-projects</text><text
class="breeze-down-r1" x="219.6" y="361.6" textLength="1220"
clip-path="url(#breeze-down-line-14)">Also bring down docker compose projects whose names do not match any known breez
[...]
+</text><text class="breeze-down-r5" x="0" y="386" textLength="12.2"
clip-path="url(#breeze-down-line-15)">│</text><text class="breeze-down-r1"
x="219.6" y="386" textLength="1220"
clip-path="url(#breeze-down-line-15)">default to avoid touching unrelated projects on the host.                             
[...]
+</text><text class="breeze-down-r5" x="0" y="410.4" textLength="12.2"
clip-path="url(#breeze-down-line-16)">│</text><text class="breeze-down-r4"
x="24.4" y="410.4" textLength="170.8"
clip-path="url(#breeze-down-line-16)">--project-name</text><text
class="breeze-down-r1" x="219.6" y="410.4" textLength="1220"
clip-path="url(#breeze-down-line-16)">Restrict the cleanup to a single docker compose project name and skip discovery. 
[...]
+</text><text class="breeze-down-r5" x="0" y="434.8" textLength="12.2"
clip-path="url(#breeze-down-line-17)">│</text><text class="breeze-down-r1"
x="219.6" y="434.8" textLength="549"
clip-path="url(#breeze-down-line-17)">that want to bring exactly one project down. </text><text
class="breeze-down-r7" x="768.6" y="434.8" textLength="73.2"
clip-path="url(#breeze-down-line-17)">(TEXT)</text><text class="breeze-down-r5"
x="1451.8" y="434.8" textLength=" [...]
+</text><text class="breeze-down-r5" x="0" y="459.2" textLength="1464"
clip-path="url(#breeze-down-line-18)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-down-r1" x="1464" y="459.2" textLength="12.2"
clip-path="url(#breeze-down-line-18)">
+</text><text class="breeze-down-r5" x="0" y="483.6" textLength="24.4"
clip-path="url(#breeze-down-line-19)">╭─</text><text class="breeze-down-r5"
x="24.4" y="483.6" textLength="195.2"
clip-path="url(#breeze-down-line-19)"> Common options </text><text
class="breeze-down-r5" x="219.6" y="483.6" textLength="1220"
clip-path="url(#breeze-down-line-19)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text
class="breeze- [...]
+</text><text class="breeze-down-r5" x="0" y="508" textLength="12.2"
clip-path="url(#breeze-down-line-20)">│</text><text class="breeze-down-r4"
x="24.4" y="508" textLength="109.8"
clip-path="url(#breeze-down-line-20)">--verbose</text><text
class="breeze-down-r6" x="158.6" y="508" textLength="24.4"
clip-path="url(#breeze-down-line-20)">-v</text><text class="breeze-down-r1"
x="207.4" y="508" textLength="585.6"
clip-path="url(#breeze-down-line-20)">Print verbose information ab
[...]
+</text><text class="breeze-down-r5" x="0" y="532.4" textLength="12.2"
clip-path="url(#breeze-down-line-21)">│</text><text class="breeze-down-r4"
x="24.4" y="532.4" textLength="109.8"
clip-path="url(#breeze-down-line-21)">--dry-run</text><text
class="breeze-down-r6" x="158.6" y="532.4" textLength="24.4"
clip-path="url(#breeze-down-line-21)">-D</text><text class="breeze-down-r1"
x="207.4" y="532.4" textLength="719.8"
clip-path="url(#breeze-down-line-21)">If dry-run is set,&# [...]
+</text><text class="breeze-down-r5" x="0" y="556.8" textLength="12.2"
clip-path="url(#breeze-down-line-22)">│</text><text class="breeze-down-r4"
x="24.4" y="556.8" textLength="109.8"
clip-path="url(#breeze-down-line-22)">--help   </text><text
class="breeze-down-r6" x="158.6" y="556.8" textLength="24.4"
clip-path="url(#breeze-down-line-22)">-h</text><text class="breeze-down-r1"
x="207.4" y="556.8" textLength="329.4"
clip-path="url(#breeze-down-line-22)">Show this  [...]
+</text><text class="breeze-down-r5" x="0" y="581.2" textLength="1464"
clip-path="url(#breeze-down-line-23)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-down-r1" x="1464" y="581.2" textLength="12.2"
clip-path="url(#breeze-down-line-23)">
</text>
</g>
</g>
diff --git a/dev/breeze/doc/images/output_down.txt
b/dev/breeze/doc/images/output_down.txt
index c0200cc3228..9c1280841b6 100644
--- a/dev/breeze/doc/images/output_down.txt
+++ b/dev/breeze/doc/images/output_down.txt
@@ -1 +1 @@
-5c5712f40f3397d077afce24bc6b5d4d
+cde032283bb58d1cf97ee92f145f1ea8
diff --git a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
index afb22da6355..d1d9b30d269 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
@@ -113,6 +113,7 @@ from airflow_breeze.params.shell_params import ShellParams
from airflow_breeze.utils.confirm import Answer, user_confirm
from airflow_breeze.utils.console import console_print
from airflow_breeze.utils.docker_command_utils import (
+ bring_all_compose_projects_down,
bring_compose_project_down,
check_docker_resources,
enter_shell,
@@ -904,7 +905,17 @@ def build_docs(
sys.exit(result.returncode)
[email protected](name="down", help="Stop running breeze environment.")
[email protected](
+ name="down",
+ help=(
+ "Stop every docker compose project breeze knows about. Discovers
running "
+ "projects via the `com.docker.compose.project` label and brings each
one "
+ "down with `--remove-orphans` (and `--volumes` unless
`--preserve-volumes` "
+ "is passed). Covers `breeze shell`, `breeze testing`, `breeze
build-docs`, "
+ "`breeze db`, release-management, registry, and prek-hook compose
projects "
+ "in a single command."
+ ),
+)
@click.option(
"-p",
"--preserve-volumes",
@@ -923,12 +934,46 @@ def build_docs(
help="Additionally cleanup Build (pip/uv) cache.",
is_flag=True,
)
[email protected](
+ "--all-projects",
+ help=(
+ "Also bring down docker compose projects whose names do not match any
known "
+ "breeze prefix. Off by default to avoid touching unrelated projects on
the host."
+ ),
+ is_flag=True,
+)
[email protected](
+ "--project-name",
+ help=(
+ "Restrict the cleanup to a single docker compose project name and skip
"
+ "discovery. Useful in CI steps that want to bring exactly one project
down."
+ ),
+ default=None,
+)
@option_verbose
@option_dry_run
-def down(preserve_volumes: bool, cleanup_mypy_cache: bool,
cleanup_build_cache: bool):
+def down(
+ preserve_volumes: bool,
+ cleanup_mypy_cache: bool,
+ cleanup_build_cache: bool,
+ all_projects: bool,
+ project_name: str | None,
+):
perform_environment_checks()
- shell_params = ShellParams(backend="all",
include_mypy_volume=cleanup_mypy_cache)
- bring_compose_project_down(preserve_volumes=preserve_volumes,
shell_params=shell_params)
+ brought_down, skipped = bring_all_compose_projects_down(
+ preserve_volumes=preserve_volumes,
+ include_unknown=all_projects,
+ only_project=project_name,
+ )
+ if not brought_down and not project_name:
+ console_print("[info]No running breeze-managed docker compose projects
found.[/]")
+ elif brought_down:
+ console_print(f"[success]Brought down {len(brought_down)} compose
project(s): {brought_down}[/]")
+ if skipped:
+ console_print(
+ f"[warning]Left {len(skipped)} unrelated compose project(s)
running: {skipped}\n"
+ f"Use `breeze down --all-projects` to also bring those down.[/]"
+ )
if cleanup_mypy_cache:
command_to_execute = ["docker", "volume", "rm", "--force",
"mypy-cache-volume"]
run_command(command_to_execute)
@@ -1071,8 +1116,9 @@ def doctor(ctx):
if not get_dry_run() and given_answer == Answer.YES:
cleanup_python_generated_files()
- shell_params = ShellParams(backend="all", include_mypy_volume=True)
- bring_compose_project_down(preserve_volumes=False,
shell_params=shell_params)
+ # Doctor is the heal-everything command, so it sweeps EVERY compose project
+ # on the host (not only the known-prefix ones that `breeze down` defaults
to).
+ bring_all_compose_projects_down(preserve_volumes=False,
include_unknown=True)
given_answer = user_confirm("Are you sure with the removal of mypy cache
and build cache dir?")
if given_answer == Answer.YES:
@@ -1178,7 +1224,6 @@ def run(
from airflow_breeze.params.shell_params import ShellParams
from airflow_breeze.utils.ci_group import ci_group
from airflow_breeze.utils.docker_command_utils import (
- bring_compose_project_down,
execute_command_in_shell,
fix_ownership_using_docker,
remove_docker_networks,
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 73059f22a06..74ca92841a1 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands_config.py
@@ -347,6 +347,13 @@ DEVELOPER_PARAMETERS: dict[str, list[dict[str, str |
list[str]]]] = {
"--cleanup-build-cache",
],
},
+ {
+ "name": "Project selection",
+ "options": [
+ "--all-projects",
+ "--project-name",
+ ],
+ },
],
"breeze build-docs": [
{
diff --git a/dev/breeze/src/airflow_breeze/commands/registry_commands.py
b/dev/breeze/src/airflow_breeze/commands/registry_commands.py
index 900ce65f2de..70cb6949734 100644
--- a/dev/breeze/src/airflow_breeze/commands/registry_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/registry_commands.py
@@ -31,7 +31,12 @@ from airflow_breeze.commands.common_options import
option_dry_run, option_python
from airflow_breeze.params.shell_params import ShellParams
from airflow_breeze.utils.ci_group import ci_group
from airflow_breeze.utils.click_utils import BreezeGroup
-from airflow_breeze.utils.docker_command_utils import
execute_command_in_shell, fix_ownership_using_docker
+from airflow_breeze.utils.docker_command_utils import (
+ bring_compose_project_down,
+ execute_command_in_shell,
+ fix_ownership_using_docker,
+ remove_docker_networks,
+)
from airflow_breeze.utils.path_utils import AIRFLOW_ROOT_PATH
from airflow_breeze.utils.run_utils import run_command
@@ -111,15 +116,18 @@ def extract_data(python: str, provider: str | None,
allow_unreleased: bool):
f"python dev/registry/extract_connections.py{provider_flag}"
)
- with ci_group("Extracting registry data"):
- result = execute_command_in_shell(
- shell_params=shell_params,
- project_name=unique_project_name,
- command=command,
- preserve_backend=True,
- )
-
- fix_ownership_using_docker()
+ try:
+ with ci_group("Extracting registry data"):
+ result = execute_command_in_shell(
+ shell_params=shell_params,
+ project_name=unique_project_name,
+ command=command,
+ preserve_backend=True,
+ )
+ finally:
+ bring_compose_project_down(preserve_volumes=False,
shell_params=shell_params)
+ remove_docker_networks([f"{unique_project_name}_default"])
+ fix_ownership_using_docker()
sys.exit(result.returncode)
@@ -336,6 +344,8 @@ def _backfill_docker(
failed.append(f"{version}/docker-extraction")
finally:
shutil.rmtree(backfill_tmp_dir, ignore_errors=True)
+ bring_compose_project_down(preserve_volumes=False,
shell_params=shell_params)
+ remove_docker_networks([f"{unique_project_name}_default"])
fix_ownership_using_docker()
return failed
diff --git a/dev/breeze/src/airflow_breeze/global_constants.py
b/dev/breeze/src/airflow_breeze/global_constants.py
index ad0bcc4c215..bd7155f8e04 100644
--- a/dev/breeze/src/airflow_breeze/global_constants.py
+++ b/dev/breeze/src/airflow_breeze/global_constants.py
@@ -146,6 +146,28 @@ AUTOCOMPLETE_ALL_INTEGRATIONS = sorted(
ALLOWED_TTY = ["auto", "enabled", "disabled"]
ALLOWED_TERMINAL_MULTIPLEXERS = ["mprocs", "tmux"]
ALLOWED_DOCKER_COMPOSE_PROJECTS = ["breeze", "prek", "docker-compose"]
+
+# Every docker compose project name that any breeze command, prek hook, or
+# CI workflow uses. `breeze down` discovers running compose projects via the
+# `com.docker.compose.project` label and only touches the ones that match
+# either an exact entry in `KNOWN_DOCKER_COMPOSE_PROJECT_NAMES` or one of the
+# prefixes in `KNOWN_DOCKER_COMPOSE_PROJECT_PREFIXES`. When you add a new
+# project_name pattern anywhere (new breeze command, new prek hook, new CI
+# step), update this list so `breeze down` stays a one-shot cleanup.
+KNOWN_DOCKER_COMPOSE_PROJECT_NAMES = [
+ "breeze", # default `breeze shell` / `breeze start-airflow`
+ "prek", # prek hooks (see scripts/ci/prek/common_prek_utils.py)
+ "docker-compose", # legacy name kept for migration_tests CI
+ "docs", # `breeze build-docs`
+ "db", # `breeze db ...`
+ "providers", # release-management providers builds
+]
+KNOWN_DOCKER_COMPOSE_PROJECT_PREFIXES = [
+ "breeze-", # breeze-registry-*, breeze-backfill-*, *-run-*
+ "airflow-test", # airflow-test, airflow-test-<test-type>
+ "constraints-", # constraints-<python-version>
+ "providers-", # providers-<index> (parallel provider builds)
+]
ALLOWED_LOG_LEVELS = ["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"]
DEFAULT_LOG_LEVEL = ALLOWED_LOG_LEVELS[0]
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 4cb8356169b..d95ae33fc1f 100644
--- a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
+++ b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
@@ -54,6 +54,8 @@ from airflow_breeze.global_constants import (
ALLOWED_DEBIAN_VERSIONS,
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
DOCKER_DEFAULT_PLATFORM,
+ KNOWN_DOCKER_COMPOSE_PROJECT_NAMES,
+ KNOWN_DOCKER_COMPOSE_PROJECT_PREFIXES,
MIN_DOCKER_COMPOSE_VERSION,
MIN_DOCKER_VERSION,
)
@@ -833,6 +835,83 @@ def bring_compose_project_down(preserve_volumes: bool,
shell_params: ShellParams
)
+def discover_running_compose_projects() -> set[str]:
+ """
+ Return the set of compose project names of every container on the host.
+
+ Reads the ``com.docker.compose.project`` label that ``docker compose``
+ sets on every container/network/volume it creates. Returns an empty set
+ if docker is unreachable or no compose-managed containers exist.
+ """
+ result = run_command(
+ [
+ "docker",
+ "ps",
+ "--all",
+ "--filter",
+ "label=com.docker.compose.project",
+ "--format",
+ '{{ index .Labels "com.docker.compose.project" }}',
+ ],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ if result.returncode != 0 or not result.stdout:
+ return set()
+ return {line.strip() for line in result.stdout.splitlines() if
line.strip()}
+
+
+def is_known_breeze_compose_project(name: str) -> bool:
+ """Return True if ``name`` matches a project breeze knows it owns."""
+ if name in KNOWN_DOCKER_COMPOSE_PROJECT_NAMES:
+ return True
+ return any(name.startswith(prefix) for prefix in
KNOWN_DOCKER_COMPOSE_PROJECT_PREFIXES)
+
+
+def bring_all_compose_projects_down(
+ *,
+ preserve_volumes: bool = False,
+ include_unknown: bool = False,
+ only_project: str | None = None,
+) -> tuple[list[str], list[str]]:
+ """
+ Discover and bring down every docker compose project breeze manages.
+
+ :param preserve_volumes: if True, pass ``--volumes`` is omitted so DB
+ volumes survive (matches the existing ``--preserve-volumes`` flag
+ on ``breeze down``).
+ :param include_unknown: if True, also bring down projects whose names
+ do not match any known breeze prefix. Useful as an emergency
+ cleanup; can wipe out unrelated docker compose projects on the
+ host, so off by default.
+ :param only_project: if set, restrict to exactly this project name and
+ skip discovery entirely (for the ``--project-name`` flag).
+ :returns: ``(brought_down, skipped)`` lists of project names, both
+ sorted, suitable for printing in a session summary.
+ """
+ if only_project:
+ targets = {only_project}
+ skipped: set[str] = set()
+ else:
+ running = discover_running_compose_projects()
+ if include_unknown:
+ targets = running
+ skipped = set()
+ else:
+ targets = {name for name in running if
is_known_breeze_compose_project(name)}
+ skipped = running - targets
+ brought_down: list[str] = []
+ for name in sorted(targets):
+ console_print(f"[info]Bringing down docker compose project: {name}[/]")
+ cmd = ["docker", "compose", "--project-name", name, "down",
"--remove-orphans"]
+ if not preserve_volumes:
+ cmd.append("--volumes")
+ run_command(cmd, text=True, check=False)
+ brought_down.append(name)
+ return brought_down, sorted(skipped)
+
+
def execute_command_in_shell(
shell_params: ShellParams,
project_name: str,
diff --git a/dev/breeze/tests/test_docker_command_utils.py
b/dev/breeze/tests/test_docker_command_utils.py
index 7c832813186..f08862e047d 100644
--- a/dev/breeze/tests/test_docker_command_utils.py
+++ b/dev/breeze/tests/test_docker_command_utils.py
@@ -24,8 +24,11 @@ import pytest
from airflow_breeze.utils.docker_command_utils import (
autodetect_docker_context,
+ bring_all_compose_projects_down,
check_docker_compose_version,
check_docker_version,
+ discover_running_compose_projects,
+ is_known_breeze_compose_project,
)
@@ -292,3 +295,97 @@ SOCKET_INFO_DESKTOP_LINUX = json.dumps(
}
]
)
+
+
[email protected](
+ ("name", "expected"),
+ [
+ ("breeze", True),
+ ("prek", True),
+ ("docker-compose", True),
+ ("docs", True),
+ ("db", True),
+ ("providers", True),
+ ("breeze-registry-abcd1234", True),
+ ("breeze-backfill-deadbeef", True),
+ ("breeze-run-12345678", True),
+ ("airflow-test", True),
+ ("airflow-test-providers-google", True),
+ ("constraints-3-12", True),
+ ("providers-7", True),
+ ("my-other-project", False),
+ ("airflow", False),
+ ("doc", False),
+ ("", False),
+ ],
+)
+def test_is_known_breeze_compose_project(name, expected):
+ assert is_known_breeze_compose_project(name) is expected
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
+def
test_discover_running_compose_projects_parses_label_output(mock_run_command):
+ mock_run_command.return_value = mock.Mock(
+ returncode=0,
+ stdout="breeze\nbreeze\nairflow-test-providers-amazon\n \n",
+ )
+ assert discover_running_compose_projects() == {"breeze",
"airflow-test-providers-amazon"}
+ cmd = mock_run_command.call_args.args[0]
+ assert cmd[:2] == ["docker", "ps"]
+ assert "label=com.docker.compose.project" in cmd
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
+def
test_discover_running_compose_projects_returns_empty_on_failure(mock_run_command):
+ mock_run_command.return_value = mock.Mock(returncode=1, stdout="")
+ assert discover_running_compose_projects() == set()
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.console_print")
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
[email protected]("airflow_breeze.utils.docker_command_utils.discover_running_compose_projects")
+def test_bring_all_compose_projects_down_filters_unknown_by_default(
+ mock_discover, mock_run_command, _mock_console
+):
+ mock_discover.return_value = {"breeze", "providers-3", "my-app"}
+ brought_down, skipped = bring_all_compose_projects_down()
+ assert brought_down == ["breeze", "providers-3"]
+ assert skipped == ["my-app"]
+ down_calls = [c for c in mock_run_command.call_args_list if c.args[0][:2]
== ["docker", "compose"]]
+ assert len(down_calls) == 2
+ for c in down_calls:
+ assert "--volumes" in c.args[0]
+ assert "--remove-orphans" in c.args[0]
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.console_print")
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
[email protected]("airflow_breeze.utils.docker_command_utils.discover_running_compose_projects")
+def test_bring_all_compose_projects_down_include_unknown(mock_discover,
mock_run_command, _mock_console):
+ mock_discover.return_value = {"breeze", "my-app"}
+ brought_down, skipped =
bring_all_compose_projects_down(include_unknown=True)
+ assert brought_down == ["breeze", "my-app"]
+ assert skipped == []
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.console_print")
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
[email protected]("airflow_breeze.utils.docker_command_utils.discover_running_compose_projects")
+def test_bring_all_compose_projects_down_only_project_skips_discovery(
+ mock_discover, mock_run_command, _mock_console
+):
+ brought_down, skipped =
bring_all_compose_projects_down(only_project="my-app")
+ assert brought_down == ["my-app"]
+ assert skipped == []
+ mock_discover.assert_not_called()
+
+
[email protected]("airflow_breeze.utils.docker_command_utils.console_print")
[email protected]("airflow_breeze.utils.docker_command_utils.run_command")
[email protected]("airflow_breeze.utils.docker_command_utils.discover_running_compose_projects")
+def test_bring_all_compose_projects_down_preserve_volumes(mock_discover,
mock_run_command, _mock_console):
+ mock_discover.return_value = {"breeze"}
+ bring_all_compose_projects_down(preserve_volumes=True)
+ down_call = next(c for c in mock_run_command.call_args_list if
c.args[0][:2] == ["docker", "compose"])
+ assert "--volumes" not in down_call.args[0]
+ assert "--remove-orphans" in down_call.args[0]
diff --git a/scripts/ci/prek/common_prek_utils.py
b/scripts/ci/prek/common_prek_utils.py
index f28923b78bc..45f38b5d3c6 100644
--- a/scripts/ci/prek/common_prek_utils.py
+++ b/scripts/ci/prek/common_prek_utils.py
@@ -374,19 +374,24 @@ def run_command_via_breeze_shell(
print(f"Running command: {' '.join([shlex.quote(item) for item in
subprocess_cmd])}")
print("With environment:")
print(new_env)
- result = subprocess.run(
- subprocess_cmd,
- check=False,
- text=True,
- **other_popen_kwargs,
- env=new_env,
- )
- # Stop remaining containers
- down_command = ["docker", "compose", "--progress", "quiet"]
- if project_name:
- down_command.extend(["--project-name", project_name])
- down_command.extend(["down", "--remove-orphans"])
- subprocess.run(down_command, check=False, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
+ try:
+ result = subprocess.run(
+ subprocess_cmd,
+ check=False,
+ text=True,
+ **other_popen_kwargs,
+ env=new_env,
+ )
+ finally:
+ # Always clean up containers, networks, and volumes the breeze shell
+ # invocation created — even if the subprocess raised
(KeyboardInterrupt,
+ # OSError, etc.). Without --volumes the next prek run inherits state
+ # from the previous run, which is the bug this finally clause prevents.
+ down_command = ["docker", "compose", "--progress", "quiet"]
+ if project_name:
+ down_command.extend(["--project-name", project_name])
+ down_command.extend(["down", "--remove-orphans", "--volumes"])
+ subprocess.run(down_command, check=False, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return result