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 23bc655c13d Add pre-commit checks for shared distributions usage. 
(#53769)
23bc655c13d is described below

commit 23bc655c13d848e6eaae24cf226be1f31f794184
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat Jul 26 10:22:14 2025 +0200

    Add pre-commit checks for shared distributions usage. (#53769)
    
    The pre-commit will make sure that usages of shared
    distributions are correctly done in all the distributions using them when 
[tool.airflow] shared_distributions added in pyproject.toml:
    
    It checks:
    
    * whether _shared folder exists
    * whether the shared library is properly symlinked (adds symlinks if 
missing)
    * whether hatchling force-include is configured (adds
      it if missing0
    * synchronizes dependencies of shared distributions and updates them in 
using projects
---
 .pre-commit-config.yaml                            |  13 +-
 contributing-docs/08_static_code_checks.rst        |   4 +-
 dev/breeze/doc/images/output_static-checks.svg     | 130 ++++----
 dev/breeze/doc/images/output_static-checks.txt     |   2 +-
 dev/breeze/src/airflow_breeze/pre_commit_ids.py    |   3 +-
 ....py => check_shared_distributions_structure.py} |   4 +-
 .../pre_commit/check_shared_distributions_usage.py | 368 +++++++++++++++++++++
 shared/README.md                                   |  85 ++++-
 shared/timezones/pyproject.toml                    |   2 +-
 task-sdk/pyproject.toml                            |   9 +-
 task-sdk/src/airflow/sdk/_shared/__init__.py       |  16 -
 11 files changed, 531 insertions(+), 105 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a54c40be51d..142049efe7c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -388,13 +388,20 @@ repos:
         always_run: true
         pass_filenames: false
         additional_dependencies: ['rich>=12.4.4', 'pyyaml>=6.0.2', 
'tomli>=2.0.1']
-      - id: check-shared-projects
-        name: Check shared projects structure
-        entry: ./scripts/ci/pre_commit/check_shared_projects.py
+      - id: check-shared-distributions-structure
+        name: Check shared distributions structure
+        entry: ./scripts/ci/pre_commit/check_shared_distributions_structure.py
         language: python
         additional_dependencies: ['rich>=12.4.4', 'tomli>=2.0.1']
         pass_filenames: false
         include: ^shared/.*$
+      - id: check-shared-distributions-usage
+        name: Check shared distributions usage
+        entry: ./scripts/ci/pre_commit/check_shared_distributions_usage.py
+        language: python
+        additional_dependencies: ['rich>=12.4.4', 'tomli>=2.0.1']
+        pass_filenames: false
+        include: ^shared/.*$|^.*/pyproject.toml$|^.*/_shared/.*$
       - id: validate-operators-init
         name: No templated field logic checks in operator __init__
         description: Prevent templated field logic checks in operators' 
__init__
diff --git a/contributing-docs/08_static_code_checks.rst 
b/contributing-docs/08_static_code_checks.rst
index 6f0861493fb..2e115abab16 100644
--- a/contributing-docs/08_static_code_checks.rst
+++ b/contributing-docs/08_static_code_checks.rst
@@ -229,7 +229,9 @@ require Breeze Docker image to be built locally.
 
+-----------------------------------------------------------+--------------------------------------------------------+---------+
 | check-sdk-imports                                         | Check for SDK 
imports in core files                    | *       |
 
+-----------------------------------------------------------+--------------------------------------------------------+---------+
-| check-shared-projects                                     | Check shared 
projects structure                        |         |
+| check-shared-distributions-structure                      | Check shared 
distributions structure                   |         |
++-----------------------------------------------------------+--------------------------------------------------------+---------+
+| check-shared-distributions-usage                          | Check shared 
distributions usage                       |         |
 
+-----------------------------------------------------------+--------------------------------------------------------+---------+
 | check-significant-newsfragments-are-valid                 | Check 
significant newsfragments are valid              |         |
 
+-----------------------------------------------------------+--------------------------------------------------------+---------+
diff --git a/dev/breeze/doc/images/output_static-checks.svg 
b/dev/breeze/doc/images/output_static-checks.svg
index a55ac921a94..95009de3741 100644
--- a/dev/breeze/doc/images/output_static-checks.svg
+++ b/dev/breeze/doc/images/output_static-checks.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 2294.7999999999997" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 2319.2" 
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="2243.7999999999997" />
+      <rect x="0" y="0" width="1463.0" height="2268.2" />
     </clipPath>
     <clipPath id="breeze-static-checks-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -318,9 +318,12 @@
 <clipPath id="breeze-static-checks-line-90">
     <rect x="0" y="2197.5" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-static-checks-line-91">
+    <rect x="0" y="2221.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="2292.8" rx="8"/><text 
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;static-checks</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="2317.2" rx="8"/><text 
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;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"/>
@@ -362,66 +365,67 @@
 </text><text class="breeze-static-checks-r5" x="0" y="727.6" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-29)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="727.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-29)">check-provider-docs-valid&#160;|&#160;check-provider-yaml-valid&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><
 [...]
 </text><text class="breeze-static-checks-r5" x="0" y="752" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-30)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="752" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-30)">check-providers-subpackages-init-file-exist&#160;|&#160;check-pydevd-left-in-code&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="752" textLength="12.2" 
clip-path="url(#bree [...]
 </text><text class="breeze-static-checks-r5" x="0" y="776.4" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-31)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="776.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-31)">check-pytest-mark-db-test-in-providers&#160;|&#160;check-revision-heads-map&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="776.4" te [...]
-</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-safe-filter-usage-in-html&#160;|&#160;check-sdk-imports&#160;|&#160;check-shared-projects&#160;|&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="800.8" textLength="12.2" 
clip-path="url(#breeze-s [...]
-</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)">check-significant-newsfragments-are-valid&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
-</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)">check-sql-dependency-common-data-structure&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
-</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)">check-start-date-not-used-in-defaults&#160;|&#160;check-system-tests-present&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="874" textLength="1 [...]
-</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)">check-system-tests-tocs&#160;|&#160;check-taskinstance-tis-attrs&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
  [...]
