zozo123 commented on code in PR #74173:
URL: https://github.com/apache/airflow/pull/74173#discussion_r4221175135


##########
.github/workflows/ci-image-build.yml:
##########
@@ -401,3 +401,70 @@ jobs:
           steps.stashed-image.outputs.reusable != 'true'
       - name: "Check disk space after build"
         run: df -H
+
+      # Snapshot only after cache publication: pruning removes the mount-cache 
image.
+      # Keep creation in trusted workflow commands; the configurable checkout 
is untrusted.

Review Comment:
   Agreed: the builder already runs PR-authored Breeze code, so the inline 
workflow commands did not provide an extra isolation boundary. Removed that 
claim and moved creation into a `create` subcommand in 
`docker_data_root_snapshot.sh`, sharing the fingerprint check, stop/start 
helpers and EXIT recovery with restore. The workflow now calls the script after 
cache publication, and the tests invoke both subcommands directly rather than 
extracting the YAML run block. All 24 snapshot tests pass; the full scripts 
suite on the rebased branch passed with 1,696 tests and one skip. Pushed in 
head `3793fc0ad560`.
   
   ---
   Drafted-by: Codex (GPT-6) (no human review before posting)



##########
.github/actions/prepare_breeze_and_image/action.yml:
##########
@@ -79,6 +79,33 @@ runs:
       run: |
         echo "Checking free space!"
         df -H
+    - name: "Restore CI image snapshot ${{ inputs.platform }}:${{ 
inputs.python }}"
+      continue-on-error: true
+      uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c 
 # v8.0.1
+      with:
+        name: >-
+          ci-image-snapshot-v2-${{ inputs.python }}-${{
+          inputs.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
+        path: "/mnt/"
+      id: restore-snapshot
+      if: >
+        github.event_name == 'pull_request' &&
+        inputs.image-type == 'ci' && inputs.image-stash-ref == ''
+    # Falls back to the image stash below whenever the snapshot does not fit 
this runner's daemon.
+    - name: "Unpack CI image snapshot ${{ inputs.platform }}:${{ inputs.python 
}}"
+      id: snapshot
+      env:
+        PLATFORM: ${{ inputs.platform }}
+        PYTHON: ${{ inputs.python }}
+      run: |
+        if ./scripts/ci/docker_data_root_snapshot.sh restore \
+            "/mnt/ci-image-snapshot-${PLATFORM//\//_}-${PYTHON}.tar.zst"; then
+          echo "restored=true" >> "${GITHUB_OUTPUT}"
+        else
+          echo "Falling back to loading the image stash"

Review Comment:
   Fixed in head `3793fc0ad560`: restore registers the exact archive and 
`.meta` paths with its EXIT cleanup before checking whether either file exists. 
Every restore exit now removes both files, including missing/malformed 
metadata, stale checkout, fingerprint/storage/inventory rejection, extraction 
failure and failed image startup. This frees `/mnt` before the stash fallback 
starts. The regression harness asserts archive and metadata removal for every 
restore invocation, covering success and all tested rejection/recovery paths; 
24 tests pass.
   
   ---
   Drafted-by: Codex (GPT-6) (no human review before posting)



##########
.github/workflows/ci-image-build.yml:
##########
@@ -401,3 +401,70 @@ jobs:
           steps.stashed-image.outputs.reusable != 'true'
       - name: "Check disk space after build"
         run: df -H
