This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new 9e45a1923c7 [v3-2-test] Run breeze command-image prek hook against
local sources, not stale uvx cache (#67966) (#67975)
9e45a1923c7 is described below
commit 9e45a1923c7c851ad2abbe25ecfd4f82b02f6ad3
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jun 4 00:13:11 2026 +0200
[v3-2-test] Run breeze command-image prek hook against local sources, not
stale uvx cache (#67966) (#67975)
The update-breeze-cmd-output hook invokes 'breeze' from PATH, which is
normally the uvx shim (ADR 0017) running from a cached build. uvx does
not rebuild a local path dependency on --refresh/--reinstall (only
--no-cache does), so after editing dev/breeze the hook computes command
hashes and option groups from stale code: it misses a needed image
regeneration, or worse, reverts a correctly regenerated image back to
the stale version during commit.
Prepend the local breeze sources to PYTHONPATH for the hook's breeze
subprocesses so the in-process computation (and the help rendering it
spawns) always reflects the current worktree source, while third-party
deps still resolve from the breeze environment. CI is unaffected (it
installs breeze fresh from the same checkout).
Also wrap the hook body in main() so the new helper is unit-testable.
(cherry picked from commit c67bba009b31a369c0cef41fc2c69a2383d9cd86)
Co-authored-by: Jarek Potiuk <[email protected]>
---
scripts/ci/prek/breeze_cmd_line.py | 84 +++++++++++++++++++--------
scripts/tests/ci/prek/test_breeze_cmd_line.py | 55 ++++++++++++++++++
2 files changed, 116 insertions(+), 23 deletions(-)
diff --git a/scripts/ci/prek/breeze_cmd_line.py
b/scripts/ci/prek/breeze_cmd_line.py
index d95cc4e8552..7a694073a42 100755
--- a/scripts/ci/prek/breeze_cmd_line.py
+++ b/scripts/ci/prek/breeze_cmd_line.py
@@ -37,6 +37,35 @@ BREEZE_SOURCES_DIR = BREEZE_INSTALL_DIR / "src"
FORCE = os.environ.get("FORCE", "false")[0].lower() == "t"
+def breeze_env_with_local_sources() -> dict[str, str]:
+ """Return an environment that forces breeze to import the local worktree
sources.
+
+ The ``breeze`` command on PATH is normally the uvx shim (ADR 0017), which
runs
+ breeze from a *cached* build. That cache does not always reflect
uncommitted edits
+ to ``dev/breeze``: ``uvx --refresh`` / ``--reinstall`` do not rebuild a
local path
+ dependency, only ``uvx --no-cache`` does. When the cache is stale this
hook computes
+ the command hashes / option groups from old code and then either misses a
needed
+ regeneration or *reverts* a correctly regenerated image back to the stale
version.
+
+ Prepending the local breeze sources to ``PYTHONPATH`` makes the in-process
+ computation (and the help rendering it spawns) always reflect the current
source,
+ while third-party dependencies (rich-click, click, ...) still resolve from
the
+ breeze environment. CI, which installs breeze fresh from the same
checkout, is
+ unaffected.
+ """
+ env = os.environ.copy()
+ existing_pythonpath = env.get("PYTHONPATH")
+ env["PYTHONPATH"] = (
+ f"{BREEZE_SOURCES_DIR}{os.pathsep}{existing_pythonpath}"
+ if existing_pythonpath
+ else str(BREEZE_SOURCES_DIR)
+ )
+ return env
+
+
+BREEZE_ENV = breeze_env_with_local_sources()
+
+
def verify_all_commands_described_in_docs():
errors = []
doc_content = ""
@@ -66,36 +95,45 @@ def is_regeneration_needed() -> bool:
result = subprocess.run(
["breeze", "setup", "regenerate-command-images", "--check-only"],
check=False,
+ env=BREEZE_ENV,
)
return result.returncode != 0
-initialize_breeze_prek(__name__, __file__)
+def main() -> int:
+ initialize_breeze_prek(__name__, __file__)
-return_code = 0
-verify_all_commands_described_in_docs()
-if is_regeneration_needed():
- console.print(
- "\n[bright_blue]Some of the commands changed since last time images
were generated. Regenerating.\n"
- )
- return_code = 1
+ return_code = 0
+ verify_all_commands_described_in_docs()
+ if is_regeneration_needed():
+ console.print(
+ "\n[bright_blue]Some of the commands changed since last time
images were generated. "
+ "Regenerating.\n"
+ )
+ return_code = 1
+ res = subprocess.run(
+ ["breeze", "setup", "regenerate-command-images"],
+ check=False,
+ env=BREEZE_ENV,
+ )
+ if res.returncode != 0:
+ console.print("\n[red]Breeze command configuration has changed.\n")
+ console.print("\n[bright_blue]Images have been regenerated.\n")
+ console.print("\n[bright_blue]You might want to run it
manually:\n")
+ console.print("\n[magenta]breeze setup
regenerate-command-images\n")
res = subprocess.run(
- ["breeze", "setup", "regenerate-command-images"],
+ ["breeze", "setup", "check-all-params-in-groups"],
check=False,
+ env=BREEZE_ENV,
)
if res.returncode != 0:
+ return_code = 1
console.print("\n[red]Breeze command configuration has changed.\n")
- console.print("\n[bright_blue]Images have been regenerated.\n")
- console.print("\n[bright_blue]You might want to run it manually:\n")
- console.print("\n[magenta]breeze setup regenerate-command-images\n")
-res = subprocess.run(
- ["breeze", "setup", "check-all-params-in-groups"],
- check=False,
-)
-if res.returncode != 0:
- return_code = 1
- console.print("\n[red]Breeze command configuration has changed.\n")
- console.print("\n[yellow]Please fix it in the appropriate
command_*_config.py file\n")
- console.print("\n[bright_blue]You can run consistency check manually by
running:\n")
- console.print("\nbreeze setup check-all-params-in-groups\n")
-sys.exit(return_code)
+ console.print("\n[yellow]Please fix it in the appropriate
command_*_config.py file\n")
+ console.print("\n[bright_blue]You can run consistency check manually
by running:\n")
+ console.print("\nbreeze setup check-all-params-in-groups\n")
+ return return_code
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/scripts/tests/ci/prek/test_breeze_cmd_line.py
b/scripts/tests/ci/prek/test_breeze_cmd_line.py
new file mode 100644
index 00000000000..5e83c718e61
--- /dev/null
+++ b/scripts/tests/ci/prek/test_breeze_cmd_line.py
@@ -0,0 +1,55 @@
+# 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.
+"""Unit tests for ``scripts/ci/prek/breeze_cmd_line.py``."""
+
+from __future__ import annotations
+
+import os
+
+from ci.prek.breeze_cmd_line import BREEZE_SOURCES_DIR,
breeze_env_with_local_sources
+
+
+class TestBreezeEnvWithLocalSources:
+ """The hook must run breeze against the local worktree sources, not a
stale cached build.
+
+ The ``breeze`` shim (ADR 0017) runs from a uvx cache that does not always
reflect
+ uncommitted ``dev/breeze`` edits. Prepending the local sources to
``PYTHONPATH``
+ makes the in-process command-hash / option-group computation use the
current code.
+ """
+
+ def test_sets_pythonpath_to_breeze_sources_when_unset(self, monkeypatch):
+ monkeypatch.delenv("PYTHONPATH", raising=False)
+ env = breeze_env_with_local_sources()
+ assert env["PYTHONPATH"] == str(BREEZE_SOURCES_DIR)
+
+ def test_prepends_breeze_sources_before_existing_pythonpath(self,
monkeypatch):
+ existing = f"/some/path{os.pathsep}/other/path"
+ monkeypatch.setenv("PYTHONPATH", existing)
+ env = breeze_env_with_local_sources()
+ # Local breeze sources must come first so they win over the cached
build.
+ assert env["PYTHONPATH"] ==
f"{BREEZE_SOURCES_DIR}{os.pathsep}{existing}"
+ assert env["PYTHONPATH"].split(os.pathsep)[0] ==
str(BREEZE_SOURCES_DIR)
+
+ def test_does_not_mutate_the_process_environment(self, monkeypatch):
+ monkeypatch.delenv("PYTHONPATH", raising=False)
+ breeze_env_with_local_sources()
+ assert "PYTHONPATH" not in os.environ
+
+ def test_preserves_other_environment_variables(self, monkeypatch):
+ monkeypatch.setenv("BREEZE_CMD_LINE_TEST_MARKER", "preserved")
+ env = breeze_env_with_local_sources()
+ assert env["BREEZE_CMD_LINE_TEST_MARKER"] == "preserved"