-</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)">check-template-context-variable-in-sync&#160;|&#160;check-template-fields-valid&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="922.8" textLength="12.2" clip 
[...]
-</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)">check-tests-in-the-right-folders&#160;|&#160;check-tests-unittest-testcase&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="947. [...]
-</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)">check-urlparse-usage-in-code&#160;|&#160;check-xml&#160;|&#160;check-zip-file-is-not-committed&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="971.6" textLength="12.2" 
clip-path="url(#bre [...]
-</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)">codespell&#160;|&#160;compile-fab-assets&#160;|&#160;compile-ui-assets&#160;|&#160;compile-ui-assets-dev&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="996" textLength="12.2" 
clip-path="url( [...]
-</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)">create-missing-init-py-files-tests&#160;|&#160;debug-statements&#160;|&#160;detect-private-key&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLength="12.2" 
clip-path="url(# [...]
-</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)">doctoc&#160;|&#160;end-of-file-fixer&#160;|&#160;fix-encoding-pragma&#160;|&#160;flynt&#160;|&#160;gci&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="b [...]
-</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)">generate-airflow-diagrams&#160;|&#160;generate-airflowctl-datamodels&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-stat [...]
-</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)">generate-airflowctl-help-images&#160;|&#160;generate-openapi-spec&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 cla [...]
-</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)">generate-openapi-spec-fab&#160;|&#160;generate-pypi-readme&#160;|&#160;generate-tasksdk-datamodels&#160;|&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1118" textLength="12.2" 
clip-path="url(#breeze-static-checks-line- [...]
-</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)">generate-volumes-for-sources&#160;|&#160;go-mockery&#160;|&#160;go-mod-tidy&#160;|&#160;gofmt&#160;|&#160;identity&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12 [...]
-</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)">insert-license&#160;|&#160;kubeconform&#160;|&#160;lint-chart-schema&#160;|&#160;lint-dockerfile&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="145 [...]
-</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)">lint-helm-chart&#160;|&#160;lint-json-schema&#160;|&#160;lint-markdown&#160;|&#160;mixed-line-ending&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1191.2" text [...]
-</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)">mypy-airflow-core&#160;|&#160;mypy-airflow-ctl&#160;|&#160;mypy-dev&#160;|&#160;mypy-devel-common&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8"  [...]
-</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-r7" x="451.4" y="1240" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-50)">mypy-providers&#160;|&#160;mypy-task-sdk&#160;|&#160;pretty-format-json&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
-</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-r7" x="451.4" y="1264.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-51)">prevent-deprecated-sqlalchemy-usage&#160;|&#160;provider-version-compat&#160;|&#160;pylint&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1264.4" textLength="12 [...]
-</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="988.2" 
clip-path="url(#breeze-static-checks-line-52)">python-no-log-warn&#160;|&#160;replace-bad-characters&#160;|&#160;rst-backticks&#160;|&#160;ruff&#160;|&#160;ruff-format&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1288.8" textLength="12.2" 
clip-path="url(#breez [...]
-</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="988.2" 
clip-path="url(#breeze-static-checks-line-53)">|&#160;shellcheck&#160;|&#160;trailing-whitespace&#160;|&#160;ts-compile-lint-simple-auth-manager-ui&#160;|&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1313.2" textLength="12.2" 
clip-path="url(# [...]
-</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-r7" x="451.4" y="1337.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-54)">ts-compile-lint-ui&#160;|&#160;update-black-version&#160;|&#160;update-breeze-cmd-output&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1337.6" text [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-55)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="1362" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-55)">update-breeze-readme-config-hash&#160;|&#160;update-chart-dependencies&#160;|&#160;update-er-diagram&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" 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="12.2" clip-path="url(#breeze-static-checks-line-56)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="1386.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-56)">|&#160;update-in-the-wild-to-be-sorted&#160;|&#160;update-inlined-dockerfile-scripts&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1386.4" textLength="12 [...]
-</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-r7" x="451.4" y="1410.8" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-57)">update-installed-providers-to-be-sorted&#160;|&#160;update-installers-and-pre-commit&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1410.8" textLength="12.2" 
clip-path="url(#breeze-sta [...]
-</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-r7" x="451.4" y="1435.2" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-58)">update-local-yml-file&#160;|&#160;update-migration-references&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
-</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-r7" x="451.4" y="1459.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-59)">update-providers-build-files&#160;|&#160;update-providers-dependencies&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks- [...]
-</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="451.4" y="1484" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-60)">update-pyproject-toml&#160;|&#160;update-reproducible-source-date-epoch&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="14 [...]
-</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="451.4" y="1508.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-61)">update-spelling-wordlist-to-be-sorted&#160;|&#160;update-supported-versions&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1508.4" [...]
-</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-r7" x="451.4" y="1532.8" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-62)">update-vendored-in-k8s-json-schema&#160;|&#160;update-version&#160;|&#160;validate-chart-annotations&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1532.8" textLength="12.2" 
clip-path="url(#breeze-static-checks-line [...]
-</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-r7" x="451.4" y="1557.2" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-63)">|&#160;validate-operators-init&#160;|&#160;yamllint&#160;|&#160;zizmor)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
-</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-r4" x="24.4" y="1581.6" textLength="268.4" 
clip-path="url(#breeze-static-checks-line-64)">--show-diff-on-failure</text><text
 class="breeze-static-checks-r6" x="402.6" y="1581.6" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-64)">-s</text><text 
class="breeze-static-checks-r1" x="451.4" y="1581.6" textLength=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-65)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="1606" textLength="292.8" 
clip-path="url(#breeze-static-checks-line-65)">--initialize-environment</text><text
 class="breeze-static-checks-r1" x="451.4" y="1606" textLength="549" 
clip-path="url(#breeze-static-checks-line-65)">Initialize&#160;environment&#160;before&#160;running&#160;checks.</text><text
 class=" [...]
-</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="353.8" 
clip-path="url(#breeze-static-checks-line-66)">--max-initialization-attempts</text><text
 class="breeze-static-checks-r1" x="451.4" y="1630.4" textLength="854" 
clip-path="url(#breeze-static-checks-line-66)">Maximum&#160;number&#160;of&#160;attempts&#160;to&#160;initialize&#160;env
 [...]
-</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-r7" x="451.4" y="1654.8" textLength="854" 
clip-path="url(#breeze-static-checks-line-67)">(INTEGER&#160;RANGE)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
-</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-r5" x="451.4" y="1679.2" textLength="854" 
clip-path="url(#breeze-static-checks-line-68)">[default:&#160;3;&#160;1&lt;=x&lt;=10]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1703.6" 
textLength="1464" 
clip-path="url(#breeze-static-checks-line-69)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="1703.6" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-69)">
-</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-70)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="1728" textLength="463.6" 
clip-path="url(#breeze-static-checks-line-70)">&#160;Selecting&#160;files&#160;to&#160;run&#160;the&#160;checks&#160;on&#160;</text><text
 class="breeze-static-checks-r5" x="488" y="1728" textLength="951.6" 
clip-path="url(#breeze-static-checks-line-70)">────────────────────────────── 
[...]
-</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="73.2" 
clip-path="url(#breeze-static-checks-line-71)">--file</text><text 
class="breeze-static-checks-r6" x="256.2" y="1752.4" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-71)">-f</text><text 
class="breeze-static-checks-r1" x="305" y="1752.4" textLength="427" 
clip-path="url [...]
-</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="134.2" 
clip-path="url(#breeze-static-checks-line-72)">--all-files</text><text 
class="breeze-static-checks-r6" x="256.2" y="1776.8" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-72)">-a</text><text 
class="breeze-static-checks-r1" x="305" y="1776.8" textLength="292.8" clip-p 
[...]
-</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="146.4" 
clip-path="url(#breeze-static-checks-line-73)">--commit-ref</text><text 
class="breeze-static-checks-r6" x="256.2" y="1801.2" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-73)">-r</text><text 
class="breeze-static-checks-r1" x="305" y="1801.2" textLength="1134.6" clip 
[...]
-</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-r1" x="305" y="1825.6" textLength="183" 
clip-path="url(#breeze-static-checks-line-74)">exclusive&#160;with&#160;</text><text
 class="breeze-static-checks-r4" x="488" y="1825.6" textLength="158.6" 
clip-path="url(#breeze-static-checks-line-74)">--last-commit</text><text 
class="breeze-static-checks-r1" x="646.6" y="1825.6" te [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-75)">│</text><text 
class="breeze-static-checks-r7" x="305" y="1850" textLength="1134.6" 
clip-path="url(#breeze-static-checks-line-75)">(TEXT)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
-</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="158.6" 
clip-path="url(#breeze-static-checks-line-76)">--last-commit</text><text 
class="breeze-static-checks-r6" x="256.2" y="1874.4" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-76)">-c</text><text 
class="breeze-static-checks-r1" x="305" y="1874.4" textLength="793" clip-p [...]
-</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="207.4" 
clip-path="url(#breeze-static-checks-line-77)">--only-my-changes</text><text 
class="breeze-static-checks-r6" x="256.2" y="1898.8" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-77)">-m</text><text 
class="breeze-static-checks-r1" x="305" y="1898.8" textLength="1134.6" [...]
-</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-r1" x="305" y="1923.2" textLength="1134.6" 
clip-path="url(#breeze-static-checks-line-78)">branch&#160;and&#160;HEAD&#160;of&#160;your&#160;branch.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
-</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="1972" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-80)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="1972" textLength="463.6" 
clip-path="url(#breeze-static-checks-line-80)">&#160;Building&#160;image&#160;before&#160;running&#160;checks&#160;</text><text
 class="breeze-static-checks-r5" x="488" y="1972" textLength="951.6" 
