This is an automated email from the ASF dual-hosted git repository.
potiuk 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 5dfee8b992 Replace architecture diagram of Airflow with
diagrams-generated one (#36035)
5dfee8b992 is described below
commit 5dfee8b992e23370937c28052ebca231c361da71
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Dec 5 21:15:16 2023 +0100
Replace architecture diagram of Airflow with diagrams-generated one (#36035)
The architecture diagram of Airflow has been long time outdated.
This is an attempt to generate it using generated diagrams using
Python's diagrams library (already used by some tools in our
ecosystem).
---
.pre-commit-config.yaml | 7 ++
STATIC_CODE_CHECKS.rst | 2 +
dev/breeze/src/airflow_breeze/pre_commit_ids.py | 1 +
docs/apache-airflow/core-concepts/overview.rst | 21 +++-
docs/apache-airflow/img/arch-diag-basic.png | Bin 16357 -> 0 bytes
.../img/diagram_basic_airflow_architecture.png | Bin 0 -> 87096 bytes
.../diagram_dag_processor_airflow_architecture.png | Bin 0 -> 106642 bytes
docs/apache-airflow/security/security_model.rst | 22 +++-
images/breeze/output_static-checks.svg | 100 ++++++++-------
images/breeze/output_static-checks.txt | 2 +-
images/diagrams/python_multiprocess_logo.png | Bin 0 -> 210513 bytes
scripts/ci/pre_commit/.gitignore | 1 +
scripts/ci/pre_commit/diagram_hash.txt | 1 +
.../pre_commit_generate_airflow_diagrams.py | 139 +++++++++++++++++++++
14 files changed, 241 insertions(+), 55 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 80d5136097..8f635c1960 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -405,6 +405,13 @@ repos:
files: ^Dockerfile$
pass_filenames: false
additional_dependencies: ['rich>=12.4.4']
+ - id: generate-airflow-diagrams
+ name: Generate airflow diagrams
+ entry: ./scripts/ci/pre_commit/pre_commit_generate_airflow_diagrams.py
+ language: python
+ files: ^scripts/ci/pre_commit/pre_commit_generate_airflow_diagrams.py
+ pass_filenames: false
+ additional_dependencies: ['rich>=12.4.4', "diagrams>=0.23.4"]
- id: update-supported-versions
name: Updates supported versions in documentation
entry: ./scripts/ci/pre_commit/pre_commit_supported_versions.py
diff --git a/STATIC_CODE_CHECKS.rst b/STATIC_CODE_CHECKS.rst
index 437bd48ffc..0c064ef5fd 100644
--- a/STATIC_CODE_CHECKS.rst
+++ b/STATIC_CODE_CHECKS.rst
@@ -272,6 +272,8 @@ require Breeze Docker image to be built locally.
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
| flynt | Run flynt string
format converter for Python | |
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
+| generate-airflow-diagrams | Generate airflow
diagrams | |
++-----------------------------------------------------------+--------------------------------------------------------------+---------+
| generate-pypi-readme | Generate PyPI
README | |
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
| identity | Print input to
the static check hooks for troubleshooting | |
diff --git a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
index 252bbaa558..9725087609 100644
--- a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
+++ b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
@@ -89,6 +89,7 @@ PRE_COMMIT_LIST = [
"end-of-file-fixer",
"fix-encoding-pragma",
"flynt",
+ "generate-airflow-diagrams",
"generate-pypi-readme",
"identity",
"insert-license",
diff --git a/docs/apache-airflow/core-concepts/overview.rst
b/docs/apache-airflow/core-concepts/overview.rst
index 547497b0ce..ce8d1adfb9 100644
--- a/docs/apache-airflow/core-concepts/overview.rst
+++ b/docs/apache-airflow/core-concepts/overview.rst
@@ -31,18 +31,37 @@ An Airflow installation generally consists of the following
components:
* An :doc:`executor <executor/index>`, which handles running tasks. In the
default Airflow installation, this runs everything *inside* the scheduler, but
most production-suitable executors actually push task execution out to
*workers*.
+* A *triggerer*, which executes deferred tasks - executed in an async-io event
loop.
+
* A *webserver*, which presents a handy user interface to inspect, trigger and
debug the behaviour of DAGs and tasks.
* A folder of *DAG files*, read by the scheduler and executor (and any workers
the executor has)
* A *metadata database*, used by the scheduler, executor and webserver to
store state.
-.. image:: ../img/arch-diag-basic.png
+
+Basic airflow architecture
+--------------------------
+
+This is the basic architecture of Airflow that you'll see in simple
installations:
+
+.. image:: ../img/diagram_basic_airflow_architecture.png
Most executors will generally also introduce other components to let them talk
to their workers - like a task queue - but you can still think of the executor
and its workers as a single logical component in Airflow overall, handling the
actual task execution.
Airflow itself is agnostic to what you're running - it will happily
orchestrate and run anything, either with high-level support from one of our
providers, or directly as a command using the shell or Python :doc:`operators`.
+Separate DAG processing architecture
+------------------------------------
+
+In a more complex installation where security and isolation are important,
you'll also see the standalone **dag file processor** component that allows to
separate scheduler from accessing DAG file. This is suitable if the
+deployment focus is on isolation between parsed tasks. While Airflow does not
yet support full multi-tenant features, it can be used to make sure that
DAG-author provided code is never executed in the context of the scheduler.
+
+.. image:: ../img/diagram_dag_processor_airflow_architecture.png
+
+You can read more about the different types of users and how they interact
with Airflow and how the
+security model of Airflow access look like in the
:doc:`/security/security_model`
+
Workloads
---------
diff --git a/docs/apache-airflow/img/arch-diag-basic.png
b/docs/apache-airflow/img/arch-diag-basic.png
deleted file mode 100755
index 1adde8f079..0000000000
Binary files a/docs/apache-airflow/img/arch-diag-basic.png and /dev/null differ
diff --git a/docs/apache-airflow/img/diagram_basic_airflow_architecture.png
b/docs/apache-airflow/img/diagram_basic_airflow_architecture.png
new file mode 100644
index 0000000000..feae0a63bb
Binary files /dev/null and
b/docs/apache-airflow/img/diagram_basic_airflow_architecture.png differ
diff --git
a/docs/apache-airflow/img/diagram_dag_processor_airflow_architecture.png
b/docs/apache-airflow/img/diagram_dag_processor_airflow_architecture.png
new file mode 100644
index 0000000000..8a2d48df19
Binary files /dev/null and
b/docs/apache-airflow/img/diagram_dag_processor_airflow_architecture.png differ
diff --git a/docs/apache-airflow/security/security_model.rst
b/docs/apache-airflow/security/security_model.rst
index 867b9faa1a..6200fff8f0 100644
--- a/docs/apache-airflow/security/security_model.rst
+++ b/docs/apache-airflow/security/security_model.rst
@@ -15,9 +15,6 @@
specific language governing permissions and limitations
under the License.
-.. contents::
- :local:
-
Airflow Security Model
======================
@@ -32,8 +29,23 @@ security reports are handled by the security team of
Airflow, head to
Airflow security model - user types
-----------------------------------
-The Airflow security model involves different types of users with
-varying access and capabilities:
+The Airflow security model involves different types of users with varying
access and capabilities:
+
+While - in smaller installations - all the actions related to Airflow can be
performed by a single user,
+in larger installations it is apparent that there different responsibilities,
roles and
+capabilities that need to be separated.
+
+This is why Airflow has the following user types:
+
+* Deployment Managers - overall responsible for the Airflow installation,
security and configuration
+* Authenticated UI users - users that can access Airflow UI and API and
interact with it
+* DAG Authors - responsible for creating DAGs and submitting them to Airflow
+
+You can see more on how the user types influence Airflow's architecture in
:doc:`/core-concepts/overview`,
+including, seeing the diagrams of less and more complex deployments.
+
+
+
Deployment Managers
...................
diff --git a/images/breeze/output_static-checks.svg
b/images/breeze/output_static-checks.svg
index f566259da0..a30351839b 100644
--- a/images/breeze/output_static-checks.svg
+++ b/images/breeze/output_static-checks.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 2002.0"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 2026.3999999999999"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -43,7 +43,7 @@
<defs>
<clipPath id="breeze-static-checks-clip-terminal">
- <rect x="0" y="0" width="1463.0" height="1951.0" />
+ <rect x="0" y="0" width="1463.0" height="1975.3999999999999" />
</clipPath>
<clipPath id="breeze-static-checks-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -282,9 +282,12 @@
<clipPath id="breeze-static-checks-line-78">
<rect x="0" y="1904.7" width="1464" height="24.65"/>
</clipPath>
+<clipPath id="breeze-static-checks-line-79">
+ <rect x="0" y="1929.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="2000" rx="8"/><text
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Command: static-checks</text>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="2024.4" rx="8"/><text
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Command: static-checks</text>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -329,51 +332,52 @@
</text><text class="breeze-static-checks-r5" x="0" y="800.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-32)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="800.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-32)">check-urlparse-usage-in-code | check-usage-of-re2-over-re | check-xml | codespell</text><text
class="breeze-static-checks-r5" x="1451.8" y="800.8" textLength="12.2"
clip-path="url(#breeze-static-checks-li [...]
</text><text class="breeze-static-checks-r5" x="0" y="825.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-33)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="825.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-33)">| compile-www-assets | compile-www-assets-dev |                            
[...]
</text><text class="breeze-static-checks-r5" x="0" y="849.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-34)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="849.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-34)">create-missing-init-py-files-tests | debug-statements | detect-private-key |     </text><text
class="breeze-static-checks-r5" x="1451.8" y="849.6" textLength="12.2"
clip-path="url(#bre [...]
-</text><text class="breeze-static-checks-r5" x="0" y="874" textLength="12.2"
clip-path="url(#breeze-static-checks-line-35)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="874" textLength="988.2"
clip-path="url(#breeze-static-checks-line-35)">doctoc | end-of-file-fixer | fix-encoding-pragma | flynt | generate-pypi-readme |</text><text
class="breeze-static-checks-r5" x="1451.8" y="874" textLength="12.2"
clip-path="url(#breeze-static- [...]
-</text><text class="breeze-static-checks-r5" x="0" y="898.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-36)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="898.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-36)">identity | insert-license | lint-chart-schema | lint-css | lint-dockerfile |     </text><text
class="breeze-static-checks-r5" x="1451.8" y="898.4" textLength="12.2" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="922.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-37)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="922.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-37)">lint-helm-chart | lint-json-schema | lint-markdown | lint-openapi |              </text><text
class="breeze-static-checks-r5" x=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="947.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-38)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="947.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-38)">mixed-line-ending | mypy-core | mypy-dev | mypy-docs | mypy-providers |          </text><text
class="breeze-static-checks-r5" x="1451.8" y= [...]
-</text><text class="breeze-static-checks-r5" x="0" y="971.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-39)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="971.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-39)">pretty-format-json | python-no-log-warn | replace-bad-characters | rst-backticks </text><text
class="breeze-static-checks-r5" x="1451.8" y="971.6" textLength="12.2"
clip-path="url(#breeze-static-chec [...]
-</text><text class="breeze-static-checks-r5" x="0" y="996" textLength="12.2"
clip-path="url(#breeze-static-checks-line-40)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="996" textLength="988.2"
clip-path="url(#breeze-static-checks-line-40)">| ruff | ruff-format | shellcheck | trailing-whitespace |                       
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1020.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-41)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1020.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-41)">ts-compile-format-lint-www | update-black-version | update-breeze-cmd-output |   </text><text
class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLength="12.2"
clip-path="url(#breeze-sta [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1044.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-42)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1044.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-42)">update-breeze-readme-config-hash | update-common-sql-api-stubs |                 </text><text
class="breeze-static-checks-r5" x="145 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1069.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-43)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1069.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-43)">update-er-diagram | update-extras | update-in-the-wild-to-be-sorted |            </text><text
class="breeze-static-checks-r5" x="1451.8" y="1069.2" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1093.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-44)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1093.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-44)">update-inlined-dockerfile-scripts | update-installed-providers-to-be-sorted |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1093.6" textLength="12.2"
clip-path="url(#breeze-static-c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1118" textLength="12.2"
clip-path="url(#breeze-static-checks-line-45)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1118" textLength="988.2"
clip-path="url(#breeze-static-checks-line-45)">update-local-yml-file | update-migration-references |                            
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">update-providers-dependencies | update-spelling-wordlist-to-be-sorted |          </text><text
class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12.2" c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">update-supported-versions | update-vendored-in-k8s-json-schema | update-version |</text><text
class="breeze-static-checks-r5" x="1451.8" y="1166.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">validate-pyproject | yamllint)                                
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1215.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-49)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1215.6" textLength="61"
clip-path="url(#breeze-static-checks-line-49)">-show</text><text
class="breeze-static-checks-r4" x="97.6" y="1215.6" textLength="195.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1240" textLength="134.2"
clip-path="url(#breeze-static-checks-line-50)">-initialize</text><text
class="breeze-static-checks-r4" x="170.8" y="1240" textLength="146.4"
clip-path="url [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1264.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-51)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1264.4" textLength="48.8"
clip-path="url(#breeze-static-checks-line-51)">-max</text><text
class="breeze-static-checks-r4" x="85.4" y="1264.4" textLength="292.8"
clip-path="url( [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1288.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-52)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1288.8" textLength="854"
clip-path="url(#breeze-static-checks-line-52)">(INTEGER RANGE)                                   
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1313.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-53)">│</text><text
class="breeze-static-checks-r5" x="451.4" y="1313.2" textLength="854"
clip-path="url(#breeze-static-checks-line-53)">[default: 3; 1<=x<=10]                                
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1337.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-54)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1337.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-54)">
-</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="24.4"
clip-path="url(#breeze-static-checks-line-55)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1362" textLength="463.6"
clip-path="url(#breeze-static-checks-line-55)"> Selecting files to run the checks on </text><text
class="breeze-static-checks-r5" x="488" y="1362" textLength="951.6"
clip-path="url(#breeze-static-checks-line-55)">──────────────────────────────
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1386.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-56)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1386.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-56)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1386.4" textLength="61"
clip-path="url(#breeze-static-checks-line-56)">-file</text><text
class="breeze-static-checks-r6" x="256.2" y="1386.4" textLength="24.4"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1410.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-57)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1410.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-57)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1410.8" textLength="48.8"
clip-path="url(#breeze-static-checks-line-57)">-all</text><text
class="breeze-static-checks-r4" x="85.4" y="1410.8" textLength="73.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1435.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-58)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1435.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-58)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1435.2" textLength="85.4"
clip-path="url(#breeze-static-checks-line-58)">-commit</text><text
class="breeze-static-checks-r4" x="122" y="1435.2" textLength="48.8"
clip-path="url [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1459.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-59)">│</text><text
class="breeze-static-checks-r1" x="305" y="1459.6" textLength="183"
clip-path="url(#breeze-static-checks-line-59)">exclusive with </text><text
class="breeze-static-checks-r4" x="488" y="1459.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-59)">-</text><text
class="breeze-static-checks-r4" x="500.2" y="1459.6" textLength="61" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1484" textLength="12.2"
clip-path="url(#breeze-static-checks-line-60)">│</text><text
class="breeze-static-checks-r7" x="305" y="1484" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-60)">(TEXT)                                      
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1508.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-61)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1508.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-61)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1508.4" textLength="61"
clip-path="url(#breeze-static-checks-line-61)">-last</text><text
class="breeze-static-checks-r4" x="97.6" y="1508.4" textLength="85.4"
clip-path="url(#b [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1532.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-62)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1532.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-62)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1532.8" textLength="61"
clip-path="url(#breeze-static-checks-line-62)">-only</text><text
class="breeze-static-checks-r4" x="97.6" y="1532.8" textLength="134.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1557.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-63)">│</text><text
class="breeze-static-checks-r1" x="305" y="1557.2" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-63)">branch and HEAD of your branch.                             
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1581.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-64)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1581.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-64)">
-</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="24.4"
clip-path="url(#breeze-static-checks-line-65)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1606" textLength="463.6"
clip-path="url(#breeze-static-checks-line-65)"> Building image before running checks </text><text
class="breeze-static-checks-r5" x="488" y="1606" textLength="951.6"
clip-path="url(#breeze-static-checks-line-65)">────────────────────────────────────────
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1630.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-66)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1630.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-66)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1630.4" textLength="61"
clip-path="url(#breeze-static-checks-line-66)">-skip</text><text
class="breeze-static-checks-r4" x="97.6" y="1630.4" textLength="244"
clip-path="url(#br [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1654.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-67)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1654.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-67)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1654.8" textLength="73.2"
clip-path="url(#breeze-static-checks-line-67)">-force</text><text
class="breeze-static-checks-r4" x="109.8" y="1654.8" textLength="73.2"
clip-path="ur [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1679.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-68)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1679.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-68)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1679.2" textLength="73.2"
clip-path="url(#breeze-static-checks-line-68)">-image</text><text
class="breeze-static-checks-r4" x="109.8" y="1679.2" textLength="48.8"
clip-path="ur [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1703.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-69)">│</text><text
class="breeze-static-checks-r7" x="414.8" y="1703.6" textLength="963.8"
clip-path="url(#breeze-static-checks-line-69)">(TEXT)                                     
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="12.2"
clip-path="url(#breeze-static-checks-line-70)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1728" textLength="963.8"
clip-path="url(#breeze-static-checks-line-70)">[default: latest]                                   
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1752.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-71)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1752.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-71)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1752.4" textLength="85.4"
clip-path="url(#breeze-static-checks-line-71)">-github</text><text
class="breeze-static-checks-r4" x="122" y="1752.4" textLength="134.2"
clip-path="ur [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1776.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-72)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1776.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-72)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1776.8" textLength="97.6"
clip-path="url(#breeze-static-checks-line-72)">-builder</text><text
class="breeze-static-checks-r1" x="414.8" y="1776.8" textLength="756.4"
clip-path= [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1801.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-73)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1801.2" textLength="756.4"
clip-path="url(#breeze-static-checks-line-73)">[default: autodetect]                                  &#
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1825.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-74)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1825.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-74)">
-</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="24.4"
clip-path="url(#breeze-static-checks-line-75)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1850" textLength="195.2"
clip-path="url(#breeze-static-checks-line-75)"> Common options </text><text
class="breeze-static-checks-r5" x="219.6" y="1850" textLength="1220"
clip-path="url(#breeze-static-checks-line-75)">────────────────────────────────────────────────────────────────────────────
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1874.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-76)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1874.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-76)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1874.4" textLength="48.8"
clip-path="url(#breeze-static-checks-line-76)">-dry</text><text
class="breeze-static-checks-r4" x="85.4" y="1874.4" textLength="48.8"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1898.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-77)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1898.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-77)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1898.8" textLength="97.6"
clip-path="url(#breeze-static-checks-line-77)">-verbose</text><text
class="breeze-static-checks-r6" x="158.6" y="1898.8" textLength="24.4"
clip-path=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1923.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-78)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1923.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-78)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1923.2" textLength="61"
clip-path="url(#breeze-static-checks-line-78)">-help</text><text
class="breeze-static-checks-r6" x="158.6" y="1923.2" textLength="24.4"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1947.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-79)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1947.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-79)">
+</text><text class="breeze-static-checks-r5" x="0" y="874" textLength="12.2"
clip-path="url(#breeze-static-checks-line-35)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="874" textLength="988.2"
clip-path="url(#breeze-static-checks-line-35)">doctoc | end-of-file-fixer | fix-encoding-pragma | flynt |                       </text>
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="898.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-36)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="898.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-36)">generate-airflow-diagrams | generate-pypi-readme | identity | insert-license |   </text><text
class="breeze-static-checks-r5" x="1451.8" y="898.4" textLength="12.2"
clip-path="url(#bre [...]
+</text><text class="breeze-static-checks-r5" x="0" y="922.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-37)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="922.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-37)">lint-chart-schema | lint-css | lint-dockerfile | lint-helm-chart |               </text><text
class="breeze-static-checks-r5 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="947.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-38)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="947.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-38)">lint-json-schema | lint-markdown | lint-openapi | mixed-line-ending | mypy-core |</text><text
class="breeze-static-checks-r5" x="1451.8" y="947.2" textLength="12.2"
clip-path="url(#breeze-s [...]
+</text><text class="breeze-static-checks-r5" x="0" y="971.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-39)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="971.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-39)">mypy-dev | mypy-docs | mypy-providers | pretty-format-json | python-no-log-warn |</text><text
class="breeze-static-checks-r5" x="1451.8" y="971.6" textLength="12.2"
clip-path="url(#breeze-s [...]
+</text><text class="breeze-static-checks-r5" x="0" y="996" textLength="12.2"
clip-path="url(#breeze-static-checks-line-40)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="996" textLength="988.2"
clip-path="url(#breeze-static-checks-line-40)">replace-bad-characters | rst-backticks | ruff | ruff-format | shellcheck |       </text><text
class="breeze-static-checks-r5" x="1451.8" y="996" textLength="1 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1020.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-41)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1020.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-41)">trailing-whitespace | ts-compile-format-lint-www | update-black-version |        </text><text
class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLength="12.2" c [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1044.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-42)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1044.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-42)">update-breeze-cmd-output | update-breeze-readme-config-hash |                    </text><text
class="breeze-static-ch [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1069.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-43)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1069.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-43)">update-common-sql-api-stubs | update-er-diagram | update-extras |                </text><text
class="breeze-static-checks-r5" x [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1093.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-44)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1093.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-44)">update-in-the-wild-to-be-sorted | update-inlined-dockerfile-scripts |            </text><text
class="breeze-static-checks-r5" x="1451.8" y="1093.6" textLengt [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1118" textLength="12.2"
clip-path="url(#breeze-static-checks-line-45)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1118" textLength="988.2"
clip-path="url(#breeze-static-checks-line-45)">update-installed-providers-to-be-sorted | update-local-yml-file |                </text><text
class="breeze-static-checks-r5" x="1451.8" y="1 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">update-migration-references | update-providers-dependencies |                    </text><text
class="breeze-static-ch [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">update-spelling-wordlist-to-be-sorted | update-supported-versions |              </text><text
class="breeze-static-checks-r5" x="1451.8" y="1166.8" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">update-vendored-in-k8s-json-schema | update-version | validate-pyproject |       </text><text
class="breeze-static-checks-r5" x="1451.8" y="1191.2" textLength="12.2" clip-p
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1215.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-49)">yamllint)                                     &
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1240" textLength="61"
clip-path="url(#breeze-static-checks-line-50)">-show</text><text
class="breeze-static-checks-r4" x="97.6" y="1240" textLength="195.2"
clip-path="url(#breeze-s [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1264.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-51)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1264.4" textLength="134.2"
clip-path="url(#breeze-static-checks-line-51)">-initialize</text><text
class="breeze-static-checks-r4" x="170.8" y="1264.4" textLength="146.4" clip-p
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1288.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-52)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1288.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-52)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1288.8" textLength="48.8"
clip-path="url(#breeze-static-checks-line-52)">-max</text><text
class="breeze-static-checks-r4" x="85.4" y="1288.8" textLength="292.8"
clip-path="url( [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1313.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-53)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1313.2" textLength="854"
clip-path="url(#breeze-static-checks-line-53)">(INTEGER RANGE)                                   
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1337.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-54)">│</text><text
class="breeze-static-checks-r5" x="451.4" y="1337.6" textLength="854"
clip-path="url(#breeze-static-checks-line-54)">[default: 3; 1<=x<=10]                                
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="1464"
clip-path="url(#breeze-static-checks-line-55)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1362" textLength="12.2"
clip-path="url(#breeze-static-checks-line-55)">
+</text><text class="breeze-static-checks-r5" x="0" y="1386.4"
textLength="24.4" clip-path="url(#breeze-static-checks-line-56)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1386.4" textLength="463.6"
clip-path="url(#breeze-static-checks-line-56)"> Selecting files to run the checks on </text><text
class="breeze-static-checks-r5" x="488" y="1386.4" textLength="951.6"
clip-path="url(#breeze-static-checks-line-56)">──────────────────────── [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1410.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-57)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1410.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-57)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1410.8" textLength="61"
clip-path="url(#breeze-static-checks-line-57)">-file</text><text
class="breeze-static-checks-r6" x="256.2" y="1410.8" textLength="24.4"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1435.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-58)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1435.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-58)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1435.2" textLength="48.8"
clip-path="url(#breeze-static-checks-line-58)">-all</text><text
class="breeze-static-checks-r4" x="85.4" y="1435.2" textLength="73.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1459.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-59)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1459.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-59)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1459.6" textLength="85.4"
clip-path="url(#breeze-static-checks-line-59)">-commit</text><text
class="breeze-static-checks-r4" x="122" y="1459.6" textLength="48.8"
clip-path="url [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1484" textLength="12.2"
clip-path="url(#breeze-static-checks-line-60)">│</text><text
class="breeze-static-checks-r1" x="305" y="1484" textLength="183"
clip-path="url(#breeze-static-checks-line-60)">exclusive with </text><text
class="breeze-static-checks-r4" x="488" y="1484" textLength="12.2"
clip-path="url(#breeze-static-checks-line-60)">-</text><text
class="breeze-static-checks-r4" x="500.2" y="1484" textLength="61" clip-pa [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1508.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-61)">│</text><text
class="breeze-static-checks-r7" x="305" y="1508.4" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-61)">(TEXT)                                      
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1532.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-62)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1532.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-62)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1532.8" textLength="61"
clip-path="url(#breeze-static-checks-line-62)">-last</text><text
class="breeze-static-checks-r4" x="97.6" y="1532.8" textLength="85.4"
clip-path="url(#b [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1557.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-63)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1557.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-63)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1557.2" textLength="61"
clip-path="url(#breeze-static-checks-line-63)">-only</text><text
class="breeze-static-checks-r4" x="97.6" y="1557.2" textLength="134.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1581.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-64)">│</text><text
class="breeze-static-checks-r1" x="305" y="1581.6" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-64)">branch and HEAD of your branch.                             
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="1464"
clip-path="url(#breeze-static-checks-line-65)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1606" textLength="12.2"
clip-path="url(#breeze-static-checks-line-65)">
+</text><text class="breeze-static-checks-r5" x="0" y="1630.4"
textLength="24.4" clip-path="url(#breeze-static-checks-line-66)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1630.4" textLength="463.6"
clip-path="url(#breeze-static-checks-line-66)"> Building image before running checks </text><text
class="breeze-static-checks-r5" x="488" y="1630.4" textLength="951.6"
clip-path="url(#breeze-static-checks-line-66)">──────────────────────────────────
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1654.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-67)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1654.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-67)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1654.8" textLength="61"
clip-path="url(#breeze-static-checks-line-67)">-skip</text><text
class="breeze-static-checks-r4" x="97.6" y="1654.8" textLength="244"
clip-path="url(#br [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1679.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-68)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1679.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-68)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1679.2" textLength="73.2"
clip-path="url(#breeze-static-checks-line-68)">-force</text><text
class="breeze-static-checks-r4" x="109.8" y="1679.2" textLength="73.2"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1703.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-69)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1703.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-69)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1703.6" textLength="73.2"
clip-path="url(#breeze-static-checks-line-69)">-image</text><text
class="breeze-static-checks-r4" x="109.8" y="1703.6" textLength="48.8"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="12.2"
clip-path="url(#breeze-static-checks-line-70)">│</text><text
class="breeze-static-checks-r7" x="414.8" y="1728" textLength="963.8"
clip-path="url(#breeze-static-checks-line-70)">(TEXT)                                      &#
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1752.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-71)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1752.4" textLength="963.8"
clip-path="url(#breeze-static-checks-line-71)">[default: latest]                                   
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1776.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-72)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1776.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-72)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1776.8" textLength="85.4"
clip-path="url(#breeze-static-checks-line-72)">-github</text><text
class="breeze-static-checks-r4" x="122" y="1776.8" textLength="134.2"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1801.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-73)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1801.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-73)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1801.2" textLength="97.6"
clip-path="url(#breeze-static-checks-line-73)">-builder</text><text
class="breeze-static-checks-r1" x="414.8" y="1801.2" textLength="756.4"
clip-path= [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1825.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-74)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1825.6" textLength="756.4"
clip-path="url(#breeze-static-checks-line-74)">[default: autodetect]                                  &#
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="1464"
clip-path="url(#breeze-static-checks-line-75)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1850" textLength="12.2"
clip-path="url(#breeze-static-checks-line-75)">
+</text><text class="breeze-static-checks-r5" x="0" y="1874.4"
textLength="24.4" clip-path="url(#breeze-static-checks-line-76)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1874.4" textLength="195.2"
clip-path="url(#breeze-static-checks-line-76)"> Common options </text><text
class="breeze-static-checks-r5" x="219.6" y="1874.4" textLength="1220"
clip-path="url(#breeze-static-checks-line-76)">──────────────────────────────────────────────────────────────────────
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1898.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-77)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1898.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-77)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1898.8" textLength="48.8"
clip-path="url(#breeze-static-checks-line-77)">-dry</text><text
class="breeze-static-checks-r4" x="85.4" y="1898.8" textLength="48.8"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1923.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-78)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1923.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-78)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1923.2" textLength="97.6"
clip-path="url(#breeze-static-checks-line-78)">-verbose</text><text
class="breeze-static-checks-r6" x="158.6" y="1923.2" textLength="24.4"
clip-path=" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1947.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-79)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1947.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-79)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1947.6" textLength="61"
clip-path="url(#breeze-static-checks-line-79)">-help</text><text
class="breeze-static-checks-r6" x="158.6" y="1947.6" textLength="24.4"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1972" textLength="1464"
clip-path="url(#breeze-static-checks-line-80)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1972" textLength="12.2"
clip-path="url(#breeze-static-checks-line-80)">
</text>
</g>
</g>
diff --git a/images/breeze/output_static-checks.txt
b/images/breeze/output_static-checks.txt
index 605983ca00..db19bfb4e6 100644
--- a/images/breeze/output_static-checks.txt
+++ b/images/breeze/output_static-checks.txt
@@ -1 +1 @@
-26c037ac78063fdb0ff40832867e678d
+4f78b9aa5b7e62a2ceca1478900d74d9
diff --git a/images/diagrams/python_multiprocess_logo.png
b/images/diagrams/python_multiprocess_logo.png
new file mode 100644
index 0000000000..4e9f4f3c37
Binary files /dev/null and b/images/diagrams/python_multiprocess_logo.png differ
diff --git a/scripts/ci/pre_commit/.gitignore b/scripts/ci/pre_commit/.gitignore
new file mode 100644
index 0000000000..e33609d251
--- /dev/null
+++ b/scripts/ci/pre_commit/.gitignore
@@ -0,0 +1 @@
+*.png
diff --git a/scripts/ci/pre_commit/diagram_hash.txt
b/scripts/ci/pre_commit/diagram_hash.txt
new file mode 100644
index 0000000000..ecd0b0067f
--- /dev/null
+++ b/scripts/ci/pre_commit/diagram_hash.txt
@@ -0,0 +1 @@
+05097f80bb47432ecb9a22fee5c1c752
diff --git a/scripts/ci/pre_commit/pre_commit_generate_airflow_diagrams.py
b/scripts/ci/pre_commit/pre_commit_generate_airflow_diagrams.py
new file mode 100755
index 0000000000..22f05715b1
--- /dev/null
+++ b/scripts/ci/pre_commit/pre_commit_generate_airflow_diagrams.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+# 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 __future__ import annotations
+
+import hashlib
+import os
+from pathlib import Path
+
+from diagrams import Cluster, Diagram, Edge
+from diagrams.custom import Custom
+from diagrams.onprem.client import User
+from diagrams.onprem.database import PostgreSQL
+from diagrams.programming.flowchart import MultipleDocuments
+from rich.console import Console
+
+console = Console(width=400, color_system="standard")
+
+LOCAL_DIR = Path(__file__).parent
+AIRFLOW_SOURCES_ROOT = Path(__file__).parents[3]
+DOCS_IMAGES_DIR = AIRFLOW_SOURCES_ROOT / "docs" / "apache-airflow" / "img"
+PYTHON_MULTIPROCESS_LOGO = AIRFLOW_SOURCES_ROOT / "images" / "diagrams" /
"python_multiprocess_logo.png"
+
+BASIC_ARCHITECTURE_IMAGE_NAME = "diagram_basic_airflow_architecture"
+DAG_PROCESSOR_AIRFLOW_ARCHITECTURE_IMAGE_NAME =
"diagram_dag_processor_airflow_architecture"
+DIAGRAM_HASH_FILE_NAME = "diagram_hash.txt"
+
+
+def generate_basic_airflow_diagram(filename: str):
+ basic_architecture_image_file = (DOCS_IMAGES_DIR /
BASIC_ARCHITECTURE_IMAGE_NAME).with_suffix(".png")
+ console.print(f"[bright_blue]Generating architecture image
{basic_architecture_image_file}")
+ with Diagram(name="", show=False, direction="LR", curvestyle="ortho",
filename=filename):
+ with Cluster("Parsing & Scheduling"):
+ schedulers = Custom("Scheduler(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+
+ metadata_db = PostgreSQL("Metadata DB")
+
+ dag_author = User("DAG Author")
+ dag_files = MultipleDocuments("DAG files")
+
+ dag_author >> Edge(color="black", style="dashed", reverse=False) >>
dag_files
+
+ with Cluster("Execution"):
+ workers = Custom("Worker(s)", PYTHON_MULTIPROCESS_LOGO.as_posix())
+ triggerer = Custom("Triggerer(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+
+ schedulers - Edge(color="blue", style="dashed", taillabel="Executor")
- workers
+
+ schedulers >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+ workers >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+ triggerer >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+
+ operations_user = User("Operations User")
+ with Cluster("UI"):
+ webservers = Custom("Webserver(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+
+ webservers >> Edge(color="black", style="dashed", reverse=True) >>
operations_user
+
+ metadata_db >> Edge(color="red", style="dotted", reverse=True) >>
webservers
+
+ dag_files >> Edge(color="brown", style="solid") >> workers
+ dag_files >> Edge(color="brown", style="solid") >> schedulers
+ dag_files >> Edge(color="brown", style="solid") >> triggerer
+ console.print(f"[green]Generating architecture image
{basic_architecture_image_file}")
+
+
+def generate_dag_processor_airflow_diagram(filename: str):
+ dag_processor_architecture_image_file = (
+ DOCS_IMAGES_DIR / DAG_PROCESSOR_AIRFLOW_ARCHITECTURE_IMAGE_NAME
+ ).with_suffix(".png")
+ console.print(f"[bright_blue]Generating architecture image
{dag_processor_architecture_image_file}")
+ with Diagram(name="", show=False, direction="LR", curvestyle="ortho",
filename=filename):
+ operations_user = User("Operations User")
+ with Cluster("No DAG Python Code Execution", graph_attr={"bgcolor":
"lightgrey"}):
+ with Cluster("Scheduling"):
+ schedulers = Custom("Scheduler(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+
+ with Cluster("UI"):
+ webservers = Custom("Webserver(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+
+ webservers >> Edge(color="black", style="dashed", reverse=True) >>
operations_user
+
+ metadata_db = PostgreSQL("Metadata DB")
+
+ dag_author = User("DAG Author")
+ with Cluster("DAG Python Code Execution"):
+ with Cluster("Execution"):
+ workers = Custom("Worker(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+ triggerer = Custom("Triggerer(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+ with Cluster("Parsing"):
+ dag_processors = Custom("DAG\nProcessor(s)",
PYTHON_MULTIPROCESS_LOGO.as_posix())
+ dag_files = MultipleDocuments("DAG files")
+
+ dag_author >> Edge(color="black", style="dashed", reverse=False) >>
dag_files
+
+ workers - Edge(color="blue", style="dashed", headlabel="Executor") -
schedulers
+
+ metadata_db >> Edge(color="red", style="dotted", reverse=True) >>
webservers
+ metadata_db >> Edge(color="red", style="dotted", reverse=True) >>
schedulers
+ dag_processors >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+ workers >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+ triggerer >> Edge(color="red", style="dotted", reverse=True) >>
metadata_db
+
+ dag_files >> Edge(color="brown", style="solid") >> workers
+ dag_files >> Edge(color="brown", style="solid") >> dag_processors
+ dag_files >> Edge(color="brown", style="solid") >> triggerer
+ console.print(f"[green]Generating architecture image
{dag_processor_architecture_image_file}")
+
+
+def main():
+ hash_md5 = hashlib.md5()
+ hash_md5.update(Path(__file__).resolve().read_bytes())
+ my_file_hash = hash_md5.hexdigest()
+ hash_file = LOCAL_DIR / DIAGRAM_HASH_FILE_NAME
+ if not hash_file.exists() or not hash_file.read_text().strip() ==
str(my_file_hash).strip():
+ os.chdir(DOCS_IMAGES_DIR)
+ generate_basic_airflow_diagram(BASIC_ARCHITECTURE_IMAGE_NAME)
+
generate_dag_processor_airflow_diagram(DAG_PROCESSOR_AIRFLOW_ARCHITECTURE_IMAGE_NAME)
+ hash_file.write_text(str(my_file_hash) + "\n")
+ else:
+ console.print("[bright_blue]No changes to generation script. Not
regenerating the images.")
+
+
+if __name__ == "__main__":
+ main()