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

potiuk 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 9b114c8deee Reuse locked CI dependencies across workspace source 
changes (#74466)
9b114c8deee is described below

commit 9b114c8deeeb0ae02d462cf2464350ad09af35fa
Author: Yossi Eliaz <[email protected]>
AuthorDate: Sat Oct 31 21:59:15 2026 +0200

    Reuse locked CI dependencies across workspace source changes (#74466)
    
    * Reuse locked CI dependencies across workspace source changes
    
    * Skip the locked dependency pre-install when uv.lock does not fit
    
    A frozen sync fails when uv.lock is missing or lacks a workspace member,
    which would fail the whole image build even though the full sync after
    COPY already re-resolves in that case.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    * Run the locked dependency pre-install through the install script
    
    The pre-install repeated the uv sync flags of install_from_sources, so the
    two layers could drift apart and install different builds of the same
    packages. INSTALL_LOCKED_THIRD_PARTY_ONLY makes the install script add
    --no-install-workspace, skip the re-resolving fallback and skip the steps
    that need workspace packages, so both layers run the same uv sync. The
    Dockerfile.ci step stays best-effort and is still skipped for upgrade 
builds.
    
    * Skip the locked dependency pre-install only when uv.lock does not fit
    
    A frozen dry run now decides whether to skip: it fails when uv.lock is
    missing or lacks a workspace member, and only then is the warm-up skipped.
    Any other failure, such as a network error or an lxml/xmlsec build error,
    would fail the full sync as well, so it now fails the build instead of
    leaving an empty layer in the cache. The install script also refuses the
    locked mode for upgrade builds itself instead of relying on the caller.
    
    ---------
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
    Co-authored-by: Shahar Epstein <[email protected]>
---
 Dockerfile                                         | 43 ++++++++++---
 Dockerfile.ci                                      | 71 +++++++++++++++++++---
 .../docker/install_airflow_when_building_images.sh | 48 ++++++++++++---
 3 files changed, 140 insertions(+), 22 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index bf64f1a27fa..25a14d98316 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1540,6 +1540,15 @@ function install_from_sources() {
     if [[ ${VIRTUAL_ENV=} != "" ]]; then
         extra_sync_flags="--active"
     fi
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
+            echo
+            echo "${COLOR_YELLOW}Upgrade builds resolve dependencies anew; 
skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        extra_sync_flags="${extra_sync_flags} --no-install-workspace"
+    fi
     if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
         if [[ ${PACKAGING_TOOL_CMD} == "pip" ]]; then
             set +x
@@ -1560,19 +1569,35 @@ function install_from_sources() {
             ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
             --no-python-downloads --no-managed-python
     else
-        set +x
-        echo
-        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
-        echo
         # Use uv sync --frozen to install exactly what is pinned in uv.lock 
without re-resolving.
         # --no-binary-package is needed in order to avoid libxml and xmlsec 
using different version of
         # libxml2 (binary lxml embeds its own libxml2, while xmlsec uses 
system one).
         # See https://bugs.launchpad.net/lxml/+bug/2110068
+        # shellcheck disable=SC2206 # extra_sync_flags holds several flags
+        local -a frozen_sync=(uv sync --all-packages --frozen --group ci-image
+            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec
+            --no-python-downloads --no-managed-python)
+        set +x
+        # Without the workspace sources a lock that does not match the 
manifests cannot be re-resolved;
+        # the installation that adds the workspace packages does that later.
+        if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]] && ! 
"${frozen_sync[@]}" --dry-run; then
+            echo
+            echo "${COLOR_YELLOW}uv.lock does not match the dependency 
manifests; skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        echo
+        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
+        echo
         set -x
-        if ! uv sync --all-packages --frozen --group ci-image \
-            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
-            --no-python-downloads --no-managed-python; then
+        if ! "${frozen_sync[@]}"; then
             set +x
+            if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+                echo
+                echo "${COLOR_RED}Failing because the locked third-party 
installation failed although uv.lock matches the dependency 
manifests.${COLOR_RESET}"
+                echo
+                exit 1
+            fi
             if [[ ${AIRFLOW_FALLBACK_NO_CONSTRAINTS_INSTALLATION} != "true" 
]]; then
                 echo
                 echo "${COLOR_RED}Failing because frozen uv.lock installation 
failed and fallback is disabled.${COLOR_RESET}"
@@ -1662,6 +1687,10 @@ function install_airflow_when_building_images() {
         install_from_external_spec
     fi
     set +x
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        # Packaging tools and 'pip check' are handled by the installation that 
adds the workspace packages
+        return
+    fi
     common::install_packaging_tools
     echo
     echo "${COLOR_BLUE}Running 'pip check'${COLOR_RESET}"
diff --git a/Dockerfile.ci b/Dockerfile.ci
index d164e2acd56..ff756aa62a0 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -1233,6 +1233,15 @@ function install_from_sources() {
     if [[ ${VIRTUAL_ENV=} != "" ]]; then
         extra_sync_flags="--active"
     fi
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
+            echo
+            echo "${COLOR_YELLOW}Upgrade builds resolve dependencies anew; 
skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        extra_sync_flags="${extra_sync_flags} --no-install-workspace"
+    fi
     if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
         if [[ ${PACKAGING_TOOL_CMD} == "pip" ]]; then
             set +x
@@ -1253,19 +1262,35 @@ function install_from_sources() {
             ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
             --no-python-downloads --no-managed-python
     else
-        set +x
-        echo
-        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
-        echo
         # Use uv sync --frozen to install exactly what is pinned in uv.lock 
without re-resolving.
         # --no-binary-package is needed in order to avoid libxml and xmlsec 
using different version of
         # libxml2 (binary lxml embeds its own libxml2, while xmlsec uses 
system one).
         # See https://bugs.launchpad.net/lxml/+bug/2110068
+        # shellcheck disable=SC2206 # extra_sync_flags holds several flags
+        local -a frozen_sync=(uv sync --all-packages --frozen --group ci-image
+            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec
+            --no-python-downloads --no-managed-python)
+        set +x
+        # Without the workspace sources a lock that does not match the 
manifests cannot be re-resolved;
+        # the installation that adds the workspace packages does that later.
+        if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]] && ! 
"${frozen_sync[@]}" --dry-run; then
+            echo
+            echo "${COLOR_YELLOW}uv.lock does not match the dependency 
manifests; skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        echo
+        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
+        echo
         set -x
-        if ! uv sync --all-packages --frozen --group ci-image \
-            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
-            --no-python-downloads --no-managed-python; then
+        if ! "${frozen_sync[@]}"; then
             set +x
+            if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+                echo
+                echo "${COLOR_RED}Failing because the locked third-party 
installation failed although uv.lock matches the dependency 
manifests.${COLOR_RESET}"
+                echo
+                exit 1
+            fi
             if [[ ${AIRFLOW_FALLBACK_NO_CONSTRAINTS_INSTALLATION} != "true" 
]]; then
                 echo
                 echo "${COLOR_RED}Failing because frozen uv.lock installation 
failed and fallback is disabled.${COLOR_RESET}"
@@ -1355,6 +1380,10 @@ function install_airflow_when_building_images() {
         install_from_external_spec
     fi
     set +x
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        # Packaging tools and 'pip check' are handled by the installation that 
adds the workspace packages
+        return
+    fi
     common::install_packaging_tools
     echo
     echo "${COLOR_BLUE}Running 'pip check'${COLOR_RESET}"
@@ -1992,6 +2021,22 @@ fi
 exec /bin/bash "${@}"
 EOF
 
+# Extract manifests separately so COPY below depends on their contents, not 
all source files.
+FROM ${BASE_IMAGE} as dependency-manifests
+WORKDIR /source
+SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-o", "nounset", "-o", 
"nolog", "-c"]
+RUN --mount=type=bind,target=/source \
+    manifest_list="$(mktemp)" && \
+    trap 'rm -f "${manifest_list}"' EXIT && \
+    find . -name pyproject.toml -type f -print0 > "${manifest_list}" && \
+    mkdir -p /dependency-manifests && \
+    while IFS= read -r -d '' manifest; do \
+        relative_path="${manifest#./}"; \
+        mkdir -p "/dependency-manifests/$(dirname "${relative_path}")" || exit 
1; \
+        cp "${manifest}" "/dependency-manifests/${relative_path}" || exit 1; \
+    done < "${manifest_list}" && \
+    if [ -f uv.lock ]; then cp uv.lock /dependency-manifests/ || exit 1; fi
+
 FROM ${BASE_IMAGE} as main
 
 # Nolog bash flag is currently ignored - but you can replace it with other 
flags (for example
@@ -2233,13 +2278,23 @@ RUN bash /scripts/docker/install_packaging_tools.sh
 
 COPY --from=scripts install_airflow_when_building_images.sh /scripts/docker/
 
+COPY --from=dependency-manifests /dependency-manifests/ ${AIRFLOW_SOURCES}/
+ARG UPGRADE_RANDOM_INDICATOR_STRING=""
+# Only preinstall locked third-party dependencies, with the same uv sync as 
the full installation.
+# A uv.lock that does not match the manifests only skips this warm-up: the 
full sync after COPY still
+# installs workspace packages from the current checkout and retains its 
resolution fallback. Any other
+# failure would fail the full sync as well, so it fails the build here.
+RUN 
--mount=type=cache,id=ci-$TARGETARCH-$DEPENDENCY_CACHE_EPOCH,target=/root/.cache/
 \
+    if [[ -z "${UPGRADE_RANDOM_INDICATOR_STRING}" ]]; then \
+        INSTALL_LOCKED_THIRD_PARTY_ONLY="true" bash 
/scripts/docker/install_airflow_when_building_images.sh; \
+    fi
+
 # We can copy everything here. The Context is filtered by dockerignore. This 
makes sure we are not
 # copying over stuff that is accidentally generated or that we do not need 
(such as egg-info)
 # if you want to add something that is missing and you expect to see it in the 
image you can
 # add it with ! in .dockerignore next to the airflow, test etc. directories 
there
 COPY . ${AIRFLOW_SOURCES}/
 
-ARG UPGRADE_RANDOM_INDICATOR_STRING=""
 ARG VERSION_SUFFIX=""
 
 ENV UPGRADE_RANDOM_INDICATOR_STRING=${UPGRADE_RANDOM_INDICATOR_STRING} \
diff --git a/scripts/docker/install_airflow_when_building_images.sh 
b/scripts/docker/install_airflow_when_building_images.sh
index 772465678a8..45e60d5fdbf 100644
--- a/scripts/docker/install_airflow_when_building_images.sh
+++ b/scripts/docker/install_airflow_when_building_images.sh
@@ -30,6 +30,11 @@
 # UPGRADE_RANDOM_INDICATOR_STRING - if set with random value determines that 
dependency upgrade should be done
 #                                   with highest resolution. It is random in 
order to trigger cache
 #                                   invalidation when building images
+# INSTALL_LOCKED_THIRD_PARTY_ONLY - if "true", only the third-party 
dependencies locked in uv.lock are installed
+#                                   from sources (--no-install-workspace), so 
only the pyproject.toml files
+#                                   and uv.lock are needed. When a frozen dry 
run shows that uv.lock does not
+#                                   match them, or for upgrade builds, nothing 
is installed; any other
+#                                   failure fails. The steps that need the 
workspace packages are skipped.
 #
 # shellcheck shell=bash disable=SC2086
 # shellcheck source=scripts/docker/common.sh
@@ -41,6 +46,15 @@ function install_from_sources() {
     if [[ ${VIRTUAL_ENV=} != "" ]]; then
         extra_sync_flags="--active"
     fi
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
+            echo
+            echo "${COLOR_YELLOW}Upgrade builds resolve dependencies anew; 
skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        extra_sync_flags="${extra_sync_flags} --no-install-workspace"
+    fi
     if [[ "${UPGRADE_RANDOM_INDICATOR_STRING=}" != "" ]]; then
         if [[ ${PACKAGING_TOOL_CMD} == "pip" ]]; then
             set +x
@@ -61,19 +75,35 @@ function install_from_sources() {
             ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
             --no-python-downloads --no-managed-python
     else
-        set +x
-        echo
-        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
-        echo
         # Use uv sync --frozen to install exactly what is pinned in uv.lock 
without re-resolving.
         # --no-binary-package is needed in order to avoid libxml and xmlsec 
using different version of
         # libxml2 (binary lxml embeds its own libxml2, while xmlsec uses 
system one).
         # See https://bugs.launchpad.net/lxml/+bug/2110068
+        # shellcheck disable=SC2206 # extra_sync_flags holds several flags
+        local -a frozen_sync=(uv sync --all-packages --frozen --group ci-image
+            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec
+            --no-python-downloads --no-managed-python)
+        set +x
+        # Without the workspace sources a lock that does not match the 
manifests cannot be re-resolved;
+        # the installation that adds the workspace packages does that later.
+        if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]] && ! 
"${frozen_sync[@]}" --dry-run; then
+            echo
+            echo "${COLOR_YELLOW}uv.lock does not match the dependency 
manifests; skipping the locked third-party installation.${COLOR_RESET}"
+            echo
+            return
+        fi
+        echo
+        echo "${COLOR_BLUE}Installing all packages from uv.lock 
(frozen).${COLOR_RESET}"
+        echo
         set -x
-        if ! uv sync --all-packages --frozen --group ci-image \
-            ${extra_sync_flags} --no-binary-package lxml --no-binary-package 
xmlsec \
-            --no-python-downloads --no-managed-python; then
+        if ! "${frozen_sync[@]}"; then
             set +x
+            if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+                echo
+                echo "${COLOR_RED}Failing because the locked third-party 
installation failed although uv.lock matches the dependency 
manifests.${COLOR_RESET}"
+                echo
+                exit 1
+            fi
             if [[ ${AIRFLOW_FALLBACK_NO_CONSTRAINTS_INSTALLATION} != "true" 
]]; then
                 echo
                 echo "${COLOR_RED}Failing because frozen uv.lock installation 
failed and fallback is disabled.${COLOR_RESET}"
@@ -163,6 +193,10 @@ function install_airflow_when_building_images() {
         install_from_external_spec
     fi
     set +x
+    if [[ ${INSTALL_LOCKED_THIRD_PARTY_ONLY=} == "true" ]]; then
+        # Packaging tools and 'pip check' are handled by the installation that 
adds the workspace packages
+        return
+    fi
     common::install_packaging_tools
     echo
     echo "${COLOR_BLUE}Running 'pip check'${COLOR_RESET}"

Reply via email to