+
+      # Snapshot only after cache publication: pruning removes the mount-cache 
image.
+      # Keep creation in trusted workflow commands; the configurable checkout 
is untrusted.
+      - name: "Snapshot CI image ${{ inputs.platform }}:${{ 
env.PYTHON_MAJOR_MINOR_VERSION }}"
+        id: snapshot-export
+        continue-on-error: true
+        timeout-minutes: 5
+        env:
+          # Docker is bind-mounted from /mnt on AMD; write the archive on the 
root disk.
+          SNAPSHOT_FILE: >-
+            ${{ runner.temp }}/ci-image-snapshot-${{
+            inputs.platform == 'linux/amd64' && 'linux_amd64' || 'linux_arm64'
+            }}-${{ env.PYTHON_MAJOR_MINOR_VERSION }}.tar.zst
+        run: |
+          daemon_stopped=false
+          function restart_docker_on_exit() {
+            local result=$?
+            trap - EXIT
+            if [[ "${daemon_stopped}" == true ]]; then
+              sudo systemctl start docker
+            fi
+            exit "${result}"
+          }
+          trap restart_docker_on_exit EXIT
+          daemon="$(docker info --format \
+            '{{.ServerVersion}} {{.Driver}} {{.Architecture}} 
{{.DockerRootDir}}')"
+          read -r _ driver _ root <<< "${daemon}"
+          if [[ "${driver}" != overlay2 || "${root}" != /var/lib/docker ]]; 
then
+            echo "::warning::Unsupported Docker image store; skipping snapshot 
creation: ${daemon}"
+            exit 3
+          fi
+          image_id="$(docker images --quiet --filter 
'label=org.apache.airflow.image=airflow-ci' | sort -u)"
+          if [[ -z "${image_id}" || "${image_id}" == *$'\n'* ]]; then
+            echo "Expected exactly one CI image in the daemon, found: 
'${image_id}'" >&2
+            exit 1
+          fi
+          docker ps --all --quiet | xargs --no-run-if-empty docker rm --force 
>/dev/null
+          docker images --quiet | sort -u | {
+            grep --invert-match --fixed-strings --line-regexp "${image_id}" || 
true
+          } | xargs --no-run-if-empty docker rmi --force >/dev/null
+          docker builder prune --all --force >/dev/null
+          printf '%s %s %s\n' "${daemon}" "${image_id}" "$(git rev-parse 
HEAD)" \
+            > "${SNAPSHOT_FILE}.meta"
+          daemon_stopped=true
+          sudo systemctl stop docker.socket docker
+          sudo tar --directory /var/lib/docker --xattrs --acls --numeric-owner 
\
+            --create --file - image overlay2 \
+            | zstd -3 -T0 --quiet --force -o "${SNAPSHOT_FILE}"
+          sudo systemctl start docker
+          daemon_stopped=false
+        shell: bash
+        if: >
+          github.event_name == 'pull_request' &&
+          inputs.upload-image-artifact == 'true' && inputs.image-stash-ref == 
'' &&
+          (inputs.checkout-ref == '' || inputs.checkout-ref == github.sha)
+      - name: "Upload CI image snapshot ${{ inputs.platform }}:${{ 
env.PYTHON_MAJOR_MINOR_VERSION }}"
+        continue-on-error: true
+        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a 
 # v7.0.1
+        with:
+          name: >-
+            ci-image-snapshot-v2-${{ env.PYTHON_MAJOR_MINOR_VERSION }}-${{
+            inputs.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
+          path: "${{ runner.temp }}/ci-image-snapshot-*-${{ 
env.PYTHON_MAJOR_MINOR_VERSION }}.tar.zst*"
+          if-no-files-found: 'error'
+          compression-level: '0'
+          retention-days: '2'

Review Comment:
   Agreed. The PR description now explicitly records 2.52 GB for the snapshot 
plus 2.35 GB for the fallback stash: approximately 4.87 GB per built CI image / 
Python-platform combination per run, with two-day retention for both 
transports. Matrix size and overlapping retained runs multiply that amount; it 
is not a 4.9 GB cap for the whole workflow. The description treats this as a 
CI-capacity/artifact-quota tradeoff requiring maintainer acceptance before 
merging, and claims no whole-workflow speedup. I have not assumed ASF budget 
approval.
   
   ---
   Drafted-by: Codex (GPT-6) (no human review before posting)



##########
Dockerfile.ci:
##########
@@ -1966,13 +1982,24 @@ 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. The full sync after COPY 
still installs
+# workspace packages from the current checkout and retains its existing 
resolution fallback.
+RUN 
--mount=type=cache,id=ci-$TARGETARCH-$DEPENDENCY_CACHE_EPOCH,target=/root/.cache/
 \
+    if [[ -z "${UPGRADE_RANDOM_INDICATOR_STRING}" ]]; then \
+        uv sync --all-packages --frozen --group ci-image 
--no-install-workspace \
+            --no-binary-package lxml --no-binary-package xmlsec \
+            --no-python-downloads --no-managed-python || \
+            echo "Dependency preinstallation failed; deferring to the full 
source installation"; \

Review Comment:
   Agreed: a failed preinstall must not become a successful cached partial 
layer. Removed the `|| echo` so installation failure now fails the build. Per 
your review's split request, the entire dependency-layer change is removed from 
#74173 and moved to draft #74466, which contains this correction. The 
Dockerfile string-splitting/Bash-semantics tests were also dropped. That draft 
records your earlier 28.7-second preinstall measurement and explicitly leaves 
full-image equivalence and registry cache-hit performance unverified pending 
separate validation. Regular and manual Dockerfile checks pass.
   
   ---
   Drafted-by: Codex (GPT-6) (no human review before posting)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to