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

potiuk pushed a commit to branch fix-docs-build-separate-images
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/fix-docs-build-separate-images 
by this push:
     new 080eb280964 Build each CI image in the job that uses it instead of a 
shared job
080eb280964 is described below

commit 080eb2809640e8a6782382dd03958db14675be67
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon Aug 3 04:25:03 2026 +0200

    Build each CI image in the job that uses it instead of a shared job
    
    Docs and the registry now want different Pythons, and a job that builds an 
image
    for another job has to hand it over as a multi-gigabyte tarball - exported,
    stashed, restored and imported - for an image whose only consumer is a 
single
    downstream job. Building in place skips that round trip entirely, and the 
two
    builds still run in parallel because both jobs now wait only on build-info.
    
    What is worth keeping between runs is the BuildKit mount cache rather than 
the
    image, so the docs job restores and stashes it under this ref and its own
    Python. The registry side already knew how to build its own image when the
    caller had not stashed one; it just could not be told which ref to build, 
so it
    would have documented main rather than the tag being published.
---
 .github/workflows/publish-docs-to-s3.yml | 126 ++++++++++++++++---------------
 .github/workflows/registry-build.yml     |  17 +++++
 2 files changed, 82 insertions(+), 61 deletions(-)

diff --git a/.github/workflows/publish-docs-to-s3.yml 
b/.github/workflows/publish-docs-to-s3.yml
index eab0ba59e98..6e2c1f1044f 100644
--- a/.github/workflows/publish-docs-to-s3.yml
+++ b/.github/workflows/publish-docs-to-s3.yml
@@ -237,49 +237,8 @@ jobs:
           echo "publish-execution-api-schema=${PUBLISH_EXEC}" >> 
${GITHUB_OUTPUT}
           echo "publish-supervisor-schema=${PUBLISH_SUP}" >> ${GITHUB_OUTPUT}
 
-  build-ci-image:
-    name: "Build CI image"
-    needs: [build-info]
-    uses: ./.github/workflows/ci-image-build.yml
-    permissions:
-      contents: read
-      packages: write
-    with:
-      runners: '["ubuntu-22.04"]'
-      platform: "linux/amd64"
-      # Built from the docs ref so the image matches the sources being 
documented, and
-      # cached against main's registry cache, which the regular Test workflow 
keeps warm.
-      checkout-ref: ${{ inputs.ref }}
-      push-image: "false"
-      upload-image-artifact: "true"
-      # Leaves the BuildKit mount cache behind for the next publish of this 
same ref;
-      # 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. 
Publishing the same ref
-      # twice - an RC and then the final docs - skips the build outright.
-      seed-cache-from-stashed-image: "true"
-      image-stash-ref: ${{ needs.build-info.outputs.image-stash-ref }}
-      # One matrix job, two images built in parallel - docs and registry each 
get their own
-      # Python, and ci-image-build.yml keys every cache and stash it writes by 
Python version.
-      python-versions: ${{ format('["{0}", "{1}"]', 
needs.build-info.outputs.docs-python-version,
-        needs.build-info.outputs.registry-python-version) }}
-      branch: "main"
-      constraints-branch: "constraints-main"
-      use-uv: "true"
-      upgrade-to-newer-dependencies: "false"
-      docker-cache: "registry"
-      disable-airflow-repo-cache: "false"
-      # Building against main's cache can fail outright when the ref's 
pyproject.toml has
-      # diverged from main — the case the docs job's inline `breeze ci-image 
build || docker
-      # buildx build` fallback used to cover.
-      retry-without-cache: "true"
-
   build-docs:
-    needs: [build-info, build-ci-image]
+    needs: [build-info]
     timeout-minutes: 150
     name: "Build documentation"
     runs-on: ubuntu-latest
@@ -345,27 +304,69 @@ jobs:
         uses: ./.github/actions/breeze
         with:
           python-version: "${{ needs.build-info.outputs.docs-python-version }}"