clip-path="url(#breeze-static-checks-line-80)">────────────────────────────────────────
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1996.4" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-81)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="1996.4" textLength="317.2" 
clip-path="url(#breeze-static-checks-line-81)">--skip-image-upgrade-check</text><text
 class="breeze-static-checks-r1" x="414.8" y="1996.4" textLength="536.8" 
clip-path="url(#breeze-static-checks-line-81)">Skip&#160;checking&#160;if&#160;the&#160;CI&#160;image&#160;is&#160;up&#160
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2020.8" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-82)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2020.8" textLength="158.6" 
clip-path="url(#breeze-static-checks-line-82)">--force-build</text><text 
class="breeze-static-checks-r1" x="414.8" y="2020.8" textLength="707.6" 
clip-path="url(#breeze-static-checks-line-82)">Force&#160;image&#160;build&#160;no&#160;matter&#160;if&#160;it&#160;is&#160;determined&
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2045.2" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-83)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2045.2" textLength="231.8" 
clip-path="url(#breeze-static-checks-line-83)">--github-repository</text><text 
class="breeze-static-checks-r6" x="366" y="2045.2" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-83)">-g</text><text 
class="breeze-static-checks-r1" x="414.8" y="2045.2" textLength="585.6 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2069.6" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-84)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2069.6" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-84)">--builder</text><text 
class="breeze-static-checks-r1" x="414.8" y="2069.6" textLength="756.4" 
clip-path="url(#breeze-static-checks-line-84)">Buildx&#160;builder&#160;used&#160;to&#160;perform&#160;`docker&#160;buildx&#160;build`&#160
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2094" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-85)">│</text><text 
class="breeze-static-checks-r5" x="414.8" y="2094" textLength="756.4" 
clip-path="url(#breeze-static-checks-line-85)">[default:&#160;autodetect]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2118.4" 
textLength="1464" 
clip-path="url(#breeze-static-checks-line-86)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="2118.4" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-86)">
-</text><text class="breeze-static-checks-r5" x="0" y="2142.8" 
textLength="24.4" clip-path="url(#breeze-static-checks-line-87)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="2142.8" textLength="195.2" 
clip-path="url(#breeze-static-checks-line-87)">&#160;Common&#160;options&#160;</text><text
 class="breeze-static-checks-r5" x="219.6" y="2142.8" textLength="1220" 
clip-path="url(#breeze-static-checks-line-87)">──────────────────────────────────────────────────────────────────────
 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2167.2" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-88)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2167.2" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-88)">--dry-run</text><text 
class="breeze-static-checks-r6" x="158.6" y="2167.2" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-88)">-D</text><text 
class="breeze-static-checks-r1" x="207.4" y="2167.2" textLength="719.8" clip-p 
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="2191.6" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-89)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2191.6" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-89)">--verbose</text><text 
class="breeze-static-checks-r6" x="158.6" y="2191.6" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-89)">-v</text><text 
class="breeze-static-checks-r1" x="207.4" y="2191.6" textLength="585.6" clip-p 
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="2216" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-90)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2216" textLength="73.2" 
clip-path="url(#breeze-static-checks-line-90)">--help</text><text 
class="breeze-static-checks-r6" x="158.6" y="2216" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-90)">-h</text><text 
class="breeze-static-checks-r1" x="207.4" y="2216" textLength="329.4" 
clip-path="url(#br [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2240.4" 
textLength="1464" 
clip-path="url(#breeze-static-checks-line-91)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="2240.4" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-91)">
+</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-safe-filter-usage-in-html&#160;|&#160;check-sdk-imports&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
+</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)">check-shared-distributions-structure&#160;|&#160;check-shared-distributions-usage&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="825.2" textLength="12.2" 
clip-path="url [...]
+</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)">check-significant-newsfragments-are-valid&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
+</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)">check-sql-dependency-common-data-structure&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
+</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)">check-start-date-not-used-in-defaults&#160;|&#160;check-system-tests-present&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="898.4" textLen [...]
+</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)">check-system-tests-tocs&#160;|&#160;check-taskinstance-tis-attrs&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
  [...]
+</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)">check-template-context-variable-in-sync&#160;|&#160;check-template-fields-valid&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="947.2" textLength="12.2" clip 
[...]
+</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)">check-tests-in-the-right-folders&#160;|&#160;check-tests-unittest-testcase&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="971. [...]
+</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)">check-urlparse-usage-in-code&#160;|&#160;check-xml&#160;|&#160;check-zip-file-is-not-committed&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="996" textLength="12.2" 
clip-path="url(#breeze-st [...]
+</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)">codespell&#160;|&#160;compile-fab-assets&#160;|&#160;compile-ui-assets&#160;|&#160;compile-ui-assets-dev&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLength="12.2" clip-p 
[...]
+</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)">create-missing-init-py-files-tests&#160;|&#160;debug-statements&#160;|&#160;detect-private-key&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1044.8" textLength="12.2" 
clip-path="url(# [...]
+</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)">doctoc&#160;|&#160;end-of-file-fixer&#160;|&#160;fix-encoding-pragma&#160;|&#160;flynt&#160;|&#160;gci&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="b [...]
+</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)">generate-airflow-diagrams&#160;|&#160;generate-airflowctl-datamodels&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-stat [...]
+</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)">generate-airflowctl-help-images&#160;|&#160;generate-openapi-spec&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class=" [...]
+</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)">generate-openapi-spec-fab&#160;|&#160;generate-pypi-readme&#160;|&#160;generate-tasksdk-datamodels&#160;|&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12.2" 
clip-path="url(#breeze-static-checks [...]
+</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)">generate-volumes-for-sources&#160;|&#160;go-mockery&#160;|&#160;go-mod-tidy&#160;|&#160;gofmt&#160;|&#160;identity&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1166.8" textLength="12 [...]
+</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)">insert-license&#160;|&#160;kubeconform&#160;|&#160;lint-chart-schema&#160;|&#160;lint-dockerfile&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="145 [...]
+</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)">lint-helm-chart&#160;|&#160;lint-json-schema&#160;|&#160;lint-markdown&#160;|&#160;mixed-line-ending&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1215.6" text [...]
+</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-r7" x="451.4" y="1240" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-50)">mypy-airflow-core&#160;|&#160;mypy-airflow-ctl&#160;|&#160;mypy-dev&#160;|&#160;mypy-devel-common&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1 [...]
+</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-r7" x="451.4" y="1264.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-51)">mypy-providers&#160;|&#160;mypy-task-sdk&#160;|&#160;pretty-format-json&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
+</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="988.2" 
clip-path="url(#breeze-static-checks-line-52)">prevent-deprecated-sqlalchemy-usage&#160;|&#160;provider-version-compat&#160;|&#160;pylint&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1288.8" textLength="12 [...]
+</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="988.2" 
clip-path="url(#breeze-static-checks-line-53)">python-no-log-warn&#160;|&#160;replace-bad-characters&#160;|&#160;rst-backticks&#160;|&#160;ruff&#160;|&#160;ruff-format&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1313.2" textLength="12.2" 
clip-path="url(#breez [...]
+</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-r7" x="451.4" y="1337.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-54)">|&#160;shellcheck&#160;|&#160;trailing-whitespace&#160;|&#160;ts-compile-lint-simple-auth-manager-ui&#160;|&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1337.6" textLength="12.2" 
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-55)">│</text><text 
class="breeze-static-checks-r7" x="451.4" y="1362" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-55)">ts-compile-lint-ui&#160;|&#160;update-black-version&#160;|&#160;update-breeze-cmd-output&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1362" textLength [...]
+</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-r7" x="451.4" y="1386.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-56)">update-breeze-readme-config-hash&#160;|&#160;update-chart-dependencies&#160;|&#160;update-er-diagram&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1386.4" textLength="12.2" 
clip-path="url(#breeze-static-checks-line [...]
+</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-r7" x="451.4" y="1410.8" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-57)">|&#160;update-in-the-wild-to-be-sorted&#160;|&#160;update-inlined-dockerfile-scripts&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1410.8" textLength="12 [...]
+</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-r7" x="451.4" y="1435.2" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-58)">update-installed-providers-to-be-sorted&#160;|&#160;update-installers-and-pre-commit&#160;|&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1435.2" textLength="12.2" 
clip-path="url(#breeze-sta [...]
+</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-r7" x="451.4" y="1459.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-59)">update-local-yml-file&#160;|&#160;update-migration-references&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
+</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="451.4" y="1484" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-60)">update-providers-build-files&#160;|&#160;update-providers-dependencies&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5"  [...]
+</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="451.4" y="1508.4" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-61)">update-pyproject-toml&#160;|&#160;update-reproducible-source-date-epoch&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x [...]
+</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-r7" x="451.4" y="1532.8" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-62)">update-spelling-wordlist-to-be-sorted&#160;|&#160;update-supported-versions&#160;|&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1532.8" [...]
+</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-r7" x="451.4" y="1557.2" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-63)">update-vendored-in-k8s-json-schema&#160;|&#160;update-version&#160;|&#160;validate-chart-annotations&#160;</text><text
 class="breeze-static-checks-r5" x="1451.8" y="1557.2" textLength="12.2" 
