Skip to content

Build / Cache base image #721

Build / Cache base image

Build / Cache base image #721

#
# 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.
#
name: Build / Cache base image
on:
# Rebuild cache on a schedule to keep it warm despite base image drift.
# The `ubuntu:noble` tag is re-pointed to a new image roughly monthly,
# which invalidates all cached layers built on top of it. Without a
# scheduled rebuild, the cache goes stale between Dockerfile changes
# and every commit on every PR spends ~50 minutes building base images!
schedule:
- cron: '0 4 * * *'
# Run jobs when a commit is merged
push:
branches:
- 'master'
- 'branch-*'
paths:
- 'dev/spark-test-image/docs/Dockerfile'
- 'dev/spark-test-image/lint/Dockerfile'
- 'dev/spark-test-image/sparkr/Dockerfile'
- 'dev/spark-test-image/python-minimum/Dockerfile'
- 'dev/spark-test-image/python-311/Dockerfile'
- 'dev/spark-test-image/python-312/Dockerfile'
- 'dev/spark-test-image/python-312-classic-only/Dockerfile'
- 'dev/spark-test-image/python-312-pandas-3/Dockerfile'
- 'dev/spark-test-image/python-313/Dockerfile'
- 'dev/spark-test-image/python-314/Dockerfile'
- 'dev/spark-test-image/python-314-nogil/Dockerfile'
- '.github/workflows/build_infra_images_cache.yml'
- 'pyproject.toml'
# Create infra image when cutting down branches/tags
create:
workflow_dispatch:
jobs:
main:
if: github.repository == 'apache/spark'
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Checkout Spark repository
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c
- name: Login to DockerHub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push (Documentation)
if: hashFiles('dev/spark-test-image/docs/Dockerfile') != ''
id: docker_build_docs
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/docs/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-docs-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-docs-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-docs-cache:${{ github.ref_name }},mode=max
- name: Image digest (Documentation)
if: hashFiles('dev/spark-test-image/docs/Dockerfile') != ''
run: echo ${{ steps.docker_build_docs.outputs.digest }}
- name: Build and push (Linter)
if: hashFiles('dev/spark-test-image/lint/Dockerfile') != ''
id: docker_build_lint
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/lint/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-lint-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-lint-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-lint-cache:${{ github.ref_name }},mode=max
- name: Image digest (Linter)
if: hashFiles('dev/spark-test-image/lint/Dockerfile') != ''
run: echo ${{ steps.docker_build_lint.outputs.digest }}
- name: Build and push (SparkR)
if: hashFiles('dev/spark-test-image/sparkr/Dockerfile') != ''
id: docker_build_sparkr
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/sparkr/
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-sparkr-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-sparkr-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-sparkr-cache:${{ github.ref_name }},mode=max
- name: Image digest (SparkR)
if: hashFiles('dev/spark-test-image/sparkr/Dockerfile') != ''
run: echo ${{ steps.docker_build_sparkr.outputs.digest }}
- name: Build and push (PySpark with old dependencies)
if: hashFiles('dev/spark-test-image/python-minimum/Dockerfile') != ''
id: docker_build_pyspark_python_minimum
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-minimum/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-minimum-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-minimum-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-minimum-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with old dependencies)
if: hashFiles('dev/spark-test-image/python-minimum/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_minimum.outputs.digest }}
- name: Build and push (PySpark with Python 3.11)
if: hashFiles('dev/spark-test-image/python-311/Dockerfile') != ''
id: docker_build_pyspark_python_311
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-311/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-311-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-311-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-311-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.11)
if: hashFiles('dev/spark-test-image/python-311/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_311.outputs.digest }}
- name: Build and push (PySpark Classic Only with Python 3.12)
if: hashFiles('dev/spark-test-image/python-312-classic-only/Dockerfile') != ''
id: docker_build_pyspark_python_312_classic_only
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-312-classic-only/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-classic-only-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-classic-only-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-classic-only-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark Classic Only with Python 3.12)
if: hashFiles('dev/spark-test-image/python-312-classic-only/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_312_classic_only.outputs.digest }}
- name: Build and push (PySpark with Python 3.12)
if: hashFiles('dev/spark-test-image/python-312/Dockerfile') != ''
id: docker_build_pyspark_python_312
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-312/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.12)
if: hashFiles('dev/spark-test-image/python-312/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_312.outputs.digest }}
- name: Build and push (PySpark with Python 3.12 Pandas 3)
if: hashFiles('dev/spark-test-image/python-312-pandas-3/Dockerfile') != ''
id: docker_build_pyspark_python_312_pandas_3
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-312-pandas-3/
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-pandas-3-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-pandas-3-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-312-pandas-3-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.12 Pandas 3)
if: hashFiles('dev/spark-test-image/python-312-pandas-3/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_312_pandas_3.outputs.digest }}
- name: Build and push (PySpark with Python 3.13)
if: hashFiles('dev/spark-test-image/python-313/Dockerfile') != ''
id: docker_build_pyspark_python_313
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-313/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-313-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-313-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-313-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.13)
if: hashFiles('dev/spark-test-image/python-313/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_313.outputs.digest }}
- name: Build and push (PySpark with Python 3.14)
if: hashFiles('dev/spark-test-image/python-314/Dockerfile') != ''
id: docker_build_pyspark_python_314
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-314/
build-contexts: |
root=./
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.14)
if: hashFiles('dev/spark-test-image/python-314/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_314.outputs.digest }}
- name: Build and push (PySpark with Python 3.14 no GIL)
if: hashFiles('dev/spark-test-image/python-314-nogil/Dockerfile') != ''
id: docker_build_pyspark_python_314_nogil
continue-on-error: true
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./dev/spark-test-image/python-314-nogil/
push: true
tags: ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-nogil-cache:${{ github.ref_name }}-static
cache-from: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-nogil-cache:${{ github.ref_name }}
cache-to: type=registry,ref=ghcr.io/apache/spark/apache-spark-github-action-image-pyspark-python-314-nogil-cache:${{ github.ref_name }},mode=max
- name: Image digest (PySpark with Python 3.14 no GIL)
if: hashFiles('dev/spark-test-image/python-314-nogil/Dockerfile') != ''
run: echo ${{ steps.docker_build_pyspark_python_314_nogil.outputs.digest }}
- name: Fail if any image build failed
if: always()
run: |
status=0
check() {
local name="$1"
local outcome="$2"
echo "$name: $outcome"
if [ "$outcome" = "failure" ]; then
status=1
fi
}
check "docs" "${{ steps.docker_build_docs.outcome }}"
check "lint" "${{ steps.docker_build_lint.outcome }}"
check "sparkr" "${{ steps.docker_build_sparkr.outcome }}"
check "pyspark-python-minimum" "${{ steps.docker_build_pyspark_python_minimum.outcome }}"
check "pyspark-python-311" "${{ steps.docker_build_pyspark_python_311.outcome }}"
check "pyspark-python-312-classic-only" "${{ steps.docker_build_pyspark_python_312_classic_only.outcome }}"
check "pyspark-python-312" "${{ steps.docker_build_pyspark_python_312.outcome }}"
check "pyspark-python-312-pandas-3" "${{ steps.docker_build_pyspark_python_312_pandas_3.outcome }}"
check "pyspark-python-313" "${{ steps.docker_build_pyspark_python_313.outcome }}"
check "pyspark-python-314" "${{ steps.docker_build_pyspark_python_314.outcome }}"
check "pyspark-python-314-nogil" "${{ steps.docker_build_pyspark_python_314_nogil.outcome }}"
if [ "$status" -ne 0 ]; then
echo "::error::One or more image builds failed; see entries above marked 'failure'."
fi
exit "$status"