This is an automated email from the ASF dual-hosted git repository. potiuk pushed a commit to branch seed-ci-image-cache-from-stashed-image in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 58b4fad0e37985cab71d00a7b08b16067cdbfdaf Author: Jarek Potiuk <[email protected]> AuthorDate: Wed Jul 29 00:37:17 2026 +0200 Speed up docs publishes by reusing the previously built CI image Publishing docs for a ref cut before main pays for a from-scratch CI image build. The registry cache the build reads is main's, and the first Dockerfile.ci change made since the ref was cut breaks the cache chain at the third layer - #70285, which bakes Node.js and pnpm into the image, is by itself enough to make the twenty-minute Python build run again. Nothing caches those layers for the ref being documented except the image the previous publish of that same ref already built. That image could not serve as a cache until now. BuildKit imports the cache manifest of an image loaded from a tarball and then discards every record whose layers it cannot pull from a registry, so a docker load contributes nothing at all; serving the image from a registry on localhost is what makes its records usable. Recording those records in the first place needs inline cache metadata, which no image carried before. --- .github/workflows/ci-image-build.yml | 78 ++++++++++++++++ .github/workflows/publish-docs-to-s3.yml | 7 ++ dev/breeze/doc/images/output_ci-image_build.svg | 104 ++++++++++++--------- dev/breeze/doc/images/output_ci-image_build.txt | 2 +- dev/breeze/doc/images/output_ci-image_load.svg | 22 +++-- dev/breeze/doc/images/output_ci-image_load.txt | 2 +- .../airflow_breeze/commands/ci_image_commands.py | 16 ++++ .../commands/ci_image_commands_config.py | 2 + .../commands/common_image_options.py | 11 +++ .../airflow_breeze/params/common_build_params.py | 10 ++ dev/breeze/tests/test_build_ci_params.py | 57 +++++++++++ 11 files changed, 254 insertions(+), 57 deletions(-) diff --git a/.github/workflows/ci-image-build.yml b/.github/workflows/ci-image-build.yml index f9aaa3aa473..df116423717 100644 --- a/.github/workflows/ci-image-build.yml +++ b/.github/workflows/ci-image-build.yml @@ -103,6 +103,25 @@ on: # yamllint disable-line rule:truthy description: "Disable airflow repo cache read from main." required: true type: string + seed-cache-from-stashed-image: + description: > + Seed the build cache from the CI image an earlier run stashed (true/false). Worth it + where the registry cache is built for a different branch than the sources - the + previous image matches them layer for layer, the registry cache of another branch may + match nothing. + required: false + default: "false" + type: string + image-stash-suffix: + description: > + Appended verbatim to the key of an extra copy of the image stash (so it needs its own + leading separator). Callers that build many different refs from one branch pass the ref + here to keep a copy per ref: they all share the plain per-branch stash and would + otherwise seed each other's builds, and refs far enough apart make poor caches for one + another. Seeding prefers this copy and falls back to the per-branch one. + required: false + default: "" + type: string permissions: contents: read jobs: @@ -166,6 +185,44 @@ jobs: breeze ci-image import-mount-cache --cache-file /tmp/ci-cache-mount-save-v3-${PYTHON_MAJOR_MINOR_VERSION}.tar.gz if: steps.restore-cache-mount.outputs.stash-hit == 'true' + # BuildKit reads an image's recorded layer cache only when it can pull that image's layers + # from a registry - one merely loaded into the docker engine contributes nothing. So the + # image an earlier run stashed is served back to the build from a registry on localhost. + - name: > + Restore CI docker image for this ref + ${{ inputs.platform }}:${{ env.PYTHON_MAJOR_MINOR_VERSION }} + uses: apache/infrastructure-actions/stash/restore@49df447b39b18354895520e0a63731b7cad7cbec + with: + key: "ci-image-save-v3-${{ inputs.platform }}-${{ env.PYTHON_MAJOR_MINOR_VERSION }}\ + ${{ inputs.image-stash-suffix }}" + path: "/mnt/" + only-current-branch: 'true' + id: restore-cache-image-for-ref + if: inputs.seed-cache-from-stashed-image == 'true' && inputs.image-stash-suffix != '' + - name: "Restore CI docker image ${{ inputs.platform }}:${{ env.PYTHON_MAJOR_MINOR_VERSION }}" + uses: apache/infrastructure-actions/stash/restore@49df447b39b18354895520e0a63731b7cad7cbec + with: + key: "ci-image-save-v3-${{ inputs.platform }}-${{ env.PYTHON_MAJOR_MINOR_VERSION }}" + path: "/mnt/" + only-current-branch: 'true' + id: restore-cache-image + if: > + inputs.seed-cache-from-stashed-image == 'true' && + steps.restore-cache-image-for-ref.outputs.stash-hit != 'true' + - name: "Serve stashed image as cache ${{ inputs.platform }}:${{ env.PYTHON_MAJOR_MINOR_VERSION }}" + env: + PLATFORM: ${{ inputs.platform }} + CACHE_IMAGE: "localhost:5000/ci-image-cache:${{ env.PYTHON_MAJOR_MINOR_VERSION }}" + run: | + docker run -d --name cache-registry -p 5000:5000 registry:2 + breeze ci-image load --platform "${PLATFORM}" --python "${PYTHON_MAJOR_MINOR_VERSION}" \ + --image-file-dir "/mnt" --tag-as "${CACHE_IMAGE}" + docker push "${CACHE_IMAGE}" + echo "CACHE_FROM_IMAGE=${CACHE_IMAGE}" >> "${GITHUB_ENV}" + shell: bash + if: > + steps.restore-cache-image-for-ref.outputs.stash-hit == 'true' || + steps.restore-cache-image.outputs.stash-hit == 'true' - name: "Login to ghcr.io" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -203,6 +260,14 @@ jobs: PUSH: ${{ inputs.push-image }} VERBOSE: "true" PLATFORM: ${{ inputs.platform }} + - name: "Stop serving the cache image" + # The registry holds a second copy of a multi-gigabyte image and the export below needs + # the room. + run: | + docker rm -f cache-registry + docker rmi "${CACHE_FROM_IMAGE}" + shell: bash + if: always() && env.CACHE_FROM_IMAGE != '' - name: "Export CI docker image ${{ env.PYTHON_MAJOR_MINOR_VERSION }}" env: PLATFORM: ${{ inputs.platform }} @@ -216,6 +281,19 @@ jobs: if-no-files-found: 'error' retention-days: '2' if: inputs.upload-image-artifact == 'true' + # Everything in this run reads the stash above; this copy exists only so the next run + # built from the same ref can seed its cache from sources it actually matches. It outlives + # that one by days - the same ref is rebuilt three days apart at the very least, so the + # two-day retention the shared stash gets would expire it before it is ever read. + - name: "Stash CI docker image for this ref ${{ env.PYTHON_MAJOR_MINOR_VERSION }}" + uses: apache/infrastructure-actions/stash/save@49df447b39b18354895520e0a63731b7cad7cbec + with: + key: "ci-image-save-v3-${{ inputs.platform }}-${{ env.PYTHON_MAJOR_MINOR_VERSION }}\ + ${{ inputs.image-stash-suffix }}" + path: "/mnt/ci-image-save-*-${{ env.PYTHON_MAJOR_MINOR_VERSION }}.tar" + if-no-files-found: 'error' + retention-days: '6' + if: inputs.upload-image-artifact == 'true' && inputs.image-stash-suffix != '' - name: "Export mount cache ${{ inputs.platform }}:${{ env.PYTHON_MAJOR_MINOR_VERSION }}" env: PYTHON_MAJOR_MINOR_VERSION: ${{ env.PYTHON_MAJOR_MINOR_VERSION }} diff --git a/.github/workflows/publish-docs-to-s3.yml b/.github/workflows/publish-docs-to-s3.yml index 6345afa84b6..5f375004d1f 100644 --- a/.github/workflows/publish-docs-to-s3.yml +++ b/.github/workflows/publish-docs-to-s3.yml @@ -247,6 +247,13 @@ jobs: # ci-image-build.yml always restores it, so consecutive publishes stop re-downloading # the whole dependency set. upload-mount-cache-artifact: "true" + # Docs are published from refs cut days or weeks before main, and main's registry cache + # stops matching at the first Dockerfile.ci change made since the cut - #70285 alone + # costs a from-scratch Python build. The image the previous publish stashed was built + # from these very sources, so it is the cache main's cannot be. Publishes of different + # refs share one branch-scoped stash, hence the per-ref copy to prefer over it. + seed-cache-from-stashed-image: "true" + image-stash-suffix: "-${{ inputs.ref }}" python-versions: ${{ format('["{0}"]', needs.build-info.outputs.default-python-version) }} branch: "main" constraints-branch: "constraints-main" diff --git a/dev/breeze/doc/images/output_ci-image_build.svg b/dev/breeze/doc/images/output_ci-image_build.svg index f58ee2f3031..1f020988b46 100644 --- a/dev/breeze/doc/images/output_ci-image_build.svg +++ b/dev/breeze/doc/images/output_ci-image_build.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 1482 1953.1999999999998" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 1482 2026.3999999999999" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -43,7 +43,7 @@ <defs> <clipPath id="breeze-ci-image-build-clip-terminal"> - <rect x="0" y="0" width="1463.0" height="1902.1999999999998" /> + <rect x="0" y="0" width="1463.0" height="1975.3999999999999" /> </clipPath> <clipPath id="breeze-ci-image-build-line-0"> <rect x="0" y="1.5" width="1464" height="24.65"/> @@ -276,9 +276,18 @@ <clipPath id="breeze-ci-image-build-line-76"> <rect x="0" y="1855.9" width="1464" height="24.65"/> </clipPath> +<clipPath id="breeze-ci-image-build-line-77"> + <rect x="0" y="1880.3" width="1464" height="24.65"/> + </clipPath> +<clipPath id="breeze-ci-image-build-line-78"> + <rect x="0" y="1904.7" width="1464" height="24.65"/> + </clipPath> +<clipPath id="breeze-ci-image-build-line-79"> + <rect x="0" y="1929.1" width="1464" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="1951.2" rx="8"/><text class="breeze-ci-image-build-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: ci-image build</text> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="2024.4" rx="8"/><text class="breeze-ci-image-build-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: ci-image build</text> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> @@ -323,49 +332,52 @@ </text><text class="breeze-ci-image-build-r5" x="0" y="800.8" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-32)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="800.8" textLength="512.4" clip-path="url(#breeze-ci-image-build-line-32)"> Advanced build options (for power users) </text><text class="breeze-ci-image-build-r5" x="536.8" y="800.8" textLength="902.8" clip-path="url(#breeze-ci-image-build-line-32)">──────────────────── [...] </text><text class="breeze-ci-image-build-r5" x="0" y="825.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-33)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="825.2" textLength="366" clip-path="url(#breeze-ci-image-build-line-33)">--additional-pip-install-flags</text><text class="breeze-ci-image-build-r1" x="414.8" y="825.2" textLength="1024.8" clip-path="url(#breeze-ci-image-build-line-33)">Additional flags added to `pip install`  [...] </text><text class="breeze-ci-image-build-r5" x="0" y="849.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-34)">│</text><text class="breeze-ci-image-build-r7" x="414.8" y="849.6" textLength="73.2" clip-path="url(#breeze-ci-image-build-line-34)">(TEXT)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="849.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-34)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="849.6" textLength="12.2" clip-pat [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="874" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-35)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="874" textLength="366" clip-path="url(#breeze-ci-image-build-line-35)">--commit-sha                  </text><text class="breeze-ci-image-build-r1" x="414.8" y="874" textLength="549" clip-path="url(#breeze-ci-image-buil [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="898.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-36)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="898.4" textLength="366" clip-path="url(#breeze-ci-image-build-line-36)">--debian-version              </text><text class="breeze-ci-image-build-r1" x="414.8" y="898.4" textLength="805.2" clip-path="url(#breeze-ci-image-build-line-36)"> [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="922.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-37)">│</text><text class="breeze-ci-image-build-r5" x="414.8" y="922.8" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-37)">bookworm]</text><text class="breeze-ci-image-build-r7" x="536.8" y="922.8" textLength="122" clip-path="url(#breeze-ci-image-build-line-37)">(bookworm)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="922.8" textLength="1 [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="947.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-38)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="947.2" textLength="366" clip-path="url(#breeze-ci-image-build-line-38)">--disable-airflow-repo-cache  </text><text class="breeze-ci-image-build-r1" x="414.8" y="947.2" textLength="658.8" clip-path="url(#breeze-ci-image-build-line-38)">Disable cache from Airflow repository [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="971.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-39)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="971.6" textLength="366" clip-path="url(#breeze-ci-image-build-line-39)">--install-mysql-client-type   </text><text class="breeze-ci-image-build-r1" x="414.8" y="971.6" textLength="488" clip-path="url(#breeze-ci-image-build-line-39)">Which client to choose when inst [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="996" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-40)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="996" textLength="366" clip-path="url(#breeze-ci-image-build-line-40)">--python-image                </text><text class="breeze-ci-image-build-r1" x="414.8" y="996" textLength="1024.8" clip-path="url(#breeze-ci-image-build-line- [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1020.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-41)">│</text><text class="breeze-ci-image-build-r1" x="414.8" y="1020.4" textLength="561.2" clip-path="url(#breeze-ci-image-build-line-41)">something like: python:VERSION-slim-bookworm. </text><text class="breeze-ci-image-build-r7" x="976" y="1020.4" textLength="73.2" clip-path="url(#breeze-ci-image-build-line-41)">(TEXT)</text><text class="breeze-ci- [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1044.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-42)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1044.8" textLength="97.6" clip-path="url(#breeze-ci-image-build-line-42)">--use-uv</text><text class="breeze-ci-image-build-r1" x="122" y="1044.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-42)">/</text><text class="breeze-ci-image-build-r4" x="134.2" y="1044.8" textLength="134.2" clip [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1069.2" textLength="1464" clip-path="url(#breeze-ci-image-build-line-43)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1069.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-43)"> -</text><text class="breeze-ci-image-build-r5" x="0" y="1093.6" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-44)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1093.6" textLength="597.8" clip-path="url(#breeze-ci-image-build-line-44)"> Selecting constraint location (for power users) </text><text class="breeze-ci-image-build-r5" x="622.2" y="1093.6" textLength="817.4" clip-path="url(#breeze-ci-image-build-line-44)">────────── [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1118" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-45)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1118" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-45)">--airflow-constraints-location </text><text class="breeze-ci-image-build-r1" x="427" y="1118" textLength="915" clip-path="url(#breeze-ci-image-build-line-45)">Location of airflow constraints to use  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1142.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-46)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1142.4" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-46)">--airflow-constraints-mode     </text><text class="breeze-ci-image-build-r1" x="427" y="1142.4" textLength="671" clip-path="url(#breeze-ci-image-build-line-46)">Mode of constraints for  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1166.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-47)">│</text><text class="breeze-ci-image-build-r5" x="427" y="1166.8" textLength="353.8" clip-path="url(#breeze-ci-image-build-line-47)">constraints-source-providers]</text><text class="breeze-ci-image-build-r7" x="793" y="1166.8" textLength="561.2" clip-path="url(#breeze-ci-image-build-line-47)">(constraints-source-providers | constraints | </t [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1191.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-48)">│</text><text class="breeze-ci-image-build-r7" x="427" y="1191.2" textLength="305" clip-path="url(#breeze-ci-image-build-line-48)">constraints-no-providers)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="1191.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-48)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="1191.2" textLe [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1215.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-49)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1215.6" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-49)">--airflow-constraints-reference</text><text class="breeze-ci-image-build-r1" x="427" y="1215.6" textLength="646.6" clip-path="url(#breeze-ci-image-build-line-49)">Constraint reference to use when building [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1240" textLength="1464" clip-path="url(#breeze-ci-image-build-line-50)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1240" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-50)"> -</text><text class="breeze-ci-image-build-r5" x="0" y="1264.4" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-51)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1264.4" textLength="634.4" clip-path="url(#breeze-ci-image-build-line-51)"> Choosing dependencies and extras (for power users) </text><text class="breeze-ci-image-build-r5" x="658.8" y="1264.4" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-51)">── [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1288.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-52)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1288.8" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-52)">--additional-airflow-extras </text><text class="breeze-ci-image-build-r1" x="390.4" y="1288.8" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-52)">Additional extra package while installing& [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1313.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-53)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1313.2" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-53)">--additional-python-deps    </text><text class="breeze-ci-image-build-r1" x="390.4" y="1313.2" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-53)">Additional python dependencies t [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1337.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-54)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1337.6" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-54)">--dev-apt-deps              </text><text class="breeze-ci-image-build-r1" x="390.4" y="1337.6" textLength="658.8" clip-path="url(#breeze-ci-image-build-line-54 [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1362" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-55)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1362" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-55)">--additional-dev-apt-deps   </text><text class="breeze-ci-image-build-r1" x="390.4" y="1362" textLength="793" clip-path="url(#breeze-ci-image-build-line-55)">Additional apt dev dependencies to  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1386.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-56)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1386.4" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-56)">--dev-apt-command           </text><text class="breeze-ci-image-build-r1" x="390.4" y="1386.4" textLength="634.4" clip-path="url(#breeze-ci-image-build-line-56)">Command  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1410.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-57)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1410.8" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-57)">--additional-dev-apt-command</text><text class="breeze-ci-image-build-r1" x="390.4" y="1410.8" textLength="768.6" clip-path="url(#breeze-ci-image-build-line-57)">Additional command executed before dev apt [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1435.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-58)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1435.2" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-58)">--additional-dev-apt-env    </text><text class="breeze-ci-image-build-r1" x="390.4" y="1435.2" textLength="817.4" clip-path="url(#breeze-ci-image-build-line-58)">Additional environment variables  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1459.6" textLength="1464" clip-path="url(#breeze-ci-image-build-line-59)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1459.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-59)"> -</text><text class="breeze-ci-image-build-r5" x="0" y="1484" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-60)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1484" textLength="622.2" clip-path="url(#breeze-ci-image-build-line-60)"> Preparing cache and push (for maintainers and CI) </text><text class="breeze-ci-image-build-r5" x="646.6" y="1484" textLength="793" clip-path="url(#breeze-ci-image-build-line-60)">────── [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1508.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-61)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1508.4" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-61)">--builder             </text><text class="breeze-ci-image-build-r1" x="317.2" y="1508.4" textLength="768.6" clip-path="url(#breeze-ci-image-build-line-61)">Buildx&# [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1532.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-62)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1532.8" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-62)">--platform            </text><text class="breeze-ci-image-build-r1" x="317.2" y="1532.8" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-62)">Platform  [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1557.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-63)">│</text><text class="breeze-ci-image-build-r7" x="317.2" y="1557.2" textLength="292.8" clip-path="url(#breeze-ci-image-build-line-63)">linux/amd64,linux/arm64)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="1557.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-63)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="1557.2" tex [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1581.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-64)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1581.6" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-64)">--push                </text><text class="breeze-ci-image-build-r1" x="317.2" y="1581.6" textLength="353.8" clip-path="url(#breeze-ci-image-build-lin [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1606" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-65)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1606" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-65)">--prepare-buildx-cache</text><text class="breeze-ci-image-build-r1" x="317.2" y="1606" textLength="1122.4" clip-path="url(#breeze-ci-image-build-line-65)">Prepares build cache (this is done as separ [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1630.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-66)">│</text><text class="breeze-ci-image-build-r1" x="317.2" y="1630.4" textLength="1122.4" clip-path="url(#breeze-ci-image-build-line-66)">image).                                     [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1654.8" textLength="1464" clip-path="url(#breeze-ci-image-build-line-67)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1654.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-67)"> -</text><text class="breeze-ci-image-build-r5" x="0" y="1679.2" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-68)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1679.2" textLength="280.6" clip-path="url(#breeze-ci-image-build-line-68)"> GitHub authentication </text><text class="breeze-ci-image-build-r5" x="305" y="1679.2" textLength="1134.6" clip-path="url(#breeze-ci-image-build-line-68)">───────────────────────────────────────────────────────── [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1703.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-69)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1703.6" textLength="231.8" clip-path="url(#breeze-ci-image-build-line-69)">--github-repository</text><text class="breeze-ci-image-build-r6" x="280.6" y="1703.6" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-69)">-g</text><text class="breeze-ci-image-build-r1" x="329.4" y="1703.6" textLeng [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1728" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-70)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1728" textLength="231.8" clip-path="url(#breeze-ci-image-build-line-70)">--github-token     </text><text class="breeze-ci-image-build-r1" x="329.4" y="1728" textLength="512.4" clip-path="url(#breeze-ci-image-build-line-70)">The token used to authenticate t [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1752.4" textLength="1464" clip-path="url(#breeze-ci-image-build-line-71)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1752.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-71)"> -</text><text class="breeze-ci-image-build-r5" x="0" y="1776.8" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-72)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1776.8" textLength="195.2" clip-path="url(#breeze-ci-image-build-line-72)"> Common options </text><text class="breeze-ci-image-build-r5" x="219.6" y="1776.8" textLength="1220" clip-path="url(#breeze-ci-image-build-line-72)">──────────────────────────────────────────────────────────────── [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1801.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-73)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1801.2" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-73)">--answer </text><text class="breeze-ci-image-build-r6" x="158.6" y="1801.2" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-73)">-a</text><text class="breeze-ci-image-build-r1" x="207.4" y="1801.2" textLength="3 [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1825.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-74)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1825.6" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-74)">--dry-run</text><text class="breeze-ci-image-build-r6" x="158.6" y="1825.6" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-74)">-D</text><text class="breeze-ci-image-build-r1" x="207.4" y="1825.6" textLength="719.8" [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1850" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-75)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1850" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-75)">--verbose</text><text class="breeze-ci-image-build-r6" x="158.6" y="1850" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-75)">-v</text><text class="breeze-ci-image-build-r1" x="207.4" y="1850" textLength="585.6" clip-pa [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1874.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-76)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1874.4" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-76)">--help   </text><text class="breeze-ci-image-build-r6" x="158.6" y="1874.4" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-76)">-h</text><text class="breeze-ci-image-build-r1" x="207.4" y="1874.4" tex [...] -</text><text class="breeze-ci-image-build-r5" x="0" y="1898.8" textLength="1464" clip-path="url(#breeze-ci-image-build-line-77)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1898.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-77)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="874" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-35)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="874" textLength="366" clip-path="url(#breeze-ci-image-build-line-35)">--cache-from-image            </text><text class="breeze-ci-image-build-r1" x="414.8" y="874" textLength="1024.8" clip-path="url(#breeze-ci-image-build-line-35)">Additional  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="898.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-36)">│</text><text class="breeze-ci-image-build-r1" x="414.8" y="898.4" textLength="1024.8" clip-path="url(#breeze-ci-image-build-line-36)">registry, an image only present in the local docker engine contributes no cache.    </text><text class="breeze-ci-image-build-r5" x="1451.8" y="898.4 [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="922.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-37)">│</text><text class="breeze-ci-image-build-r7" x="414.8" y="922.8" textLength="73.2" clip-path="url(#breeze-ci-image-build-line-37)">(TEXT)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="922.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-37)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="922.8" textLength="12.2" clip-pat [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="947.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-38)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="947.2" textLength="366" clip-path="url(#breeze-ci-image-build-line-38)">--commit-sha                  </text><text class="breeze-ci-image-build-r1" x="414.8" y="947.2" textLength="549" clip-path="url(#breeze-ci-imag [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="971.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-39)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="971.6" textLength="366" clip-path="url(#breeze-ci-image-build-line-39)">--debian-version              </text><text class="breeze-ci-image-build-r1" x="414.8" y="971.6" textLength="805.2" clip-path="url(#breeze-ci-image-build-line-39)"> [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="996" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-40)">│</text><text class="breeze-ci-image-build-r5" x="414.8" y="996" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-40)">bookworm]</text><text class="breeze-ci-image-build-r7" x="536.8" y="996" textLength="122" clip-path="url(#breeze-ci-image-build-line-40)">(bookworm)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="996" textLength="12.2" cli [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1020.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-41)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1020.4" textLength="366" clip-path="url(#breeze-ci-image-build-line-41)">--disable-airflow-repo-cache  </text><text class="breeze-ci-image-build-r1" x="414.8" y="1020.4" textLength="658.8" clip-path="url(#breeze-ci-image-build-line-41)">Disable cache from Airflow repository [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1044.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-42)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1044.8" textLength="366" clip-path="url(#breeze-ci-image-build-line-42)">--install-mysql-client-type   </text><text class="breeze-ci-image-build-r1" x="414.8" y="1044.8" textLength="488" clip-path="url(#breeze-ci-image-build-line-42)">Which client to choose when i [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1069.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-43)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1069.2" textLength="366" clip-path="url(#breeze-ci-image-build-line-43)">--python-image                </text><text class="breeze-ci-image-build-r1" x="414.8" y="1069.2" textLength="1024.8" clip-path="url(#breeze-ci-image-bu [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1093.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-44)">│</text><text class="breeze-ci-image-build-r1" x="414.8" y="1093.6" textLength="561.2" clip-path="url(#breeze-ci-image-build-line-44)">something like: python:VERSION-slim-bookworm. </text><text class="breeze-ci-image-build-r7" x="976" y="1093.6" textLength="73.2" clip-path="url(#breeze-ci-image-build-line-44)">(TEXT)</text><text class="breeze-ci- [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1118" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-45)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1118" textLength="97.6" clip-path="url(#breeze-ci-image-build-line-45)">--use-uv</text><text class="breeze-ci-image-build-r1" x="122" y="1118" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-45)">/</text><text class="breeze-ci-image-build-r4" x="134.2" y="1118" textLength="134.2" clip-path="u [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1142.4" textLength="1464" clip-path="url(#breeze-ci-image-build-line-46)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1142.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-46)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="1166.8" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-47)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1166.8" textLength="597.8" clip-path="url(#breeze-ci-image-build-line-47)"> Selecting constraint location (for power users) </text><text class="breeze-ci-image-build-r5" x="622.2" y="1166.8" textLength="817.4" clip-path="url(#breeze-ci-image-build-line-47)">────────── [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1191.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-48)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1191.2" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-48)">--airflow-constraints-location </text><text class="breeze-ci-image-build-r1" x="427" y="1191.2" textLength="915" clip-path="url(#breeze-ci-image-build-line-48)">Location of airflow constraints to use [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1215.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-49)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1215.6" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-49)">--airflow-constraints-mode     </text><text class="breeze-ci-image-build-r1" x="427" y="1215.6" textLength="671" clip-path="url(#breeze-ci-image-build-line-49)">Mode of constraints for  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1240" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-50)">│</text><text class="breeze-ci-image-build-r5" x="427" y="1240" textLength="353.8" clip-path="url(#breeze-ci-image-build-line-50)">constraints-source-providers]</text><text class="breeze-ci-image-build-r7" x="793" y="1240" textLength="561.2" clip-path="url(#breeze-ci-image-build-line-50)">(constraints-source-providers | constraints | </text><t [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1264.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-51)">│</text><text class="breeze-ci-image-build-r7" x="427" y="1264.4" textLength="305" clip-path="url(#breeze-ci-image-build-line-51)">constraints-no-providers)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="1264.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-51)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="1264.4" textLe [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1288.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-52)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1288.8" textLength="378.2" clip-path="url(#breeze-ci-image-build-line-52)">--airflow-constraints-reference</text><text class="breeze-ci-image-build-r1" x="427" y="1288.8" textLength="646.6" clip-path="url(#breeze-ci-image-build-line-52)">Constraint reference to use when building [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1313.2" textLength="1464" clip-path="url(#breeze-ci-image-build-line-53)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1313.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-53)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="1337.6" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-54)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1337.6" textLength="634.4" clip-path="url(#breeze-ci-image-build-line-54)"> Choosing dependencies and extras (for power users) </text><text class="breeze-ci-image-build-r5" x="658.8" y="1337.6" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-54)">── [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1362" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-55)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1362" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-55)">--additional-airflow-extras </text><text class="breeze-ci-image-build-r1" x="390.4" y="1362" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-55)">Additional extra package while installing A [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1386.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-56)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1386.4" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-56)">--additional-python-deps    </text><text class="breeze-ci-image-build-r1" x="390.4" y="1386.4" textLength="780.8" clip-path="url(#breeze-ci-image-build-line-56)">Additional python dependencies t [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1410.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-57)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1410.8" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-57)">--dev-apt-deps              </text><text class="breeze-ci-image-build-r1" x="390.4" y="1410.8" textLength="658.8" clip-path="url(#breeze-ci-image-build-line-57 [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1435.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-58)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1435.2" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-58)">--additional-dev-apt-deps   </text><text class="breeze-ci-image-build-r1" x="390.4" y="1435.2" textLength="793" clip-path="url(#breeze-ci-image-build-line-58)">Additional apt dev dependencies to [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1459.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-59)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1459.6" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-59)">--dev-apt-command           </text><text class="breeze-ci-image-build-r1" x="390.4" y="1459.6" textLength="634.4" clip-path="url(#breeze-ci-image-build-line-59)">Command  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1484" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-60)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1484" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-60)">--additional-dev-apt-command</text><text class="breeze-ci-image-build-r1" x="390.4" y="1484" textLength="768.6" clip-path="url(#breeze-ci-image-build-line-60)">Additional command executed before dev apt  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1508.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-61)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1508.4" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-61)">--additional-dev-apt-env    </text><text class="breeze-ci-image-build-r1" x="390.4" y="1508.4" textLength="817.4" clip-path="url(#breeze-ci-image-build-line-61)">Additional environment variables  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1532.8" textLength="1464" clip-path="url(#breeze-ci-image-build-line-62)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1532.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-62)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="1557.2" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-63)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1557.2" textLength="622.2" clip-path="url(#breeze-ci-image-build-line-63)"> Preparing cache and push (for maintainers and CI) </text><text class="breeze-ci-image-build-r5" x="646.6" y="1557.2" textLength="793" clip-path="url(#breeze-ci-image-build-line-63)"> [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1581.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-64)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1581.6" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-64)">--builder             </text><text class="breeze-ci-image-build-r1" x="317.2" y="1581.6" textLength="768.6" clip-path="url(#breeze-ci-image-build-line-64)">Buildx&# [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1606" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-65)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1606" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-65)">--platform            </text><text class="breeze-ci-image-build-r1" x="317.2" y="1606" textLength="341.6" clip-path="url(#breeze-ci-image-build-line-65)">Platform for&# [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1630.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-66)">│</text><text class="breeze-ci-image-build-r7" x="317.2" y="1630.4" textLength="292.8" clip-path="url(#breeze-ci-image-build-line-66)">linux/amd64,linux/arm64)</text><text class="breeze-ci-image-build-r5" x="1451.8" y="1630.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-66)">│</text><text class="breeze-ci-image-build-r1" x="1464" y="1630.4" tex [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1654.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-67)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1654.8" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-67)">--push                </text><text class="breeze-ci-image-build-r1" x="317.2" y="1654.8" textLength="353.8" clip-path="url(#breeze-ci-image-build-lin [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1679.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-68)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1679.2" textLength="268.4" clip-path="url(#breeze-ci-image-build-line-68)">--prepare-buildx-cache</text><text class="breeze-ci-image-build-r1" x="317.2" y="1679.2" textLength="1122.4" clip-path="url(#breeze-ci-image-build-line-68)">Prepares build cache (this is done as  [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1703.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-69)">│</text><text class="breeze-ci-image-build-r1" x="317.2" y="1703.6" textLength="1122.4" clip-path="url(#breeze-ci-image-build-line-69)">image).                                     [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1728" textLength="1464" clip-path="url(#breeze-ci-image-build-line-70)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1728" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-70)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="1752.4" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-71)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1752.4" textLength="280.6" clip-path="url(#breeze-ci-image-build-line-71)"> GitHub authentication </text><text class="breeze-ci-image-build-r5" x="305" y="1752.4" textLength="1134.6" clip-path="url(#breeze-ci-image-build-line-71)">───────────────────────────────────────────────────────── [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1776.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-72)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1776.8" textLength="231.8" clip-path="url(#breeze-ci-image-build-line-72)">--github-repository</text><text class="breeze-ci-image-build-r6" x="280.6" y="1776.8" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-72)">-g</text><text class="breeze-ci-image-build-r1" x="329.4" y="1776.8" textLeng [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1801.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-73)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1801.2" textLength="231.8" clip-path="url(#breeze-ci-image-build-line-73)">--github-token     </text><text class="breeze-ci-image-build-r1" x="329.4" y="1801.2" textLength="512.4" clip-path="url(#breeze-ci-image-build-line-73)">The token used to authenticate& [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1825.6" textLength="1464" clip-path="url(#breeze-ci-image-build-line-74)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1825.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-74)"> +</text><text class="breeze-ci-image-build-r5" x="0" y="1850" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-75)">╭─</text><text class="breeze-ci-image-build-r5" x="24.4" y="1850" textLength="195.2" clip-path="url(#breeze-ci-image-build-line-75)"> Common options </text><text class="breeze-ci-image-build-r5" x="219.6" y="1850" textLength="1220" clip-path="url(#breeze-ci-image-build-line-75)">────────────────────────────────────────────────────────────────────── [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1874.4" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-76)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1874.4" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-76)">--answer </text><text class="breeze-ci-image-build-r6" x="158.6" y="1874.4" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-76)">-a</text><text class="breeze-ci-image-build-r1" x="207.4" y="1874.4" textLength="3 [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1898.8" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-77)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1898.8" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-77)">--dry-run</text><text class="breeze-ci-image-build-r6" x="158.6" y="1898.8" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-77)">-D</text><text class="breeze-ci-image-build-r1" x="207.4" y="1898.8" textLength="719.8" [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1923.2" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-78)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1923.2" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-78)">--verbose</text><text class="breeze-ci-image-build-r6" x="158.6" y="1923.2" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-78)">-v</text><text class="breeze-ci-image-build-r1" x="207.4" y="1923.2" textLength="585.6" [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1947.6" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-79)">│</text><text class="breeze-ci-image-build-r4" x="24.4" y="1947.6" textLength="109.8" clip-path="url(#breeze-ci-image-build-line-79)">--help   </text><text class="breeze-ci-image-build-r6" x="158.6" y="1947.6" textLength="24.4" clip-path="url(#breeze-ci-image-build-line-79)">-h</text><text class="breeze-ci-image-build-r1" x="207.4" y="1947.6" tex [...] +</text><text class="breeze-ci-image-build-r5" x="0" y="1972" textLength="1464" clip-path="url(#breeze-ci-image-build-line-80)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-build-r1" x="1464" y="1972" textLength="12.2" clip-path="url(#breeze-ci-image-build-line-80)"> </text> </g> </g> diff --git a/dev/breeze/doc/images/output_ci-image_build.txt b/dev/breeze/doc/images/output_ci-image_build.txt index a1bafba4172..f0c4db66c4a 100644 --- a/dev/breeze/doc/images/output_ci-image_build.txt +++ b/dev/breeze/doc/images/output_ci-image_build.txt @@ -1 +1 @@ -52f338c973e63396bbffd450b229d6c9 +12742b1b784b6e3a33582d4fe5f90d1f diff --git a/dev/breeze/doc/images/output_ci-image_load.svg b/dev/breeze/doc/images/output_ci-image_load.svg index d6c510fd5e2..5972fb23a4d 100644 --- a/dev/breeze/doc/images/output_ci-image_load.svg +++ b/dev/breeze/doc/images/output_ci-image_load.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 1482 806.4" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 1482 830.8" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -43,7 +43,7 @@ <defs> <clipPath id="breeze-ci-image-load-clip-terminal"> - <rect x="0" y="0" width="1463.0" height="755.4" /> + <rect x="0" y="0" width="1463.0" height="779.8" /> </clipPath> <clipPath id="breeze-ci-image-load-line-0"> <rect x="0" y="1.5" width="1464" height="24.65"/> @@ -135,9 +135,12 @@ <clipPath id="breeze-ci-image-load-line-29"> <rect x="0" y="709.1" width="1464" height="24.65"/> </clipPath> +<clipPath id="breeze-ci-image-load-line-30"> + <rect x="0" y="733.5" width="1464" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="804.4" rx="8"/><text class="breeze-ci-image-load-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: ci-image load</text> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="828.8" rx="8"/><text class="breeze-ci-image-load-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command: ci-image load</text> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> @@ -172,12 +175,13 @@ </text><text class="breeze-ci-image-load-r5" x="0" y="556.8" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-22)">│</text><text class="breeze-ci-image-load-r1" x="414.8" y="556.8" textLength="256.2" clip-path="url(#breeze-ci-image-load-line-22)">from the latest job. </text><text class="breeze-ci-image-load-r7" x="671" y="556.8" textLength="73.2" clip-path="url(#breeze-ci-image-load-line-22)">(TEXT)</text><text class="breeze-ci-image-load-r5" x="1451.8" y=" [...] </text><text class="breeze-ci-image-load-r5" x="0" y="581.2" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-23)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="581.2" textLength="317.2" clip-path="url(#breeze-ci-image-load-line-23)">--from-run                </text><text class="breeze-ci-image-load-r1" x="414.8" y="581.2" textLength="793" clip-path="url(#breeze-ci-image-load-line-23)"> [...] </text><text class="breeze-ci-image-load-r5" x="0" y="605.6" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-24)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="605.6" textLength="317.2" clip-path="url(#breeze-ci-image-load-line-24)">--skip-image-file-deletion</text><text class="breeze-ci-image-load-r1" x="414.8" y="605.6" textLength="414.8" clip-path="url(#breeze-ci-image-load-line-24)">Skip image deletion after loading.</text><text class="bree [...] -</text><text class="breeze-ci-image-load-r5" x="0" y="630" textLength="1464" clip-path="url(#breeze-ci-image-load-line-25)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-load-r1" x="1464" y="630" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-25)"> -</text><text class="breeze-ci-image-load-r5" x="0" y="654.4" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-26)">╭─</text><text class="breeze-ci-image-load-r5" x="24.4" y="654.4" textLength="195.2" clip-path="url(#breeze-ci-image-load-line-26)"> Common options </text><text class="breeze-ci-image-load-r5" x="219.6" y="654.4" textLength="1220" clip-path="url(#breeze-ci-image-load-line-26)">───────────────────────────────────────────────────────────────────────── [...] -</text><text class="breeze-ci-image-load-r5" x="0" y="678.8" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-27)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="678.8" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-27)">--dry-run</text><text class="breeze-ci-image-load-r6" x="158.6" y="678.8" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-27)">-D</text><text class="breeze-ci-image-load-r1" x="207.4" y="678.8" textLength="719.8" clip-path= [...] -</text><text class="breeze-ci-image-load-r5" x="0" y="703.2" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-28)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="703.2" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-28)">--verbose</text><text class="breeze-ci-image-load-r6" x="158.6" y="703.2" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-28)">-v</text><text class="breeze-ci-image-load-r1" x="207.4" y="703.2" textLength="585.6" clip-path= [...] -</text><text class="breeze-ci-image-load-r5" x="0" y="727.6" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-29)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="727.6" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-29)">--help   </text><text class="breeze-ci-image-load-r6" x="158.6" y="727.6" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-29)">-h</text><text class="breeze-ci-image-load-r1" x="207.4" y="727.6" textLength="32 [...] -</text><text class="breeze-ci-image-load-r5" x="0" y="752" textLength="1464" clip-path="url(#breeze-ci-image-load-line-30)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-load-r1" x="1464" y="752" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-30)"> +</text><text class="breeze-ci-image-load-r5" x="0" y="630" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-25)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="630" textLength="317.2" clip-path="url(#breeze-ci-image-load-line-25)">--tag-as                  </text><text class="breeze-ci-image-load-r1" x="414.8" y="630" textLength="671" clip-path="url(#breeze-ci-image-load-line-2 [...] +</text><text class="breeze-ci-image-load-r5" x="0" y="654.4" textLength="1464" clip-path="url(#breeze-ci-image-load-line-26)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-load-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-26)"> +</text><text class="breeze-ci-image-load-r5" x="0" y="678.8" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-27)">╭─</text><text class="breeze-ci-image-load-r5" x="24.4" y="678.8" textLength="195.2" clip-path="url(#breeze-ci-image-load-line-27)"> Common options </text><text class="breeze-ci-image-load-r5" x="219.6" y="678.8" textLength="1220" clip-path="url(#breeze-ci-image-load-line-27)">───────────────────────────────────────────────────────────────────────── [...] +</text><text class="breeze-ci-image-load-r5" x="0" y="703.2" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-28)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="703.2" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-28)">--dry-run</text><text class="breeze-ci-image-load-r6" x="158.6" y="703.2" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-28)">-D</text><text class="breeze-ci-image-load-r1" x="207.4" y="703.2" textLength="719.8" clip-path= [...] +</text><text class="breeze-ci-image-load-r5" x="0" y="727.6" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-29)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="727.6" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-29)">--verbose</text><text class="breeze-ci-image-load-r6" x="158.6" y="727.6" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-29)">-v</text><text class="breeze-ci-image-load-r1" x="207.4" y="727.6" textLength="585.6" clip-path= [...] +</text><text class="breeze-ci-image-load-r5" x="0" y="752" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-30)">│</text><text class="breeze-ci-image-load-r4" x="24.4" y="752" textLength="109.8" clip-path="url(#breeze-ci-image-load-line-30)">--help   </text><text class="breeze-ci-image-load-r6" x="158.6" y="752" textLength="24.4" clip-path="url(#breeze-ci-image-load-line-30)">-h</text><text class="breeze-ci-image-load-r1" x="207.4" y="752" textLength="329.4" cli [...] +</text><text class="breeze-ci-image-load-r5" x="0" y="776.4" textLength="1464" clip-path="url(#breeze-ci-image-load-line-31)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-ci-image-load-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#breeze-ci-image-load-line-31)"> </text> </g> </g> diff --git a/dev/breeze/doc/images/output_ci-image_load.txt b/dev/breeze/doc/images/output_ci-image_load.txt index 2f2f4d04205..8a10a0f9d11 100644 --- a/dev/breeze/doc/images/output_ci-image_load.txt +++ b/dev/breeze/doc/images/output_ci-image_load.txt @@ -1 +1 @@ -baf0bfe0e270b6982bbfbfd97707a1ba +9585cbea5c5cf55ef6b49273f4523fed diff --git a/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py index e9b34bfd164..ad1c38e30c4 100644 --- a/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py +++ b/dev/breeze/src/airflow_breeze/commands/ci_image_commands.py @@ -36,6 +36,7 @@ from airflow_breeze.commands.common_image_options import ( option_additional_python_deps, option_airflow_constraints_reference_build, option_build_progress, + option_cache_from_image, option_debian_version, option_dev_apt_command, option_dev_apt_deps, @@ -52,6 +53,7 @@ from airflow_breeze.commands.common_image_options import ( option_push, option_python_image, option_skip_image_file_deletion, + option_tag_as, option_verify, option_wait_for_image, ) @@ -247,6 +249,7 @@ option_ci_image_file_to_load = click.option( @option_answer @option_build_progress @option_builder +@option_cache_from_image @option_commit_sha @option_debian_version @option_debug_resources @@ -286,6 +289,7 @@ def build( airflow_constraints_reference: str, build_progress: str, builder: str, + cache_from_image: str | None, commit_sha: str | None, debian_version: str, debug_resources: bool, @@ -338,6 +342,7 @@ def build( airflow_constraints_reference=airflow_constraints_reference, build_progress=build_progress, builder=builder, + cache_from_image=cache_from_image, commit_sha=commit_sha, debian_version=debian_version, dev_apt_command=dev_apt_command, @@ -558,6 +563,7 @@ def save( @option_platform_single @option_python @option_skip_image_file_deletion +@option_tag_as @option_verbose def load( from_run: str | None, @@ -569,6 +575,7 @@ def load( platform: str, python: str, skip_image_file_deletion: bool, + tag_as: str | None, ): """Load CI image from a file.""" perform_environment_checks() @@ -618,6 +625,15 @@ def load( if result.returncode != 0: console_print(f"[error]Error when loading image: {result.stdout}[/]") sys.exit(result.returncode) + if tag_as: + console_print(f"[info]Tagging {build_ci_params.airflow_image_name} as {tag_as}[/]") + result = run_command( + ["docker", "tag", build_ci_params.airflow_image_name, tag_as], + check=False, + ) + if result.returncode != 0: + console_print(f"[error]Error when tagging image: {result.stdout}[/]") + sys.exit(result.returncode) if not skip_image_file_deletion: console_print(f"[info]Deleting image file {image_file_to_load}[/]") image_file_to_load.unlink() diff --git a/dev/breeze/src/airflow_breeze/commands/ci_image_commands_config.py b/dev/breeze/src/airflow_breeze/commands/ci_image_commands_config.py index b9e0cf161a9..dd60ef4f2bf 100644 --- a/dev/breeze/src/airflow_breeze/commands/ci_image_commands_config.py +++ b/dev/breeze/src/airflow_breeze/commands/ci_image_commands_config.py @@ -57,6 +57,7 @@ CI_IMAGE_TOOLS_PARAMETERS: dict[str, list[dict[str, str | list[str]]]] = { "name": "Advanced build options (for power users)", "options": [ "--additional-pip-install-flags", + "--cache-from-image", "--commit-sha", "--debian-version", "--disable-airflow-repo-cache", @@ -183,6 +184,7 @@ CI_IMAGE_TOOLS_PARAMETERS: dict[str, list[dict[str, str | list[str]]]] = { "--from-pr", "--from-run", "--skip-image-file-deletion", + "--tag-as", ], }, ], diff --git a/dev/breeze/src/airflow_breeze/commands/common_image_options.py b/dev/breeze/src/airflow_breeze/commands/common_image_options.py index 5c545fbfbeb..696b1d93146 100644 --- a/dev/breeze/src/airflow_breeze/commands/common_image_options.py +++ b/dev/breeze/src/airflow_breeze/commands/common_image_options.py @@ -101,6 +101,12 @@ option_build_progress = click.option( show_default=True, default=ALLOWED_BUILD_PROGRESS[0], ) +option_cache_from_image = click.option( + "--cache-from-image", + help="Additional image to read the build cache from - it has to be pullable from a registry, " + "an image only present in the local docker engine contributes no cache.", + envvar="CACHE_FROM_IMAGE", +) option_debian_version = click.option( "--debian-version", type=BetterChoice(ALLOWED_DEBIAN_VERSIONS), @@ -200,6 +206,11 @@ option_skip_image_file_deletion = click.option( is_flag=True, envvar="SKIP_IMAGE_FILE_DELETION", ) +option_tag_as = click.option( + "--tag-as", + help="Additionally tag the loaded image with this reference.", + envvar="TAG_AS", +) option_from_run = click.option( "--from-run", required=False, diff --git a/dev/breeze/src/airflow_breeze/params/common_build_params.py b/dev/breeze/src/airflow_breeze/params/common_build_params.py index 60f6034f925..01fc0d54061 100644 --- a/dev/breeze/src/airflow_breeze/params/common_build_params.py +++ b/dev/breeze/src/airflow_breeze/params/common_build_params.py @@ -51,6 +51,7 @@ class CommonBuildParams: airflow_constraints_location: str | None = None builder: str = "autodetect" build_progress: str = ALLOWED_BUILD_PROGRESS[0] + cache_from_image: str | None = None constraints_github_repository: str = APACHE_AIRFLOW_GITHUB_REPOSITORY commit_sha: str | None = None dev_apt_command: str | None = None @@ -107,6 +108,15 @@ class CommonBuildParams: extra_flags.append(f"--cache-from={self.get_cache(self.platform)}") elif self.docker_cache == "disabled": extra_flags.append("--no-cache") + if self.cache_from_image: + extra_flags.append(f"--cache-from={self.cache_from_image}") + if self.docker_cache != "disabled": + # Records the layer cache keys in the image config, which is what lets an image + # built by an earlier run serve as `--cache-from` for a later one. BuildKit only + # honours those records when it can pull the layers from a registry, so an image + # that travelled as a saved tarball has to be pushed to one (even a throw-away + # local registry) before it can be passed as `--cache-from-image`. + extra_flags.append("--build-arg=BUILDKIT_INLINE_CACHE=1") return extra_flags @property diff --git a/dev/breeze/tests/test_build_ci_params.py b/dev/breeze/tests/test_build_ci_params.py new file mode 100644 index 00000000000..f652ca2f613 --- /dev/null +++ b/dev/breeze/tests/test_build_ci_params.py @@ -0,0 +1,57 @@ +# 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 pytest + +from airflow_breeze.params.build_ci_params import BuildCiParams + +INLINE_CACHE_FLAG = "--build-arg=BUILDKIT_INLINE_CACHE=1" + + [email protected]( + ("docker_cache", "inline_cache_expected"), + [ + pytest.param("registry", True, id="registry"), + pytest.param("local", True, id="local"), + pytest.param("disabled", False, id="disabled"), + ], +) +def test_inline_cache_recorded_unless_cache_disabled(docker_cache: str, inline_cache_expected: bool): + flags = BuildCiParams(docker_cache=docker_cache).common_docker_build_flags + assert (INLINE_CACHE_FLAG in flags) is inline_cache_expected + + +def test_cache_from_image_added_next_to_registry_cache(): + params = BuildCiParams(docker_cache="registry", cache_from_image="localhost:5000/ci-image-cache:3.12") + flags = params.common_docker_build_flags + assert f"--cache-from={params.get_cache(params.platform)}" in flags + assert "--cache-from=localhost:5000/ci-image-cache:3.12" in flags + + +def test_cache_from_image_added_when_registry_cache_is_not_used(): + flags = BuildCiParams( + docker_cache="disabled", cache_from_image="localhost:5000/ci-image-cache:3.12" + ).common_docker_build_flags + assert "--cache-from=localhost:5000/ci-image-cache:3.12" in flags + assert "--no-cache" in flags + + +def test_no_cache_from_image_flag_when_unset(): + flags = BuildCiParams(docker_cache="registry").common_docker_build_flags + assert not any(flag.startswith("--cache-from=localhost") for flag in flags)
