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

shahar1 pushed a commit to branch v3-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-4-test by this push:
     new 5f51c6028e0 [v3-4-test] Explain a BASE_IMAGE without Python in 
hardened image builds (#76645) (#76659)
5f51c6028e0 is described below

commit 5f51c6028e027e976cc0fc513a03f8c1b9109a00
Author: Shahar Epstein <[email protected]>
AuthorDate: Sat Nov 7 17:13:21 2026 +0200

    [v3-4-test] Explain a BASE_IMAGE without Python in hardened image builds 
(#76645) (#76659)
    
    Passing a plain Debian BASE_IMAGE to the default hardened flavor stops the
    build with "python3: command not found", which does not say what to change.
    
    dev/breeze/tests/test_os_deps_images.py does not exist on this branch, and 
the
    rest of it on main tests code that was not backported, so it holds only the
    new test here. The script reaches the check using bash builtins alone, so 
the
    tool stubs the test has on main are not needed.
    
    (cherry picked from commit 2adbce7e7b2609c1d0c28b9363b50558ae3f7d80)
    
    Claude-Session: https://claude.ai/code/session_01BVx6g8K9pmgvCdYRrKCGPk
    
    Co-authored-by: Jarek Potiuk <[email protected]>
---
 Dockerfile                                |  7 ++++++
 Dockerfile.ci                             |  7 ++++++
 dev/breeze/tests/test_os_deps_images.py   | 39 +++++++++++++++++++++++++++++++
 docker-stack-docs/build-arg-ref.rst       |  3 +++
 scripts/docker/install_os_dependencies.sh |  7 ++++++
 5 files changed, 63 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 40d7146d847..6388c95857f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -135,6 +135,13 @@ AIRFLOW_IMAGE_FLAVOR=${AIRFLOW_IMAGE_FLAVOR:-hardened}
 AIRFLOW_PYTHON_VERSION=${AIRFLOW_PYTHON_VERSION:-}
 COSIGN_VERSION=${COSIGN_VERSION:-3.0.5}
 if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
+    if ! command -v python3 >/dev/null; then
+        echo
+        echo "ERROR! The hardened flavor takes Python from BASE_IMAGE, but 
this base image has no python3."
+        echo "Pick a BASE_IMAGE with Python, or set AIRFLOW_IMAGE_FLAVOR to 
'legacy' to build on a plain Debian image."
+        echo
+        exit 1
+    fi
     # The Debian 12 hardened images ship Python under /opt/python, the Debian 
13 ones install it as
     # Debian packages under /usr - so ask the base image's Python where it 
lives.
     PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; 
print(sys.base_prefix)')}
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 283d9c5d7de..01ca2e3ed84 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -66,6 +66,13 @@ AIRFLOW_IMAGE_FLAVOR=${AIRFLOW_IMAGE_FLAVOR:-hardened}
 AIRFLOW_PYTHON_VERSION=${AIRFLOW_PYTHON_VERSION:-}
 COSIGN_VERSION=${COSIGN_VERSION:-3.0.5}
 if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
+    if ! command -v python3 >/dev/null; then
+        echo
+        echo "ERROR! The hardened flavor takes Python from BASE_IMAGE, but 
this base image has no python3."
+        echo "Pick a BASE_IMAGE with Python, or set AIRFLOW_IMAGE_FLAVOR to 
'legacy' to build on a plain Debian image."
+        echo
+        exit 1
+    fi
     # The Debian 12 hardened images ship Python under /opt/python, the Debian 
13 ones install it as
     # Debian packages under /usr - so ask the base image's Python where it 
lives.
     PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; 
print(sys.base_prefix)')}
diff --git a/dev/breeze/tests/test_os_deps_images.py 
b/dev/breeze/tests/test_os_deps_images.py
new file mode 100644
index 00000000000..7e41ef8b8d0
--- /dev/null
+++ b/dev/breeze/tests/test_os_deps_images.py
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import shutil
+import subprocess
+
+from airflow_breeze.utils.path_utils import AIRFLOW_ROOT_PATH
+
+
+def test_hardened_flavor_explains_a_base_image_without_python(tmp_path):
+    result = subprocess.run(
+        [
+            shutil.which("bash"),
+            AIRFLOW_ROOT_PATH / "scripts" / "docker" / 
"install_os_dependencies.sh",
+            "dev",
+        ],
+        env={"PATH": str(tmp_path)},
+        capture_output=True,
+        text=True,
+        check=False,
+    )
+
+    assert result.returncode == 1
+    assert "AIRFLOW_IMAGE_FLAVOR to 'legacy'" in result.stdout
diff --git a/docker-stack-docs/build-arg-ref.rst 
b/docker-stack-docs/build-arg-ref.rst
index 4bb92541a58..5b86b87a000 100644
--- a/docker-stack-docs/build-arg-ref.rst
+++ b/docker-stack-docs/build-arg-ref.rst
@@ -106,6 +106,9 @@ Python - for Airflow |airflow-version| that is 
``ghcr.io/apache/airflow/base/pyt
 pinned to the digest the Airflow CI last passed on (see :ref:`the hardened 
base images <image-hardened-base>`). Pulling the mirror needs no
 credentials; pulling ``dhi.io`` directly requires a ``docker login dhi.io``.
 
+In the default ``hardened`` flavor ``BASE_IMAGE`` has to provide Python, so 
the build fails on a plain Debian
+image such as ``debian:trixie-slim``. Use the ``legacy`` flavor below to build 
on one.
+
 For the 3.4.x line Airflow also publishes ``legacy`` images, built the way all 
images were built before
 3.4.0: Python is downloaded from python.org, its signature verified and 
compiled on a ``debian:bookworm-slim``
 base. They are deprecated and will be removed in Airflow 3.5.0. To build one 
yourself, set these arguments:
diff --git a/scripts/docker/install_os_dependencies.sh 
b/scripts/docker/install_os_dependencies.sh
index 880b4f208ba..696572137ed 100644
--- a/scripts/docker/install_os_dependencies.sh
+++ b/scripts/docker/install_os_dependencies.sh
@@ -41,6 +41,13 @@ AIRFLOW_IMAGE_FLAVOR=${AIRFLOW_IMAGE_FLAVOR:-hardened}
 AIRFLOW_PYTHON_VERSION=${AIRFLOW_PYTHON_VERSION:-}
 COSIGN_VERSION=${COSIGN_VERSION:-3.0.5}
 if [[ "${AIRFLOW_IMAGE_FLAVOR}" == "hardened" ]]; then
+    if ! command -v python3 >/dev/null; then
+        echo
+        echo "ERROR! The hardened flavor takes Python from BASE_IMAGE, but 
this base image has no python3."
+        echo "Pick a BASE_IMAGE with Python, or set AIRFLOW_IMAGE_FLAVOR to 
'legacy' to build on a plain Debian image."
+        echo
+        exit 1
+    fi
     # The Debian 12 hardened images ship Python under /opt/python, the Debian 
13 ones install it as
     # Debian packages under /usr - so ask the base image's Python where it 
lives.
     PYTHON_HOME=${PYTHON_HOME:-$(python3 -c 'import sys; 
print(sys.base_prefix)')}

Reply via email to