-      # The image comes from the `build-ci-image` matrix, which builds it from 
this same ref and
-      # stashes it under the ref's own key - the shared per-branch stash holds 
main's image, not
-      # this ref's. The stash key carries the Python version, so this restores 
the docs image and
-      # the registry restores its own; a run that had nothing to build 
restores what the previous
-      # publish left behind.
-      - name: >
-          Restore CI docker image built for ref ${{ inputs.ref }}
-          linux/amd64:${{ needs.build-info.outputs.docs-python-version }}
+      # The image is built here rather than in a job of its own. Docs are the 
only consumer of
+      # this Python's image, so a separate job would only export a 
multi-gigabyte tarball and
+      # import it straight back. What is worth carrying between runs is the 
BuildKit mount cache,
+      # which is restored and saved below under this ref's own key.
+      - name: "Restore mount cache linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
         uses: 
apache/infrastructure-actions/stash/restore@0ff9972b5872e19c9f4555c9159c2fea4f794355
         with:
-          key: "ci-image-save-v3-linux/amd64-\
+          key: "ci-cache-mount-save-v3-linux/amd64-\
             ${{ needs.build-info.outputs.docs-python-version }}\
             -${{ needs.build-info.outputs.image-stash-ref }}"
-          path: "/mnt/"
-          only-current-branch: 'true'
-          fail-on-download: 'true'
-      - name: "Load CI image linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
+          path: "/tmp/"
+        id: restore-mount-cache
+      - name: "Import mount cache linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
         env:
-          PYTHON: ${{ needs.build-info.outputs.docs-python-version }}
+          PYTHON_MAJOR_MINOR_VERSION: ${{ 
needs.build-info.outputs.docs-python-version }}
         run: >
-          breeze ci-image load --platform "linux/amd64" --python "${PYTHON}" 
--image-file-dir "/mnt"
+          breeze ci-image import-mount-cache
+          --cache-file 
/tmp/ci-cache-mount-save-v3-${PYTHON_MAJOR_MINOR_VERSION}.tar.gz
+        if: steps.restore-mount-cache.outputs.stash-hit == 'true'
+      - name: "Login to ghcr.io"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          ACTOR: ${{ github.actor }}
+        run: echo "${GITHUB_TOKEN}" | docker login ghcr.io -u ${ACTOR} 
--password-stdin
+      - name: "Build CI image linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
+        env:
+          DOCKER_CACHE: "registry"
+          DISABLE_AIRFLOW_REPO_CACHE: "false"
+          UPGRADE_TO_NEWER_DEPENDENCIES: "false"
+          CONSTRAINTS_GITHUB_REPOSITORY: >-
+            ${{ secrets.CONSTRAINTS_GITHUB_REPOSITORY != '' &&
+            secrets.CONSTRAINTS_GITHUB_REPOSITORY || 'apache/airflow' }}
+          GITHUB_REPOSITORY: ${{ github.repository }}
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_USERNAME: ${{ github.actor }}
+          PUSH: "false"
+        run: |
+          if breeze ci-image build --platform "linux/amd64"; then
+            exit 0
+          fi
+          # Reached when this ref's pyproject.toml has drifted far enough from 
the branch the
+          # registry cache was built for that the cached layers cannot satisfy 
it. Slow, but
+          # correct - and the case that matters here, since docs are published 
from refs cut
+          # days or weeks before main.
+          echo "Build with registry cache failed - retrying with the cache 
disabled."
+          DOCKER_CACHE="disabled" breeze ci-image build --platform 
"linux/amd64"
+      - name: "Export mount cache linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
+        env:
+          PYTHON_MAJOR_MINOR_VERSION: ${{ 
needs.build-info.outputs.docs-python-version }}
+        run: >
+          breeze ci-image export-mount-cache
+          --cache-file 
/tmp/ci-cache-mount-save-v3-${PYTHON_MAJOR_MINOR_VERSION}.tar.gz
+      - name: "Stash mount cache linux/amd64:${{ 
needs.build-info.outputs.docs-python-version }}"
+        uses: 
apache/infrastructure-actions/stash/save@0ff9972b5872e19c9f4555c9159c2fea4f794355
+        with:
+          key: "ci-cache-mount-save-v3-linux/amd64-\
+            ${{ needs.build-info.outputs.docs-python-version }}\
+            -${{ needs.build-info.outputs.image-stash-ref }}"
+          path: "/tmp/ci-cache-mount-save-v3-\
+            ${{ needs.build-info.outputs.docs-python-version }}.tar.gz"
+          if-no-files-found: 'error'
+          # Read by the next publish of this same ref - an RC and then the 
final docs, days apart.
+          retention-days: '6'
       - name: "Restore docs inventory cache"
         uses: 