clip-path="url(#breeze-static-checks-line [...]
+</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-r7" x="451.4" y="1581.6" textLength="988.2" 
clip-path="url(#breeze-static-checks-line-64)">|&#160;validate-operators-init&#160;|&#160;yamllint&#160;|&#160;zizmor)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-65)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="1606" textLength="268.4" 
clip-path="url(#breeze-static-checks-line-65)">--show-diff-on-failure</text><text
 class="breeze-static-checks-r6" x="402.6" y="1606" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-65)">-s</text><text 
class="breeze-static-checks-r1" x="451.4" y="1606" textLength="524.6" c [...]
+</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="292.8" 
clip-path="url(#breeze-static-checks-line-66)">--initialize-environment</text><text
 class="breeze-static-checks-r1" x="451.4" y="1630.4" textLength="549" 
clip-path="url(#breeze-static-checks-line-66)">Initialize&#160;environment&#160;before&#160;running&#160;checks.</text><text
 c [...]
+</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="353.8" 
clip-path="url(#breeze-static-checks-line-67)">--max-initialization-attempts</text><text
 class="breeze-static-checks-r1" x="451.4" y="1654.8" textLength="854" 
clip-path="url(#breeze-static-checks-line-67)">Maximum&#160;number&#160;of&#160;attempts&#160;to&#160;initialize&#160;env
 [...]
+</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-r7" x="451.4" y="1679.2" textLength="854" 
clip-path="url(#breeze-static-checks-line-68)">(INTEGER&#160;RANGE)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
+</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-r5" x="451.4" y="1703.6" textLength="854" 
clip-path="url(#breeze-static-checks-line-69)">[default:&#160;3;&#160;1&lt;=x&lt;=10]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="1464" 
clip-path="url(#breeze-static-checks-line-70)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="1728" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-70)">
+</text><text class="breeze-static-checks-r5" x="0" y="1752.4" 
textLength="24.4" clip-path="url(#breeze-static-checks-line-71)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="1752.4" textLength="463.6" 
clip-path="url(#breeze-static-checks-line-71)">&#160;Selecting&#160;files&#160;to&#160;run&#160;the&#160;checks&#160;on&#160;</text><text
 class="breeze-static-checks-r5" x="488" y="1752.4" textLength="951.6" 
clip-path="url(#breeze-static-checks-line-71)">──────────────────────── [...]
+</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="73.2" 
clip-path="url(#breeze-static-checks-line-72)">--file</text><text 
class="breeze-static-checks-r6" x="256.2" y="1776.8" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-72)">-f</text><text 
class="breeze-static-checks-r1" x="305" y="1776.8" textLength="427" 
clip-path="url [...]
+</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="134.2" 
clip-path="url(#breeze-static-checks-line-73)">--all-files</text><text 
class="breeze-static-checks-r6" x="256.2" y="1801.2" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-73)">-a</text><text 
class="breeze-static-checks-r1" x="305" y="1801.2" textLength="292.8" clip-p 
[...]
+</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-r4" x="24.4" y="1825.6" textLength="146.4" 
clip-path="url(#breeze-static-checks-line-74)">--commit-ref</text><text 
class="breeze-static-checks-r6" x="256.2" y="1825.6" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-74)">-r</text><text 
class="breeze-static-checks-r1" x="305" y="1825.6" textLength="1134.6" clip 
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-75)">│</text><text 
class="breeze-static-checks-r1" x="305" y="1850" textLength="183" 
clip-path="url(#breeze-static-checks-line-75)">exclusive&#160;with&#160;</text><text
 class="breeze-static-checks-r4" x="488" y="1850" textLength="158.6" 
clip-path="url(#breeze-static-checks-line-75)">--last-commit</text><text 
class="breeze-static-checks-r1" x="646.6" y="1850" textLength [...]
+</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-r7" x="305" y="1874.4" textLength="1134.6" 
clip-path="url(#breeze-static-checks-line-76)">(TEXT)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
+</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="158.6" 
clip-path="url(#breeze-static-checks-line-77)">--last-commit</text><text 
class="breeze-static-checks-r6" x="256.2" y="1898.8" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-77)">-c</text><text 
class="breeze-static-checks-r1" x="305" y="1898.8" textLength="793" clip-p [...]
+</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="207.4" 
clip-path="url(#breeze-static-checks-line-78)">--only-my-changes</text><text 
class="breeze-static-checks-r6" x="256.2" y="1923.2" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-78)">-m</text><text 
class="breeze-static-checks-r1" x="305" y="1923.2" textLength="1134.6" [...]
+</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-r1" x="305" y="1947.6" textLength="1134.6" 
clip-path="url(#breeze-static-checks-line-79)">branch&#160;and&#160;HEAD&#160;of&#160;your&#160;branch.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
+</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><text class="breeze-static-checks-r5" x="0" y="1996.4" 
textLength="24.4" clip-path="url(#breeze-static-checks-line-81)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="1996.4" textLength="463.6" 
clip-path="url(#breeze-static-checks-line-81)">&#160;Building&#160;image&#160;before&#160;running&#160;checks&#160;</text><text
 class="breeze-static-checks-r5" x="488" y="1996.4" textLength="951.6" 
clip-path="url(#breeze-static-checks-line-81)">──────────────────────────────────
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2020.8" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-82)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2020.8" textLength="317.2" 
clip-path="url(#breeze-static-checks-line-82)">--skip-image-upgrade-check</text><text
 class="breeze-static-checks-r1" x="414.8" y="2020.8" textLength="536.8" 
