diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 5e1cb3fd3be..280d6d8b940 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -220,7 +220,12 @@ jobs: pip install -r requirements/base.in -c requirements/base.txt slotscheck -v -m aiohttp - name: Verify CHANGES fragment references PR - if: github.event_name == 'pull_request' + # Skipped on backport PRs: their fragments are numbered for the original + # PR/issue, so the check would never pass. The workflow itself stays + # identical across branches. + if: >- + github.event_name == 'pull_request' + && github.event.pull_request.base.ref == 'master' env: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number }} @@ -636,35 +641,44 @@ jobs: echo "Predeploy step" build-wheels: - name: Build wheels on ${{ matrix.os }} ${{ matrix.qemu }} ${{ matrix.musl }} ${{ matrix.platform }} - runs-on: ${{ matrix.os }} + name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category + 📦 Build platform-specific wheels${{ '' }} needs: - build-pure-python-dists - pre-deploy + uses: ./.github/workflows/reusable-cibuildwheel.yml strategy: matrix: - os: ["ubuntu-latest", "windows-latest", "windows-11-arm", "macos-latest", "ubuntu-24.04-arm"] - qemu: [''] - musl: [""] - platform: [""] + runner-vm-os: + - ubuntu-latest + - windows-latest + - windows-11-arm + - macos-latest + - ubuntu-24.04-arm + qemu: + - '' + musl: + - '' + platform: + - '' include: - # Split ubuntu/musl jobs for the sake of speed-up - - os: ubuntu-latest + # Split ubuntu/musl jobs for the sake of speed-up + - runner-vm-os: ubuntu-latest qemu: ppc64le - musl: "" - - os: ubuntu-latest + musl: '' + - runner-vm-os: ubuntu-latest qemu: ppc64le musl: musllinux - - os: ubuntu-latest + - runner-vm-os: ubuntu-latest qemu: riscv64 - musl: "" - - os: ubuntu-latest + musl: '' + - runner-vm-os: ubuntu-latest qemu: riscv64 musl: musllinux - - os: ubuntu-latest + - runner-vm-os: ubuntu-latest qemu: s390x - musl: "" - - os: ubuntu-latest + musl: '' + - runner-vm-os: ubuntu-latest qemu: s390x musl: musllinux # armv7l builds on aarch64 hosts. We still register QEMU so @@ -672,78 +686,48 @@ jobs: # whether the host kernel has CONFIG_COMPAT enabled. Even with # emulation, aarch64-on-aarch64 hosting beats x86_64 by a wide # margin. - - os: ubuntu-24.04-arm + - runner-vm-os: ubuntu-24.04-arm qemu: armv7l - musl: "" - - os: ubuntu-24.04-arm + musl: '' + - runner-vm-os: ubuntu-24.04-arm qemu: armv7l musl: musllinux - - os: ubuntu-latest + - runner-vm-os: ubuntu-latest musl: musllinux - - os: ubuntu-24.04-arm + - runner-vm-os: ubuntu-24.04-arm musl: musllinux - - os: ubuntu-latest + - runner-vm-os: ubuntu-latest platform: android - - os: macos-14 + - runner-vm-os: macos-14 platform: ios - steps: - - name: Download the source distribution - uses: actions/download-artifact@v8 - with: - name: dist-pure - path: dist/ - - name: Set up QEMU - if: ${{ matrix.qemu }} - uses: docker/setup-qemu-action@v4 - with: - platforms: all - # This should be temporary - # xref https://github.com/docker/setup-qemu-action/issues/188 - # xref https://github.com/tonistiigi/binfmt/issues/215 - image: tonistiigi/binfmt:qemu-v8.1.5 - id: qemu - - name: Prepare emulation - run: | - if [[ -n "${{ matrix.qemu }}" ]]; then - # Build emulated architectures only if QEMU is set, - # use default "auto" otherwise - echo "CIBW_ARCHS_LINUX=${{ matrix.qemu }}" >> $GITHUB_ENV - # Override pyproject.toml's `build[uv]`: the pypa odd-arch - # manylinux/musllinux containers do not ship `uv` preinstalled. - echo "CIBW_BUILD_FRONTEND=build" >> $GITHUB_ENV - fi - shell: bash - - name: Build wheels - uses: pypa/cibuildwheel@v4.2.0 - with: - package-dir: >- # not necessarily a dir, we pass an acceptable sdist - dist/${{ needs.build-pure-python-dists.outputs.sdist-filename }} - # `build-frontend = "build[uv]"` (pyproject.toml) requires uv to be - # available on the runner for Windows and macOS. Installing - # cibuildwheel with the `uv` extra bundles uv with it; the - # tested-arch manylinux/musllinux containers also ship uv - # preinstalled. The odd-arch containers do not, so the - # `Prepare emulation` step above sets `CIBW_BUILD_FRONTEND=build` - # for those QEMU matrix cells. - extras: uv - env: - CIBW_PLATFORM: ${{ matrix.platform || 'auto' }} - CIBW_SKIP: pp* ${{ matrix.musl == 'musllinux' && '*manylinux*' || '*musllinux*' }} - CIBW_ARCHS_MACOS: x86_64 arm64 universal2 - CIBW_ARCHS_IOS: arm64_iphoneos arm64_iphonesimulator x86_64_iphonesimulator - CIBW_ARCHS_ANDROID: arm64_v8a x86_64 - - name: Upload wheels - uses: actions/upload-artifact@v7 - with: - name: >- - dist-${{ matrix.os }}-${{ matrix.musl }}-${{ - matrix.platform - && matrix.platform - || matrix.qemu - && matrix.qemu - || 'native' - }} - path: ./wheelhouse/*.whl + with: + check-name: >- + ${{ matrix.runner-vm-os }} ${{ matrix.qemu }} + ${{ matrix.musl }} ${{ matrix.platform }} + dists-artifact-name: dist-pure + environment-variables: |- + CIBW_ARCHS_ANDROID=arm64_v8a x86_64 + + CIBW_ARCHS_IOS=arm64_iphoneos arm64_iphonesimulator x86_64_iphonesimulator + + ${{ matrix.qemu && format('CIBW_ARCHS_LINUX={0}', matrix.qemu) || '' }} + + CIBW_ARCHS_MACOS=x86_64 arm64 universal2 + + ${{ matrix.qemu && 'CIBW_BUILD_FRONTEND=build' || '' }} + + CIBW_PLATFORM=${{ matrix.platform != '' && matrix.platform || 'auto' }} + + CIBW_SKIP=pp* *${{ + matrix.musl == 'musllinux' + && 'many' + || 'musl' + }}linux* + qemu: ${{ matrix.qemu != '' }} + runner-vm-os: ${{ matrix.runner-vm-os }} + source-tarball-name: >- + ${{ needs.build-pure-python-dists.outputs.sdist-filename }} + timeout-minutes: ${{ matrix.qemu != '' && 35 || 15 }} deploy: name: Deploy diff --git a/.github/workflows/reusable-cibuildwheel.yml b/.github/workflows/reusable-cibuildwheel.yml new file mode 100644 index 00000000000..a760f0eab30 --- /dev/null +++ b/.github/workflows/reusable-cibuildwheel.yml @@ -0,0 +1,141 @@ +--- + +name: >- + ❌ + [DO NOT CLICK] + Reusable cibuildwheel + +on: # yamllint disable-line rule:truthy + workflow_call: + inputs: + check-name: + description: A custom name for the Checks API-reported status + required: false + type: string + dists-artifact-name: + description: >- + Workflow artifact name containing dists. + Defaults to "python-package-distributions". + default: python-package-distributions + required: false + type: string + environment-variables: + description: >- + A newline-delimited blob of text with environment variables + to be set using `${GITHUB_ENV}` + required: false + type: string + qemu: + default: false + description: >- + Whether this job needs to configure QEMU to emulate a foreign + architecture before running `cibuildwheel`. Defaults to "false". + required: false + type: boolean + runner-vm-os: + description: VM OS to use + default: ubuntu-latest + required: false + type: string + source-tarball-name: + default: >- + *.tar.gz + description: Sdist filename wildcard. Defaults to "*.tar.gz". + required: false + type: string + timeout-minutes: + description: Deadline for the job to complete + required: true + type: number + +env: + FORCE_COLOR: "1" # Make tools pretty. + PIP_DISABLE_PIP_VERSION_CHECK: "1" + PIP_NO_PYTHON_VERSION_WARNING: "1" + +jobs: + + build-wheel: + name: >- + ${{ + inputs.check-name + && inputs.check-name + || format( + 'Build wheels on {0}{1}', + inputs.runner-vm-os, + inputs.qemu && ' under QEMU' || '' + ) + }} + runs-on: ${{ inputs.runner-vm-os }} + timeout-minutes: ${{ fromJSON(inputs.timeout-minutes) }} + + defaults: + run: + shell: bash -eEuxo pipefail {0} + + steps: + - name: Export requested job-global environment variables + if: inputs.environment-variables != '' + env: + INPUT_ENVIRONMENT_VARIABLES: ${{ inputs.environment-variables }} + run: echo "${INPUT_ENVIRONMENT_VARIABLES}" >> "${GITHUB_ENV}" + + - name: Compute GHA artifact name ending + id: gha-artifact-name + run: | + from hashlib import sha512 + from os import environ + from pathlib import Path + + FILE_APPEND_MODE = 'a' + + inputs_json_str = """${{ toJSON(inputs) }}""" + + hash = sha512(inputs_json_str.encode()).hexdigest() + + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print(f'hash={hash}', file=outputs_file) + shell: python + + - name: Download the source distribution + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.dists-artifact-name }} + path: dist/ + + - name: Set up QEMU + if: inputs.qemu + uses: docker/setup-qemu-action@v4 + with: + platforms: all + # This should be temporary + # xref https://github.com/docker/setup-qemu-action/issues/188 + # xref https://github.com/tonistiigi/binfmt/issues/215 + image: tonistiigi/binfmt:qemu-v8.1.5 + + - name: Build wheels + uses: pypa/cibuildwheel@v4.2.0 + with: + package-dir: >- # not necessarily a dir, we pass an acceptable sdist + dist/${{ inputs.source-tarball-name }} + # `build-frontend = "build[uv]"` (pyproject.toml) requires uv to be + # available on the runner for Windows and macOS. Installing + # cibuildwheel with the `uv` extra bundles uv with it; the + # tested-arch manylinux/musllinux containers also ship uv + # preinstalled. Odd-arch (QEMU) containers do not -- callers of + # this workflow are expected to set `CIBW_BUILD_FRONTEND=build` + # via `environment-variables` for those cells. + extras: uv + + - name: Upload built artifacts for testing and publishing + uses: actions/upload-artifact@v7 + with: + name: ${{ inputs.dists-artifact-name }}- + ${{ inputs.runner-vm-os }}- + ${{ inputs.qemu && 'qemu-' || '' }} + ${{ steps.gha-artifact-name.outputs.hash }} + path: ./wheelhouse/*.whl + +... diff --git a/CHANGES/59c0123d.contrib.rst b/CHANGES/59c0123d.contrib.rst new file mode 100644 index 00000000000..6231dad7318 --- /dev/null +++ b/CHANGES/59c0123d.contrib.rst @@ -0,0 +1,2 @@ +The CI/CD is now in sync with the rest of the projects in terms of where +the ``cibuildwheel`` workflow lives -- by :user:`webknjaz`. diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ed4f5fa1acb..ed5dba9b68c 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -59,7 +59,7 @@ cffi==2.1.1 # pycares cfgv==3.5.0 # via pre-commit -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 # via requests click==8.4.2 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 3c10814cafe..c228675311e 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -59,7 +59,7 @@ cffi==2.1.1 # pycares cfgv==3.5.0 # via pre-commit -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 # via requests click==8.4.2 # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 137847bbf8c..02696e31de6 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -12,7 +12,7 @@ babel==2.18.0 # via sphinx certifi==2026.7.22 # via requests -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 # via requests click==8.4.2 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 612136bd1d5..e0f60c89ce2 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -12,7 +12,7 @@ babel==2.18.0 # via sphinx certifi==2026.7.22 # via requests -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 # via requests click==8.4.2 # via towncrier