name: Wheels on: schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) # │ │ │ │ │ - cron: "42 1 * * 0,3" push: paths: - ".ci/requirements-cibw.txt" - ".github/workflows/wheel*" - "pyproject.toml" - "setup.py" - "wheels/*" - "winbuild/build_prepare.py" - "winbuild/fribidi.cmake" tags: - "*" pull_request: paths: - ".ci/requirements-cibw.txt" - ".github/workflows/wheel*" - "pyproject.toml" - "setup.py" - "wheels/*" - "winbuild/build_prepare.py" - "winbuild/fribidi.cmake" workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: EXPECTED_DISTS: 91 FORCE_COLOR: 1 jobs: build-native-wheels: if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow' name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - name: "macOS 10.10 x86_64" platform: macos os: macos-15-intel cibw_arch: x86_64 build: "cp3{10,11}*" macosx_deployment_target: "10.10" - name: "macOS 10.13 x86_64" platform: macos os: macos-15-intel cibw_arch: x86_64 build: "cp3{12,13}*" macosx_deployment_target: "10.13" - name: "macOS 10.15 x86_64" platform: macos os: macos-15-intel cibw_arch: x86_64 build: "{cp314,pp3}*" macosx_deployment_target: "10.15" - name: "macOS arm64" platform: macos os: macos-latest cibw_arch: arm64 macosx_deployment_target: "11.0" - name: "manylinux2014 and musllinux x86_64" platform: linux os: ubuntu-latest cibw_arch: x86_64 manylinux: "manylinux2014" - name: "manylinux_2_28 x86_64" platform: linux os: ubuntu-latest cibw_arch: x86_64 build: "*manylinux*" - name: "manylinux2014 and musllinux aarch64" platform: linux os: ubuntu-24.04-arm cibw_arch: aarch64 manylinux: "manylinux2014" - name: "manylinux_2_28 aarch64" platform: linux os: ubuntu-24.04-arm cibw_arch: aarch64 build: "*manylinux*" - name: "iOS arm64 device" platform: ios os: macos-latest cibw_arch: arm64_iphoneos - name: "iOS arm64 simulator" platform: ios os: macos-latest cibw_arch: arm64_iphonesimulator - name: "iOS x86_64 simulator" platform: ios os: macos-15-intel cibw_arch: x86_64_iphonesimulator steps: - uses: actions/checkout@v6 with: persist-credentials: false submodules: true - uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install cibuildwheel run: | python3 -m pip install -r .ci/requirements-cibw.txt - name: Build wheels run: | python3 -m cibuildwheel --output-dir wheelhouse env: CIBW_PLATFORM: ${{ matrix.platform }} CIBW_ARCHS: ${{ matrix.cibw_arch }} CIBW_BUILD: ${{ matrix.build }} CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux }} CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.manylinux }} CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }} CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }} MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }} - uses: actions/upload-artifact@v5 with: name: dist-${{ matrix.name }} path: ./wheelhouse/*.whl windows: if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow' name: Windows ${{ matrix.cibw_arch }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - cibw_arch: x86 os: windows-latest - cibw_arch: AMD64 os: windows-latest - cibw_arch: ARM64 os: windows-11-arm steps: - uses: actions/checkout@v6 with: persist-credentials: false - name: Checkout extra test images uses: actions/checkout@v6 with: persist-credentials: false repository: python-pillow/test-images path: Tests\test-images - uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install cibuildwheel run: | python.exe -m pip install -r .ci/requirements-cibw.txt - name: Prepare for build run: | choco install nasm --no-progress echo "C:\Program Files\NASM" >> $env:GITHUB_PATH # Install extra test images xcopy /S /Y Tests\test-images\* Tests\images & python.exe winbuild\build_prepare.py -v --no-imagequant --architecture=${{ matrix.cibw_arch }} shell: pwsh - name: Build wheels run: | setlocal EnableDelayedExpansion for %%f in (winbuild\build\license\*) do ( set x=%%~nf rem Skip FriBiDi license, it is not included in the wheel. set fribidi=!x:~0,7! if NOT !fribidi!==fribidi ( rem Skip imagequant license, it is not included in the wheel. set libimagequant=!x:~0,13! if NOT !libimagequant!==libimagequant ( echo. >> LICENSE echo ===== %%~nf ===== >> LICENSE echo. >> LICENSE type %%f >> LICENSE ) ) ) call winbuild\\build\\build_env.cmd %pythonLocation%\python.exe -m cibuildwheel . --output-dir wheelhouse env: CIBW_ARCHS: ${{ matrix.cibw_arch }} CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd" CIBW_CACHE_PATH: "C:\\cibw" CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy CIBW_TEST_SKIP: "*-win_arm64" CIBW_TEST_COMMAND: 'docker run --rm -v {project}:C:\pillow -v C:\cibw:C:\cibw -v %CD%\..\venv-test:%CD%\..\venv-test -e CI -e GITHUB_ACTIONS mcr.microsoft.com/windows/servercore:ltsc2022 powershell C:\pillow\.github\workflows\wheels-test.ps1 %CD%\..\venv-test' shell: cmd - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-windows-${{ matrix.cibw_arch }} path: ./wheelhouse/*.whl - name: Upload fribidi.dll uses: actions/upload-artifact@v5 with: name: fribidi-windows-${{ matrix.cibw_arch }} path: winbuild\build\bin\fribidi* sdist: if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3.x" - run: make sdist - uses: actions/upload-artifact@v5 with: name: dist-sdist path: dist/*.tar.gz count-dists: needs: [build-native-wheels, windows, sdist] runs-on: ubuntu-latest name: Count dists steps: - uses: actions/download-artifact@v6 with: pattern: dist-* path: dist merge-multiple: true - name: "What did we get?" run: | ls -alR echo "Number of dists, should be $EXPECTED_DISTS:" files=$(ls dist 2>/dev/null | wc -l) echo $files [ "$files" -eq $EXPECTED_DISTS ] || exit 1 scientific-python-nightly-wheels-publish: if: github.repository_owner == 'python-pillow' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') needs: count-dists runs-on: ubuntu-latest name: Upload wheels to scientific-python-nightly-wheels steps: - uses: actions/download-artifact@v6 with: pattern: dist-!(sdist)* path: dist merge-multiple: true - name: Upload wheels to scientific-python-nightly-wheels uses: scientific-python/upload-nightly-action@b36e8c0c10dbcfd2e05bf95f17ef8c14fd708dbf # 0.6.2 with: artifacts_path: dist anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} pypi-publish: if: github.repository_owner == 'python-pillow' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') needs: count-dists runs-on: ubuntu-latest name: Upload release to PyPI environment: name: release-pypi url: https://pypi.org/p/Pillow permissions: id-token: write steps: - uses: actions/download-artifact@v6 with: pattern: dist-* path: dist merge-multiple: true - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: attestations: true