clip-path="url(#breeze-static-checks-line-82)">Skip&#160;checking&#160;if&#160;the&#160;CI&#160;image&#160;is&#160;up&#160
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2045.2" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-83)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2045.2" textLength="158.6" 
clip-path="url(#breeze-static-checks-line-83)">--force-build</text><text 
class="breeze-static-checks-r1" x="414.8" y="2045.2" textLength="707.6" 
clip-path="url(#breeze-static-checks-line-83)">Force&#160;image&#160;build&#160;no&#160;matter&#160;if&#160;it&#160;is&#160;determined&
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2069.6" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-84)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2069.6" textLength="231.8" 
clip-path="url(#breeze-static-checks-line-84)">--github-repository</text><text 
class="breeze-static-checks-r6" x="366" y="2069.6" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-84)">-g</text><text 
class="breeze-static-checks-r1" x="414.8" y="2069.6" textLength="585.6 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2094" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-85)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2094" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-85)">--builder</text><text 
class="breeze-static-checks-r1" x="414.8" y="2094" textLength="756.4" 
clip-path="url(#breeze-static-checks-line-85)">Buildx&#160;builder&#160;used&#160;to&#160;perform&#160;`docker&#160;buildx&#160;build`&#160;comma
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2118.4" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-86)">│</text><text 
class="breeze-static-checks-r5" x="414.8" y="2118.4" textLength="756.4" 
clip-path="url(#breeze-static-checks-line-86)">[default:&#160;autodetect]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2142.8" 
textLength="1464" 
clip-path="url(#breeze-static-checks-line-87)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="2142.8" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-87)">
+</text><text class="breeze-static-checks-r5" x="0" y="2167.2" 
textLength="24.4" clip-path="url(#breeze-static-checks-line-88)">╭─</text><text 
class="breeze-static-checks-r5" x="24.4" y="2167.2" textLength="195.2" 
clip-path="url(#breeze-static-checks-line-88)">&#160;Common&#160;options&#160;</text><text
 class="breeze-static-checks-r5" x="219.6" y="2167.2" textLength="1220" 
clip-path="url(#breeze-static-checks-line-88)">──────────────────────────────────────────────────────────────────────
 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2191.6" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-89)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2191.6" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-89)">--dry-run</text><text 
