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

potiuk pushed a commit to branch pin-os-deps-image-layer
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit c70b19ad35d0b300879ec4816fb51f8d3b4982ef
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun Nov 1 14:02:22 2026 +0100

    Hash the database client scripts and keys into the OS-dependencies image tag
    
    install_os_dependencies.sh now runs the database client scripts, so a 
change to only one of them, or to a repository key, must produce a new 
OS-dependencies image. Otherwise the daily build sees the image as already 
built while every CI and PROD build's marker misses, and each reinstalls from 
the live repositories until the base image happens to change.
    
    Generated-by: Claude Opus 5
---
 .../airflow_breeze/commands/release_management_commands.py | 14 +++++++++++++-
 dev/breeze/tests/test_os_deps_images.py                    | 10 ++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index 8213d049c7f..7c8a5fa021c 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -5444,10 +5444,22 @@ def get_os_deps_image_build_args(
     return build_args
 
 
+OS_DEPS_DATABASE_CLIENT_SCRIPTS = ("common.sh", "install_mysql.sh", 
"install_mssql.sh", "install_postgres.sh")
+
+
 def compute_os_deps_image_inputs_hash(image_type: OsDepsImageType, build_args: 
dict[str, str]) -> str:
     """Hash what an OS-dependencies image is built from apart from its base 
image, whose tag it gets."""
     inputs = hashlib.sha256()
-    inputs.update(INSTALL_OS_DEPENDENCIES_SCRIPT_PATH.read_bytes())
+    scripts_dir = INSTALL_OS_DEPENDENCIES_SCRIPT_PATH.parent
+    # The same files the marker in install_os_dependencies.sh hashes: a change 
to any of them must
+    # produce a new image, not only a reinstall on top of the pinned one.
+    for path in [
+        INSTALL_OS_DEPENDENCIES_SCRIPT_PATH,
+        *(scripts_dir / name for name in OS_DEPS_DATABASE_CLIENT_SCRIPTS),
+        *sorted((scripts_dir / "keys").glob("*")),
+    ]:
+        inputs.update(f"{path.relative_to(scripts_dir)}\n".encode())
+        inputs.update(path.read_bytes())
     dockerfile_content = (AIRFLOW_ROOT_PATH / 
image_type.dockerfile).read_text()
     inputs.update(extract_dockerfile_stage(dockerfile_content, 
image_type.target).encode())
     for name, value in sorted(build_args.items()):
diff --git a/dev/breeze/tests/test_os_deps_images.py 
b/dev/breeze/tests/test_os_deps_images.py
index 95cb8d5daea..fd6b05302c1 100644
--- a/dev/breeze/tests/test_os_deps_images.py
+++ b/dev/breeze/tests/test_os_deps_images.py
@@ -237,6 +237,8 @@ def 
test_os_deps_images_install_what_the_default_image_builds_pass(image_type, p
     "change",
     [
         pytest.param("script", id="script"),
+        pytest.param("client-script", id="client-script"),
+        pytest.param("key", id="key"),
         pytest.param("stage", id="stage"),
         pytest.param("build-arg", id="build-arg"),
     ],
@@ -247,6 +249,10 @@ def 
test_os_deps_image_inputs_hash_changes_with_its_inputs(change, tmp_path):
     )
     script = tmp_path / "install_os_dependencies.sh"
     script.write_text("echo install\n")
+    for name in ("common.sh", "install_mysql.sh", "install_mssql.sh", 
"install_postgres.sh"):
+        (tmp_path / name).write_text(f"echo {name}\n")
+    (tmp_path / "keys").mkdir()
+    (tmp_path / "keys" / "repo.asc").write_text("key\n")
     build_args = {"BASE_IMAGE": "base@sha256:1", "AIRFLOW_PYTHON_VERSION": 
"3.12.1"}
     with (
         mock.patch(f"{PATCH_PREFIX}.AIRFLOW_ROOT_PATH", tmp_path),
@@ -258,6 +264,10 @@ def 
test_os_deps_image_inputs_hash_changes_with_its_inputs(change, tmp_path):
         )
         if change == "script":
             script.write_text("echo install more\n")
+        elif change == "client-script":
+            (tmp_path / "install_mysql.sh").write_text("echo install_mysql.sh 
8.4\n")
+        elif change == "key":
+            (tmp_path / "keys" / "repo.asc").write_text("rotated key\n")
         elif change == "stage":
             (tmp_path / "Dockerfile.ci").write_text(
                 "FROM ${CI_OS_DEPS_IMAGE} as ci-os-deps\nRUN false\nFROM x as 
main\n"

Reply via email to