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)')}