class="breeze-static-checks-r6" x="158.6" y="2191.6" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-89)">-D</text><text 
class="breeze-static-checks-r1" x="207.4" y="2191.6" textLength="719.8" clip-p 
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="2216" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-90)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2216" textLength="109.8" 
clip-path="url(#breeze-static-checks-line-90)">--verbose</text><text 
class="breeze-static-checks-r6" x="158.6" y="2216" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-90)">-v</text><text 
class="breeze-static-checks-r1" x="207.4" y="2216" textLength="585.6" 
clip-path="url [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2240.4" 
textLength="12.2" clip-path="url(#breeze-static-checks-line-91)">│</text><text 
class="breeze-static-checks-r4" x="24.4" y="2240.4" textLength="73.2" 
clip-path="url(#breeze-static-checks-line-91)">--help</text><text 
class="breeze-static-checks-r6" x="158.6" y="2240.4" textLength="24.4" 
clip-path="url(#breeze-static-checks-line-91)">-h</text><text 
class="breeze-static-checks-r1" x="207.4" y="2240.4" textLength="329.4" 
clip-path= [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2264.8" 
textLength="1464" 
clip-path="url(#breeze-static-checks-line-92)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-static-checks-r1" x="1464" y="2264.8" textLength="12.2" 
clip-path="url(#breeze-static-checks-line-92)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_static-checks.txt 
b/dev/breeze/doc/images/output_static-checks.txt
index 67be6534ca2..eb0a67ef7cc 100644
--- a/dev/breeze/doc/images/output_static-checks.txt
+++ b/dev/breeze/doc/images/output_static-checks.txt
@@ -1 +1 @@
-1f034b17785eb25440aaf27d698faf93
+02f8c77e55afc39ebdae547e32621874
diff --git a/dev/breeze/src/airflow_breeze/pre_commit_ids.py 
b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
index 6ce4c035db0..e08366b9287 100644
--- a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
+++ b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
@@ -78,7 +78,8 @@ PRE_COMMIT_LIST = [
     "check-revision-heads-map",
     "check-safe-filter-usage-in-html",
     "check-sdk-imports",
-    "check-shared-projects",
+    "check-shared-distributions-structure",
+    "check-shared-distributions-usage",
     "check-significant-newsfragments-are-valid",
     "check-sql-dependency-common-data-structure",
     "check-start-date-not-used-in-defaults",
diff --git a/scripts/ci/pre_commit/check_shared_projects.py 
b/scripts/ci/pre_commit/check_shared_distributions_structure.py
similarity index 98%
rename from scripts/ci/pre_commit/check_shared_projects.py
rename to scripts/ci/pre_commit/check_shared_distributions_structure.py
index c0c04c39b83..444f70566c9 100755
--- a/scripts/ci/pre_commit/check_shared_projects.py
+++ b/scripts/ci/pre_commit/check_shared_distributions_structure.py
@@ -169,7 +169,7 @@ def check_ruff_lint_rules(ruff: dict, shared_path: Path) -> 
bool:
     return False
 
 
-def check_shared_project(shared_path: Path) -> bool:
+def check_shared_distribution(shared_path: Path) -> bool:
     pyproject_path = shared_path / "pyproject.toml"
     console.print(f"\n[bold blue]Checking:[/bold blue] 
[magenta]{shared_path.name}[/magenta] shared project")
     if not check_pyproject_exists(shared_path):
@@ -207,7 +207,7 @@ def main() -> None:
     all_ok = True
     for shared_project in SHARED_DIR.iterdir():
         if shared_project.is_dir():
-            ok = check_shared_project(shared_project)
+            ok = check_shared_distribution(shared_project)
             if not ok:
                 all_ok = False
     if not all_ok:
diff --git a/scripts/ci/pre_commit/check_shared_distributions_usage.py 
b/scripts/ci/pre_commit/check_shared_distributions_usage.py
new file mode 100755
index 00000000000..f2763d0ade2
--- /dev/null
+++ b/scripts/ci/pre_commit/check_shared_distributions_usage.py
@@ -0,0 +1,368 @@
+#!/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.
+
+"""
+Pre-commit check to verify and sync shared_distributions in pyproject.toml 
files.
+Ensures only valid shared libraries are referenced and that they exist in the 
shared/ folder.
+"""
+
+from __future__ import annotations
+
+import os
+import re
+import sys
+from pathlib import Path
+
+try:
+    import tomllib
+except ImportError:
+    import tomli as tomllib
+
+sys.path.insert(0, str(Path(__file__).parent.resolve()))  # for 
common_precommit_utils import
+from common_precommit_utils import AIRFLOW_ROOT_PATH, console, 
insert_documentation
+
+SHARED_DIR = AIRFLOW_ROOT_PATH / "shared"
+
+
+def find_pyproject_files_with_tool_airflow(root: Path) -> list[Path]:
+    pyproject_files = []
+    for pyproject in root.glob("**/pyproject.toml"):
+        console.print(f"[bold blue]Checking:[/bold blue] {pyproject}", end="")
+        try:
+            with open(pyproject, "rb") as f:
+                data = tomllib.load(f)
+            if "tool" in data and "airflow" in data["tool"]:
+                console.print("  [green]tool.airflow section found[/green]")
+                pyproject_files.append(pyproject)
+            else:
+                console.print("  [yellow]tool.airflow section NOT 
found[/yellow]")
+        except Exception as e:
+            console.print(f"  [red]Error reading {pyproject}: {e}[/red]")
+            continue
+    return pyproject_files
+
+
+def get_shared_distributions(pyproject_path: Path) -> list[str]:
+    with open(pyproject_path, "rb") as f:
+        data = tomllib.load(f)
+    return data.get("tool", {}).get("airflow", {}).get("shared_distributions", 
[])
+
+
+def verify_shared_distributions(shared_distributions: list[str], shared_dir: 
Path) -> list[str]:
+    errors = []
+    for dist in shared_distributions:
+        console.print(f"  Checking shared distribution: 
[magenta]{dist}[/magenta]", end="")
+        if not re.match(r"^apache-airflow-shared-.+", dist):
+            errors.append(
+                f"Invalid shared distribution name: {dist}. Must start with 
'apache-airflow-shared-'."
+            )
+            console.print("    [red]Invalid name[/red]")
+            continue
+        subfolder = dist.replace("apache-airflow-shared-", "")
+        if not (shared_dir / subfolder).is_dir():
+            errors.append(f"Shared distribution '{dist}' does not correspond 
to a subfolder in 'shared/'.")
+            console.print("    [red]NOK[/red]")
+        else:
+            console.print("    [green]OK[/green]")
+    return errors
+
+
+def find_shared_folder_in_src(pyproject_path: Path) -> Path | None:
+    """
+    Checks if a _shared directory exists anywhere under the src folder of the 
project.
+    Returns the Path if found, or None if missing.
+    """
+    project_dir = pyproject_path.parent
+    src_dir = project_dir / "src"
+    if not src_dir.is_dir():
+        console.print(
+            f"  [red]The src directory not found in {project_dir}. Cannot 
check for _shared folder.[/red]"
+        )
+        return None
+    console.print(f"  Searching for _shared directory under src: {src_dir}. ", 
end="")
+    for path in src_dir.rglob("_shared"):
+        if path.is_dir():
+            console.print("[green]OK[/green]")
+            console.print(f"  The _shared directory found: {path}")
+            return path
+    console.print("[red]Not found.[/red]")
+    return None
+
+
+def check_force_include(pyproject: Path, shared_distributions: list[str], 
shared_folder: Path) -> list[str]:
+    """
+    Checks that for each shared distribution, a proper force-include entry 
exists in tool.hatch.build.targets.sdist.force-include.
+    If missing, adds it automatically to pyproject.toml.
+    Returns a list of error messages for missing or incorrect entries.
+    """
+    errors: list[str] = []
+    try:
+        with open(pyproject, "rb") as f:
+            data = tomllib.load(f)
+    except Exception as e:
+        return [f"Error reading pyproject.toml: {e}"]
+    force_include = (
+        data.get("tool", {})
+        .get("hatch", {})
+        .get("build", {})
+        .get("targets", {})
+        .get("sdist", {})
+        .get("force-include", {})
+    )
+    pyproject_text = pyproject.read_text()
+    updated = False
+    console.print(f"  Checking force-include entries in {pyproject} ", end="")
+    for dist in shared_distributions:
+        dist_name = dist.replace("apache-airflow-shared-", "")
+        shared_src = f"../shared/{dist_name}/src/airflow_shared/{dist_name}"
+        found = False
+        for src, _ in force_include.items():
+            if src == shared_src:
+                found = True
+                break
+        if not found:
+            # Add missing entry to pyproject.toml
+            rel_dest = 
f"{shared_folder.relative_to(pyproject.parent)}/{dist_name}"
+            entry = f'"{shared_src}" = "{rel_dest}"\n'
+            # Find or create the 
[tool.hatch.build.targets.sdist.force-include] section
+            section_header = "[tool.hatch.build.targets.sdist.force-include]"
+            if section_header in pyproject_text:
+                # Insert entry after section header
+                lines = pyproject_text.splitlines(keepends=True)
+                for i, line in enumerate(lines):
+                    if line.strip() == section_header:
+                        # Insert after header and any existing entries
+                        insert_at = i + 1
+                        while (
+                            insert_at < len(lines)
+                            and lines[insert_at].startswith("[") is False
+                            and lines[insert_at].strip()
+                        ):
+                            insert_at += 1
+                        lines.insert(insert_at, entry)
+                        break
+                pyproject_text = "".join(lines)
+            else:
+                # Add new section at the end
+                pyproject_text += f"\n{section_header}\n{entry}"
+            pyproject.write_text(pyproject_text)
+            updated = True
+            console.print(f"[yellow]Added missing force-include entry for 
{dist} in {pyproject}[/yellow]")
+        else:
+            console.print("[green]OK[green]")
+    if updated:
+        # Reload data for next checks if needed
+        pass
+    return errors
+
+
+def get_workspace_pyproject_toml_files(main_pyproject_path: Path) -> 
list[Path]:
+    """
+    Parse the main pyproject.toml using tomllib to get the list of workspace 
project folders from [tool.uv.sources].
+    """
+    try:
+        with open(main_pyproject_path, "rb") as f:
+            data = tomllib.load(f)
+        members = data.get("tool", {}).get("uv", {}).get("workspace", 
{}).get("members", [])
+        pyproject_toml_candidates = [AIRFLOW_ROOT_PATH / member / 
"pyproject.toml" for member in members]
+        return [file for file in pyproject_toml_candidates if file.is_file()]
+    except Exception as e:
+        console.print(f"[red]Error reading workspace projects from 
{main_pyproject_path}: {e}[/red]")
+        return []
+
+
+def ensure_symlinks(shared_folder: Path, shared_distributions: list[str]) -> 
list[str]:
+    """
+    Ensure symlinks for shared distributions exist in the _shared folder. 
Create if missing.
+    Returns a list of errors if symlink creation fails.
+    """
+    errors: list[str] = []
+    for distribution in shared_distributions:
+        subfolder = distribution.replace("apache-airflow-shared-", "")
+        symlink_path = shared_folder / subfolder
+        console.print(f"  Checking for symlink: 
[magenta]{subfolder}[/magenta].   ", end="")
+        target_path = SHARED_DIR / subfolder / "src" / "airflow_shared" / 
subfolder
+        # Make symlink relative
+        rel_target_path = os.path.relpath(target_path, symlink_path.parent)
+        if not symlink_path.exists():
+            try:
+                os.symlink(rel_target_path, symlink_path)
+                console.print(f"[yellow]Created symlink: {symlink_path} -> 
{rel_target_path}[/yellow]")
+            except Exception as e:
+                errors.append(f"Failed to create symlink for {distribution}: 
{e}")
+                console.print(
+                    f"[red]Failed to create symlink: {symlink_path} -> 
{rel_target_path}: {e}[/red]"
+                )
+        elif not symlink_path.is_symlink() or symlink_path.resolve() != 
target_path.resolve():
+            try:
+                if symlink_path.exists():
+                    if symlink_path.is_symlink():
+                        symlink_path.unlink()
+                    else:
+                        errors.append(
+                            f"{symlink_path} exists and is not a symlink. 
Please remove it manually."
+                        )
+                        console.print(
+                            f"[red]{symlink_path} exists and is not a symlink. 
Please remove it manually.[/red]"
+                        )
+                        continue
+                os.symlink(rel_target_path, symlink_path)
+                console.print(f"[green]Fixed symlink: {symlink_path} -> 
{rel_target_path}[green]")
+            except Exception as e:
+                errors.append(f"Failed to fix symlink for {distribution}: {e}")
+                console.print(f"[red]Failed to fix symlink: {symlink_path} -> 
{rel_target_path}: {e}[/red]")
+        else:
+            console.print(f"[green]OK: {symlink_path} -> 
{rel_target_path}[green]")
+    return errors
+
+
+def find_dependencies_array_range(lines: list[str]) -> tuple[int | None, int | 
None]:
+    """
+    Finds the start and end line indices of the dependencies array in a 
pyproject.toml file.
+    Returns (dep_start, dep_end) or (None, None) if not found.
+    """
+    dep_start, dep_end = None, None
+    for i, line in enumerate(lines):
+        if re.match(r"^dependencies\s*=\s*\[", line.strip()):
+            dep_start = i
+            for j in range(i, len(lines)):
+                if lines[j].strip() == "]":
+                    dep_end = j
+                    break
+            break
+    return dep_start, dep_end
+
+
+def add_shared_dependencies_block(
+    project_pyproject_path: Path, dep_end: int, header: str, footer: str, 
content: list[str], dist_name: str
+) -> None:
+    """
+    Insert a shared dependencies block at the end of the dependencies array in 
pyproject.toml.
+    """
+    lines = project_pyproject_path.read_text().splitlines(keepends=True)
+    new_lines = (
+        lines[:dep_end] + ["    " + header + "\n"] + content + ["    " + 
footer + "\n"] + lines[dep_end:]
+    )
+    project_pyproject_path.write_text("".join(new_lines))
+    console.print(f"[yellow]Added shared dependencies for {dist_name} in 
{project_pyproject_path}[/yellow]")
+
+
+def sync_shared_dependencies(project_pyproject_path: Path, 
shared_distributions: list[str]) -> None:
+    """
+    Synchronize dependencies from shared distributions into the project's 
pyproject.toml.
+    Updates or inserts blocks marked with start/end comments for each shared 
distribution using insert_documentation.
+    Adds the block if missing.
+    """
+    for dist in shared_distributions:
+        dist_name = dist.replace("apache-airflow-shared-", "")
+        console.print(
+            f"  Synchronizing shared dependencies for 
[magenta]{dist_name}[/magenta] in {project_pyproject_path}  ",
+            end="",
+        )
+        shared_pyproject = SHARED_DIR / dist_name / "pyproject.toml"
+        if not shared_pyproject.exists():
+            continue
+        with open(shared_pyproject, "rb") as f:
+            shared_data = tomllib.load(f)
+        shared_deps = shared_data.get("project", {}).get("dependencies", [])
+        if shared_deps:
+            header = f"# Start of shared {dist_name} dependencies"
+            footer = f"# End of shared {dist_name} dependencies"
+            content = [f'    "{dep}",\n' for dep in shared_deps]
+            # Check if header exists in file
+            file_text = project_pyproject_path.read_text()
+            if header not in file_text:
+                # Insert at end of dependencies array
+                lines = file_text.splitlines(keepends=True)
+                dep_start, dep_end = find_dependencies_array_range(lines)
+                if dep_start is not None and dep_end is not None:
+                    add_shared_dependencies_block(
+                        project_pyproject_path, dep_end, header, footer, 
content, dist_name
+                    )
+                else:
+                    console.print(
+                        f"[red]Failed to determine dependencies array range in 
{project_pyproject_path}[/red]"
+                    )
+            else:
+                insert_documentation(project_pyproject_path, content, header, 
footer, add_comment=False)
+                console.print("[green]OK[/green]")
+
+
+def main() -> None:
+    errors: dict[str, list[str]] = {}
+    pyproject_files = get_workspace_pyproject_toml_files(AIRFLOW_ROOT_PATH / 
"pyproject.toml")
+    console.print(
+        "\n[bold blue]Checking for shared distributions for projects in 
airflow workspace.[/bold blue]\n"
+    )
+    if not pyproject_files:
+        console.print("[red]No pyproject.toml files found in workspace 
projects.[/red]")
+        sys.exit(1)
+    found_shared_distributions_usages = {}
+    for pyproject_file in pyproject_files:
+        console.print(f"[bold blue]Verifying shared_distributions in:[/bold 
blue] {pyproject_file}", end="")
+        shared_distributions = get_shared_distributions(pyproject_file)
+        if shared_distributions:
+            console.print(f"  {shared_distributions}")
+        else:
+            console.print("  No shared_distributions found")
+            continue
+        found_shared_distributions_usages[pyproject_file] = 
shared_distributions
+    console.print()
+    console.print("Found shared_distributions usages in the following 
pyproject.toml files:")
+    for pyproject_file, shared_distributions in 
found_shared_distributions_usages.items():
+        console.print(f"  [magenta]{pyproject_file}[/magenta]: 
{shared_distributions}")
+    console.print()
+    for pyproject_file in found_shared_distributions_usages:
+        shared_distributions = 
found_shared_distributions_usages[pyproject_file]
+        file_errors = verify_shared_distributions(shared_distributions, 
SHARED_DIR)
+        sync_shared_dependencies(pyproject_file, shared_distributions)
+        shared_folder = find_shared_folder_in_src(pyproject_file)
+        if not shared_folder:
+            if str(pyproject_file) not in errors:
+                errors[str(pyproject_file)] = []
+            errors[str(pyproject_file)].append(f"Could not find shared folder 
in {pyproject_file}")
+        if file_errors:
+            if str(pyproject_file) not in errors:
+                errors[str(pyproject_file)] = []
+            errors[str(pyproject_file)].extend(file_errors)
+        if shared_folder:
+            symlink_errors = ensure_symlinks(shared_folder, 
shared_distributions)
+            if symlink_errors:
+                if str(pyproject_file) not in errors:
+                    errors[str(pyproject_file)] = []
+                errors[str(pyproject_file)].extend(symlink_errors)
+            force_include_errors = check_force_include(pyproject_file, 
shared_distributions, shared_folder)
+            if force_include_errors:
+                if str(pyproject_file) not in errors:
+                    errors[str(pyproject_file)] = []
+                errors[str(pyproject_file)].extend(force_include_errors)
+    if errors:
+        console.print("\n[bold red]Shared distributions verification 
failed:[/bold red]")
+        for file, errs in errors.items():
+            console.print(f"\n[red]{file}:[/red]")
+            for err in errs:
+                console.print(f"  [red]- {err}[/red]")
+        sys.exit(1)
+    console.print(
+        "\n[bold green]All shared distributions are valid, _shared directories 
exist under "
+        "src, and force-include entries are correct.[/bold green]"
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/shared/README.md b/shared/README.md
index baa57598546..f6152f911db 100644
--- a/shared/README.md
+++ b/shared/README.md
@@ -19,27 +19,47 @@
 
 # Shared Python Code for Airflow Components
 
-This folder contains code that is shared across two or more of the Airflow 
distributions, such as airflow-core and task-sdk.
+This folder contains code shared across two or more of the Airflow 
distributions, such as
+airflow-core and task-sdk.
 
 ## Be Thoughtful about what you add under here
 
-Not every piece of code that is used in two distributions should be 
automatically placed in one of the shared libraries, and sometimes "just 
duplicate it" is the right approach to take. For example if it's just a 5 or 10 
line function and it's used in two places, it might be easier to future 
developers to understand if the function is in two places.
+Not every piece of code used in two distributions should be automatically 
placed in one of the
+shared libraries, and sometimes "just duplicate it" is the right approach to 
take. For example,
+if it's just a 5 or 10 line function and it's used in two places, it might be 
easier for future
+developers to understand if the function is in two places.
 
-There is no hard rules about what should or shouldn't be in these libraries, 
so try to apply your best judgement.
+There is no hard rule about what should or shouldn't be in these libraries, so 
try to apply your
+best judgement.
 
 ## Mechanics of sharing
 
-The primary mechanism by which we share code is to use in-repo symlinks. This 
means that there is a single copy of the code existing in the repo (no need for 
a pre-commit to update other copies, resulting in larger diffs).
+The primary mechanism by which we share code is to use in-repo symlinks. This 
means that there is
+a single copy of the code existing in the repo (no need for a pre-commit to 
update other copies,
+resulting in larger diffs).
 
-The primary reason we are using this sharing approach, rather than the perhaps 
more traditional approach of depending on a shared distribution from PyPI is 
around compatibility. Lets say that we have a library to parse AirflowConfig 
(including all the sources, env, `_cmd` handling, etc etc.). If that code is 
used by two distributions that are installed in the same Python environment, 
then either we introduce another source of "version hell" or we have to make 
every change both backwards  [...]
+The primary reason we are using this sharing approach, rather than the perhaps 
more traditional
+approach of depending on a shared distribution from PyPI is around 
compatibility. Let's say that
+we have a library to parse AirflowConfig (including all the sources, env, 
`_cmd` handling, etc
+etc.). If that code is used by two distributions that are installed in the 
same Python
+environment, then either we introduce another source of "version hell" or we 
have to make every
+change both backwards and forwards compatible.
 
-Instead by using an approach similar to vendoring (or if you compare it to C 
libraries - static linking), where each dist ships with a copy of the code of 
the version it knows it works with it allows them to co-exist in the same env 
without problems, and also reduces the cognitive load on developers when making 
changes to the shared libs -- as long as it passes the CI it should be good 
(and we don't have to test an ever increasing matrix of versions to ensure 
compatibility.)
+Instead, by using an approach similar to vendoring (or if you compare it to C 
libraries - static
+linking), where each dist ships with a copy of the code of the version it 
knows it works with,
+it allows them to co-exist in the same env without problems, and also reduces 
the cognitive load
+on developers when making changes to the shared libs -- as long as it passes 
the CI it should be
+good (and we don't have to test an ever increasing matrix of versions to 
ensure compatibility.)
 
-### Shared libraries _must_ use relative imports for other shared libraries**
+### Shared libraries _must_ use relative imports for other shared libraries
 
-The one caveat to this is due to the side-effect that these shared modules are 
going to be imported from different locations (for example 
`airflow._shared.timezones.timezone` and 
`airflow.sdk._shared.timezones.timezone`) then any imports inside the shared 
code to other parts of shared libraries _must_ make use of relative imports, 
and possibly needs `# noqa: TID252`
+The one caveat to this is due to the side-effect that these shared modules are 
going to be
+imported from different locations (for example 
`airflow._shared.timezones.timezone` and
+`airflow.sdk._shared.timezones.timezone`) then any imports inside the shared 
code to other parts
+of shared libraries _must_ make use of relative imports, and possibly needs `# 
noqa: TID252`
 
-For example, to use the shared timezone library from another shared library, 
lets say `shared/logging/src/airflow_shared/logging/config.py` you would have
+For example, to use the shared timezone library from another shared library, 
let's say
+`shared/logging/src/airflow_shared/logging/config.py` you would have
 
 ```python
 from ..timezones.timezone import is_naive  # noqa: TID252
@@ -49,8 +69,9 @@ This is different to the rest of the airflow codebase where 
relative imports are
 
 ### Shared libraries should be grouped one level beneath `airflow_shared/`
 
-In order to make both the relative imports above function and to provider 
simple structure, we want to arrange things in single named chunks, where the 
folder name is also the "shared library name". For example
-
+In order to make both the relative imports above function and to provide 
simple structure, we
+want to arrange things in single named chunks, where the folder name
+is also the "shared library name". For example:
 
 ```
 shared
@@ -73,19 +94,51 @@ shared
 
 ## Use In Editable installs/Git checkouts
 
-In the editable checkout (which is what you get by default when using `uv 
sync` etc), the shared libraries are symlinked directly in place, for example 
`task-sdk/src/airflow/sdk/_shared/timezones` is symlinked to 
`shared/timezones/src/airflow_shared/timezones`. This means that python 
automatically finds the files underneath the module as if they lived directly 
under `airflow.sdk._shared.timezones.X`.
+In the editable checkout (which is what you get by default when using `uv 
sync` etc), the shared
+libraries are symlinked directly in place, for example 
`task-sdk/src/airflow/sdk/_shared/timezones` is
+symlinked to `shared/timezones/src/airflow_shared/timezones`. This means that 
python automatically finds
+the files underneath the module as if they lived directly under 
`airflow.sdk._shared.timezones.X`.
 
 
 ### Including In built distributions
 
-Symlinks will not work when we build the main distributions for PyPI and other 
channels (since the symlinks would be to files outside of the distribution 
otherwise). This is specific to sdist; building wheel resolves the symlinks.
+Symlinks will not work when we build the main distributions for PyPI and other 
channels (since the symlinks
+would be to files outside the distribution otherwise). This is specific to 
sdist; building wheel
+resolves the symlinks.
+
+We have a convention that when you include a shared code in your distribution, 
you should such shared
+distributions (full name of the form `apache-airlow-shared-<name>` it to the 
`tool.airflow` section of
+your pyproject.toml:
+
+```toml
+[tool.airflow]
+
+shared_distributions = [
+     "apache-airflow-shared-timezones",
+]
+```
+
+This allows `pre-commit` check to automatically verify if the shared 
distributio is properly configured in
+your source tree and `pyproject.toml`.
+
+You should also have `_shared` folder created in your source tree to be able 
to symlink the shared
+distributions code.
+
+Once you do it, pre-commit will automatically check and either correct the 
distribution using the shared one,
+but you can also do it manually (for each shared distribution used):
+
+* creating a symlink of the sub-folder of `airflow_shared` in the `shared` 
folder to the
+  corresponding shared distribution, e.g.
+   `ln -s ../../shared/timezones/src/airflow_shared/timezones 
src/airflow/sdk/_shared/timezones`
 
-To make this work we need to configure hatchling to include extra files. For 
example in the Task SDK we have
+* adding force-include in your `pyproject.toml` to have hatchling copy the 
source files instead of
+  symling when `.sdist` distribution is built:
 
 ```toml
 [tool.hatch.build.targets.sdist.force-include]
-"../shared/logging/src/airflow_shared/logging" = 
"src/airflow/sdk/_shared/logging"
 "../shared/timezones/src/airflow_shared/timezones" = 
"src/airflow/sdk/_shared/timezones"
 ```
 
-We will need one entry in that table for each module we include directly below 
`src/airflow/sdk/_shared`.
+* pre-commit will automatically synchronize all dependencies of the shared 
distributions to
+  your pyproject.toml with appropriate comments allowing it to be 
automatically synced
+  when shared distribution dependencies are updated in the future.
diff --git a/shared/timezones/pyproject.toml b/shared/timezones/pyproject.toml
index bc68f6467c8..51928c22492 100644
--- a/shared/timezones/pyproject.toml
+++ b/shared/timezones/pyproject.toml
@@ -24,7 +24,7 @@ classifiers = [
 ]
 
 dependencies = [
-    "pendulum",
+    "pendulum>=3.1.0",
 ]
 
 [build-system]
diff --git a/task-sdk/pyproject.toml b/task-sdk/pyproject.toml
index de87a4a5f31..910a8419f37 100644
--- a/task-sdk/pyproject.toml
+++ b/task-sdk/pyproject.toml
@@ -56,7 +56,6 @@ dependencies = [
     "jinja2>=3.1.5",
     "methodtools>=0.4.7",
     "msgspec>=0.19.0",
-    'pendulum>=3.1.0',
     "python-dateutil>=2.7.0",
     "psutil>=6.1.0",
     "structlog>=25.4.0",
@@ -65,6 +64,9 @@ dependencies = [
     "requests>=2.31.0,<3",
     "types-requests>=2.31.0",
     "tenacity>=8.3.0",
+    # Start of shared timezones dependencies
+    "pendulum>=3.1.0",
+    # End of shared timezones dependencies
 ]
 
 [project.urls]
@@ -226,3 +228,8 @@ pythonpath = "tests"
 # Keep temporary directories (created by `tmp_path`) for 2 recent runs only 
failed tests.
 tmp_path_retention_count = "2"
 tmp_path_retention_policy = "failed"
+
+[tool.airflow]
+shared_distributions = [
+    "apache-airflow-shared-timezones",
+]
diff --git a/task-sdk/src/airflow/sdk/_shared/__init__.py 
b/task-sdk/src/airflow/sdk/_shared/__init__.py
deleted file mode 100644
index 13a83393a91..00000000000
--- a/task-sdk/src/airflow/sdk/_shared/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.

Reply via email to