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

shahar1 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 2adbce7e7b2 Explain a BASE_IMAGE without Python in hardened image 
builds (#76645)
2adbce7e7b2 is described below

commit 2adbce7e7b2609c1d0c28b9363b50558ae3f7d80
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat Nov 7 06:39:56 2026 +0100

    Explain a BASE_IMAGE without Python in hardened image builds (#76645)
    
    Passing a plain Debian BASE_IMAGE to the default hardened flavor stopped 
the build with
    `python3: command not found`, which does not say what to change. The script 
now names
    the two ways out, and the build args reference says BASE_IMAGE has to 
provide Python,
    which so far was only mentioned in the changelog.
    
    Generated-by: Claude Opus 5
---
 Dockerfile                                |  7 +++++++
 Dockerfile.ci                             |  7 +++++++
 dev/breeze/tests/test_os_deps_images.py   | 26 ++++++++++++++++++++++++++
 docker-stack-docs/build-arg-ref.rst       |  3 +++
 scripts/docker/install_os_dependencies.sh |  7 +++++++
 5 files changed, 50 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 5e6dc9933ef..c1dea35f48a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -171,6 +171,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 6f07515fcde..986ed8c2f3c 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -101,6 +101,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
index 06262f9f442..6b648339b4b 100644
--- a/dev/breeze/tests/test_os_deps_images.py
+++ b/dev/breeze/tests/test_os_deps_images.py
@@ -18,6 +18,8 @@ from __future__ import annotations
 
 import json
 import re
+import shutil
+import subprocess
 from subprocess import CompletedProcess
 from unittest import mock
 
@@ -683,6 +685,30 @@ def test_marker_covers_the_database_clients():
     assert all(re.search(rf"\b{variable}\b", marker_block) for variable in 
CLIENT_VARIABLES)
 
 
+def test_hardened_flavor_explains_a_base_image_without_python(tmp_path):
+    for tool in ("cat", "cut", "dirname"):
+        (tmp_path / tool).symlink_to(shutil.which(tool))
+    # The marker hash the script computes first is irrelevant here, and macOS 
may not have sha256sum.
+    sha256sum = tmp_path / "sha256sum"
+    sha256sum.write_text('#!/bin/sh\ncat "$@" >/dev/null\n')
+    sha256sum.chmod(0o755)
+
+    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
+
+
 def test_marker_files_match_the_image_tag_inputs_and_the_ci_file_group():
     docker_scripts = AIRFLOW_ROOT_PATH / "scripts" / "docker"
     script = (docker_scripts / "install_os_dependencies.sh").read_text()
diff --git a/docker-stack-docs/build-arg-ref.rst 
b/docker-stack-docs/build-arg-ref.rst
index e56cefed5d8..5e10789441c 100644
--- a/docker-stack-docs/build-arg-ref.rst
+++ b/docker-stack-docs/build-arg-ref.rst
@@ -120,6 +120,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 17af14b0b6b..6e4cd73ee87 100644
--- a/scripts/docker/install_os_dependencies.sh
+++ b/scripts/docker/install_os_dependencies.sh
@@ -77,6 +77,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