apache/infrastructure-actions/stash/restore@0ff9972b5872e19c9f4555c9159c2fea4f794355
         with:
@@ -658,7 +659,7 @@ jobs:
   update-registry:
     # Runs alongside the docs build rather than after it: the registry reads 
nothing the docs
     # publish produces, so waiting only added its whole duration to the 
release manager's wait.
-    needs: [build-info, build-ci-image]
+    needs: [build-info]
     if: needs.build-info.outputs.registry-providers != '' || 
needs.build-info.outputs.registry-full-build == 'true'
     name: "Update Provider Registry"
     permissions:
@@ -668,10 +669,13 @@ jobs:
     with:
       destination: ${{ needs.build-info.outputs.destination }}
       provider: ${{ needs.build-info.outputs.registry-providers }}
+      # The registry extracts on 3.12 and the docs build on the default 
Python, so each builds
+      # the image it needs instead of sharing one. registry-build.yml builds 
its own whenever the
+      # caller has not stashed one, keyed by this ref so consecutive publishes 
reuse it.
       python-version: ${{ needs.build-info.outputs.registry-python-version }}
-      # `build-ci-image` already built and stashed both images this run - 
under the ref's key, one
-      # per Python. This picks the 3.12 one, which is what the registry has 
always extracted on.
-      ci-image-already-built: true
+      ci-image-already-built: false
+      # Without this the registry would document main rather than the ref 
being published.
+      checkout-ref: ${{ inputs.ref }}
       image-stash-ref: ${{ needs.build-info.outputs.image-stash-ref }}
     secrets:
       DOCS_AWS_ACCESS_KEY_ID: ${{ secrets.DOCS_AWS_ACCESS_KEY_ID }}
diff --git a/.github/workflows/registry-build.yml 
b/.github/workflows/registry-build.yml
index c5bae276172..fa8866c22a4 100644
--- a/.github/workflows/registry-build.yml
+++ b/.github/workflows/registry-build.yml
@@ -60,6 +60,13 @@ on:  # yamllint disable-line rule:truthy
         required: false
         type: boolean
         default: false
+      checkout-ref:
+        description: >
+          Ref the CI image is built from. Defaults to the branch tip; a 
release publish passes the
+          tag it is publishing so the registry extracts from those sources 
rather than from main.
+        required: false
+        type: string
+        default: ""
       # `github.event_name` inside a called workflow reports the *caller's* 
event, never
       # `workflow_call`, so it cannot distinguish the two entry points. An 
input declared
       # only here can: it defaults to true when called and is undefined 
(falsy) on a
@@ -117,10 +124,17 @@ jobs:
     with:
       runners: '["ubuntu-22.04"]'
       platform: "linux/amd64"
+      # Empty means the branch tip; a release publish passes the tag it is 
publishing.
+      checkout-ref: ${{ inputs.checkout-ref }}
       push-image: "false"
       upload-image-artifact: "true"
       # Kept so a standalone registry dispatch reuses the mount cache its last 
run left behind.
       upload-mount-cache-artifact: "true"
+      # A release ref is cut days or weeks before main, so main's registry 
cache stops matching at
+      # the first Dockerfile.ci change since the cut. What the previous 
publish of this same ref
+      # stashed was built from these very sources, so it is the cache main's 
cannot be.
+      seed-cache-from-stashed-image: ${{ inputs.image-stash-ref != '' }}
+      image-stash-ref: ${{ inputs.image-stash-ref }}
       python-versions: ${{ format('["{0}"]', inputs.python-version) }}
       branch: "main"
       constraints-branch: "constraints-main"
@@ -128,6 +142,9 @@ jobs:
       upgrade-to-newer-dependencies: "false"
       docker-cache: "registry"
       disable-airflow-repo-cache: "false"
+      # Building a release ref against main's cache can fail outright once its 
pyproject.toml has
+      # diverged; falling back to an uncached build is slow but correct.
+      retry-without-cache: "true"
 
   build-and-publish-registry:
     timeout-minutes: 45

Reply via email to