Fix flaky timer tests (#13473) #3043
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| merge_group: | |
| push: | |
| branches: | |
| - 'master' | |
| - '[0-9].[0-9]+' # matches to backport branches, e.g. 3.6 | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: | |
| - 'master' | |
| - '[0-9].[0-9]+' | |
| schedule: | |
| - cron: '0 6 * * *' # Daily 6AM UTC build | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: true | |
| env: | |
| COLOR: yes | |
| FORCE_COLOR: 1 # Request colored output from CLI tools supporting it | |
| MYPY_FORCE_COLOR: 1 | |
| PY_COLORS: 1 | |
| UPSTREAM_REPOSITORY_ID: >- | |
| 13258039 | |
| permissions: {} | |
| jobs: | |
| pre-setup: | |
| name: Pre-Setup global build settings | |
| runs-on: ubuntu-latest | |
| outputs: | |
| upstream-repository-id: ${{ env.UPSTREAM_REPOSITORY_ID }} | |
| release-requested: >- | |
| ${{ | |
| ( | |
| github.event_name == 'push' | |
| && github.ref_type == 'tag' | |
| ) | |
| && true | |
| || false | |
| }} | |
| steps: | |
| - name: Dummy | |
| if: false | |
| run: | | |
| echo "Pre-setup step" | |
| gen-llhttp: | |
| permissions: | |
| contents: read # to fetch code (actions/checkout) | |
| name: Generate llhttp sources | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| submodules: true | |
| - name: Cache llhttp generated files | |
| uses: actions/cache@v6.1.0 | |
| id: cache | |
| with: | |
| key: llhttp-${{ hashFiles('vendor/llhttp/package*.json', 'vendor/llhttp/src/**/*') }} | |
| path: vendor/llhttp/build | |
| - name: Setup NodeJS | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 18 | |
| - name: Generate llhttp sources | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: | | |
| make generate-llhttp | |
| - name: Upload llhttp generated files | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: llhttp | |
| path: vendor/llhttp/build | |
| if-no-files-found: error | |
| build-pure-python-dists: | |
| permissions: | |
| contents: read # to fetch code (actions/checkout) | |
| name: 📦 Build distribution packages | |
| runs-on: ubuntu-latest | |
| needs: | |
| - gen-llhttp | |
| outputs: | |
| sdist-filename: >- | |
| ${{ steps.dist-filename-detection.outputs.sdist-filename }} | |
| wheel-filename: >- | |
| ${{ steps.dist-filename-detection.outputs.wheel-filename }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| submodules: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v7.0.0 | |
| - name: Install build tooling and cython | |
| run: >- | |
| python -m | |
| pip install -U build -r requirements/cython.in -c requirements/cython.txt | |
| - name: Restore llhttp generated files | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: llhttp | |
| path: vendor/llhttp/build/ | |
| - name: Cythonize | |
| run: | | |
| make cythonize | |
| - name: Build sdists and pure-python wheel | |
| run: | | |
| python -m build | |
| env: | |
| AIOHTTP_NO_EXTENSIONS: 1 | |
| - name: Determine the sdist and wheel filenames | |
| id: dist-filename-detection | |
| run: | | |
| echo "sdist-filename=$(basename dist/*.tar.gz)" >> "$GITHUB_OUTPUT" | |
| echo "wheel-filename=$(basename dist/*.whl)" >> "$GITHUB_OUTPUT" | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: dist-pure | |
| path: dist | |
| lint-from-git: | |
| permissions: | |
| contents: read # to fetch code (actions/checkout) | |
| name: Lint / Git | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| submodules: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: 3.11 | |
| - name: >- | |
| Verify that `requirements/runtime-deps.in` | |
| is in sync with `pyproject.toml` | |
| run: | | |
| set -eEuo pipefail | |
| make sync-direct-runtime-deps | |
| git diff --exit-code -- requirements/runtime-deps.in | |
| - name: Install self | |
| run: | # needed so sphinxcontrib-spelling could allowlist some dep names | |
| python -m pip install . -c requirements/runtime-deps.txt | |
| env: | |
| AIOHTTP_NO_EXTENSIONS: 1 | |
| - name: Install spell checker | |
| run: | | |
| pip install -r requirements/doc-spelling.in -c requirements/doc-spelling.txt | |
| - name: Run docs spelling | |
| run: | | |
| # towncrier --yes # uncomment me after publishing a release | |
| make doc-spelling | |
| lint-from-sdist: | |
| permissions: | |
| pull-requests: read # to read PR metadata (verify change fragments step) | |
| name: Lint / sdist | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: | |
| - build-pure-python-dists | |
| steps: | |
| - name: Retrieve the sdist and unpack it into the workspace | |
| uses: re-actors/checkout-python-sdist@release/v3 | |
| with: | |
| source-tarball-name: >- | |
| ${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| workflow-artifact-name: dist-pure | |
| - name: Download the sdist artifact for the twine check | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: dist-pure | |
| path: dist/ | |
| - name: Setup Python | |
| uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: 3.11 | |
| - name: Cache PyPI | |
| uses: actions/cache@v6.1.0 | |
| with: | |
| key: pip-lint-${{ hashFiles('requirements/*.txt') }} | |
| path: ~/.cache/pip | |
| restore-keys: | | |
| pip-lint- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install -U pip twine -r requirements/lint.in -c requirements/lint.txt | |
| - name: Self-install from sdist | |
| run: | | |
| python -m pip install "${SDIST_PATH}" -c requirements/runtime-deps.txt | |
| env: | |
| SDIST_PATH: >- | |
| dist/${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| AIOHTTP_NO_EXTENSIONS: 1 | |
| - name: Run mypy | |
| run: | | |
| make mypy | |
| - name: Run slotscheck | |
| run: | | |
| # Some extra requirements are needed to ensure all modules | |
| # can be scanned by slotscheck. | |
| 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' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| set -euo pipefail | |
| # Fetch the body fresh: github.event.pull_request.body is frozen at | |
| # the webhook payload and stale on re-runs after a body edit. | |
| PR_BODY=$( | |
| gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}" --jq .body | |
| ) | |
| added=$( | |
| gh api --paginate \ | |
| "repos/${{ github.repository }}/pulls/${PR_NUMBER}/files" \ | |
| --jq '.[] | select(.status=="added") | .filename' \ | |
| | grep '^CHANGES/' || true | |
| ) | |
| failed=0 | |
| for f in $added; do | |
| num=$(basename "$f" | cut -d. -f1) | |
| [[ "$num" =~ ^[0-9]+$ ]] || continue | |
| if [[ "$num" == "$PR_NUMBER" ]]; then continue; fi | |
| if grep -qE "(^|[^0-9])#${num}([^0-9]|$)" <<<"$PR_BODY"; then continue; fi | |
| echo "::error file=$f::Change fragment ($num) must reference PR #$PR_NUMBER or an issue this PR fixes" | |
| failed=1 | |
| done | |
| exit $failed | |
| - name: Run twine checker | |
| run: | | |
| twine check --strict dist/* | |
| - name: Making sure that CONTRIBUTORS.txt remains sorted | |
| run: | | |
| LC_ALL=C sort --check --ignore-case CONTRIBUTORS.txt | |
| test: | |
| name: Test | |
| needs: | |
| - build-pure-python-dists | |
| strategy: | |
| matrix: | |
| pyver: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| no-extensions: ['', 'Y'] | |
| os: [ubuntu, macos, windows] | |
| experimental: [false] | |
| exclude: | |
| - os: macos | |
| no-extensions: 'Y' | |
| - os: windows | |
| no-extensions: 'Y' | |
| include: | |
| - pyver: pypy-3.11 | |
| no-extensions: 'Y' | |
| os: ubuntu | |
| experimental: false | |
| - os: ubuntu | |
| pyver: "3.14t" | |
| no-extensions: '' | |
| experimental: false | |
| fail-fast: true | |
| runs-on: ${{ matrix.os }}-latest | |
| continue-on-error: ${{ matrix.experimental }} | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Retrieve the sdist and unpack it into the workspace | |
| uses: re-actors/checkout-python-sdist@release/v3 | |
| with: | |
| source-tarball-name: >- | |
| ${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| workflow-artifact-name: dist-pure | |
| - name: Setup Python ${{ matrix.pyver }} | |
| id: python-install | |
| # important: do not use system python | |
| env: | |
| UV_PYTHON_PREFERENCE: only-managed | |
| uses: astral-sh/setup-uv@v10.0.1 | |
| with: | |
| python-version: ${{ matrix.pyver }} | |
| activate-environment: true | |
| enable-cache: true | |
| - name: Install dependencies | |
| env: | |
| DEPENDENCY_GROUP: test${{ endsWith(matrix.pyver, 't') && '-ft' || '' }} | |
| run: | | |
| uv pip install -U pip wheel setuptools build twine -r requirements/${{ env.DEPENDENCY_GROUP }}.in -c requirements/${{ env.DEPENDENCY_GROUP }}.txt | |
| - name: Set PYTHON_GIL=0 for free-threading builds | |
| if: ${{ endsWith(matrix.pyver, 't') }} | |
| run: echo "PYTHON_GIL=0" >> $GITHUB_ENV | |
| - name: Install self | |
| env: | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| run: uv pip install -e . | |
| - name: Run unittests | |
| env: | |
| COLOR: yes | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| PIP_USER: 1 | |
| run: >- | |
| pytest --junitxml=junit.xml --numprocesses=auto --cov -m 'not dev_mode and not autobahn' | |
| shell: bash | |
| - name: Re-run the failing tests with maximum verbosity | |
| if: failure() | |
| env: | |
| COLOR: yes | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| run: >- # `exit 1` makes sure that the job remains red with flaky runs | |
| pytest --no-cov -vvvvv --lf && exit 1 | |
| shell: bash | |
| - name: Run dev_mode tests | |
| env: | |
| COLOR: yes | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| PIP_USER: 1 | |
| PYTHONDEVMODE: 1 | |
| run: pytest -m dev_mode --cov --cov-append --cov-report=xml | |
| shell: bash | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./coverage.xml | |
| flags: >- | |
| CI-GHA,OS-${{ | |
| runner.os | |
| }},VM-${{ | |
| matrix.os | |
| }},Py-${{ | |
| steps.python-install.outputs.python-version | |
| }} | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload test results to Codecov | |
| if: ${{ !cancelled() }} | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./junit.xml | |
| report_type: test_results | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| test-mobile: | |
| name: Test (${{ matrix.config.platform }}, ${{ matrix.pyver }}, ${{ matrix.config.os }}) | |
| runs-on: ${{ matrix.config.os }} | |
| needs: | |
| - build-pure-python-dists | |
| strategy: | |
| matrix: | |
| pyver: ["cp313", "cp314"] | |
| config: | |
| - os: ubuntu-latest | |
| platform: android | |
| archs: x86_64 | |
| - os: macos-14 | |
| platform: ios | |
| archs: arm64_iphonesimulator | |
| steps: | |
| - name: Download the source distribution | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: dist-pure | |
| path: dist/ | |
| - name: Setup Python ${{ matrix.pyver }} | |
| id: python-install | |
| # important: do not use system python | |
| env: | |
| UV_PYTHON_PREFERENCE: only-managed | |
| uses: astral-sh/setup-uv@v10.0.1 | |
| with: | |
| python-version: ${{ matrix.pyver }} | |
| activate-environment: true | |
| enable-cache: true | |
| - name: Free up disk space for Android emulator | |
| if: ${{ matrix.config.platform == 'android' }} | |
| uses: BRAINSia/free-disk-space@v2.1.3 | |
| with: | |
| android: false | |
| docker-images: false | |
| mandb: false | |
| large-packages: false | |
| - name: Enable KVM group perms for Android emulator | |
| if: ${{ matrix.config.platform == 'android' }} | |
| # This is normally done by cibuildwheel automatically, when it detects Github Actions. But by unsetting GITHUB_ACTIONS | |
| # in the test step, we also disable that automatic setup. So we need to do it manually here. | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Install cibuildwheel | |
| run: uv pip install cibuildwheel==3.4.1 | |
| - name: Build wheels and test | |
| # cibuildwheel normally uses grouping in its outputs for its build/test steps. But the loading time when | |
| # expanding large groups in GitHub Actions is very high. So by unsetting GITHUB_ACTIONS, cibuildwheel does | |
| # not know that it is running in a GitHub Action and thus does not use groups. | |
| # Additionally, passing a tarball means that cibuildwheel will use that | |
| # tarball and not the current working directory as the source. | |
| run: env -u GITHUB_ACTIONS cibuildwheel "${SDIST_PATH}" | |
| env: | |
| CIBW_BUILD: ${{ matrix.pyver }}-* | |
| CIBW_PLATFORM: ${{ matrix.config.platform }} | |
| CIBW_ARCHS: ${{ matrix.config.archs }} | |
| CIBW_TEST_REQUIRES: -r requirements/test-mobile.txt | |
| CIBW_TEST_SOURCES: setup.cfg README.rst tests | |
| # Currently only Android supports colored output. See https://github.com/python/cpython/issues/150932 for iOS. | |
| CIBW_TEST_COMMAND: python -m pytest ${{ matrix.config.platform == 'android' && '--color=yes' || '' }} | |
| SDIST_PATH: >- | |
| dist/${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| autobahn: | |
| name: Autobahn testsuite | |
| needs: | |
| - build-pure-python-dists | |
| strategy: | |
| matrix: | |
| pyver: ['3.14'] | |
| no-extensions: [''] | |
| os: [ubuntu] | |
| fail-fast: true | |
| runs-on: ${{ matrix.os }}-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Retrieve the sdist and unpack it into the workspace | |
| uses: re-actors/checkout-python-sdist@release/v3 | |
| with: | |
| source-tarball-name: >- | |
| ${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| workflow-artifact-name: dist-pure | |
| - name: Setup Python ${{ matrix.pyver }} | |
| id: python-install | |
| # important: do not use system python | |
| env: | |
| UV_PYTHON_PREFERENCE: only-managed | |
| uses: astral-sh/setup-uv@v10.0.1 | |
| with: | |
| python-version: ${{ matrix.pyver }} | |
| activate-environment: true | |
| enable-cache: true | |
| - name: Install dependencies | |
| env: | |
| DEPENDENCY_GROUP: test${{ endsWith(matrix.pyver, 't') && '-ft' || '' }} | |
| run: | | |
| uv pip install -r requirements/${{ env.DEPENDENCY_GROUP }}.in -c requirements/${{ env.DEPENDENCY_GROUP }}.txt | |
| - name: Install self | |
| env: | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| run: uv pip install -e . | |
| - name: Run unittests | |
| env: | |
| COLOR: yes | |
| AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} | |
| PIP_USER: 1 | |
| run: >- | |
| PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}" | |
| pytest --junitxml=junit.xml --cov --cov-report=xml | |
| --timeout=0 -m autobahn | |
| shell: bash | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./coverage.xml | |
| flags: Autobahn | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload test results to Codecov | |
| if: ${{ !cancelled() }} | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./junit.xml | |
| report_type: test_results | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| benchmark: | |
| name: Benchmark | |
| needs: | |
| - build-pure-python-dists | |
| - pre-setup # transitive, for accessing settings | |
| if: >- | |
| needs.pre-setup.outputs.upstream-repository-id == github.repository_id | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout project | |
| uses: actions/checkout@v7 # despite sdist, codspeed needs Git | |
| - name: Retrieve the sdist and unpack it into the workspace | |
| uses: re-actors/checkout-python-sdist@release/v3 | |
| with: | |
| source-tarball-name: >- | |
| ${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| workflow-artifact-name: dist-pure | |
| - name: Download the source distribution | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: dist-pure | |
| path: dist/ | |
| - name: Setup Python 3.13.2 | |
| id: python-install | |
| uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: 3.13.2 | |
| cache: pip | |
| cache-dependency-path: requirements/*.txt | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install -r requirements/test.in -c requirements/test.txt | |
| - name: Self-install from sdist | |
| run: | | |
| python -m pip install "${SDIST_PATH}" -c requirements/runtime-deps.txt | |
| env: | |
| SDIST_PATH: >- | |
| dist/${{ needs.build-pure-python-dists.outputs.sdist-filename }} | |
| - name: Load kernel TLS module | |
| if: runner.os == 'Linux' | |
| run: sudo modprobe tls | |
| - name: Show kernel and OpenSSL build information | |
| if: runner.os == 'Linux' | |
| run: | | |
| lsb_release -a | |
| uname -r | |
| openssl version -a | |
| - name: Run benchmarks | |
| uses: CodSpeedHQ/action@v5.0.3 | |
| with: | |
| mode: instrumentation | |
| run: python -Im pytest --no-cov -vvvvv --codspeed --durations=30 --timeout=0 | |
| cython-coverage: | |
| permissions: | |
| contents: read # to fetch code (actions/checkout) | |
| name: Cython coverage | |
| needs: | |
| - gen-llhttp | |
| strategy: | |
| matrix: | |
| os: [ubuntu, windows] | |
| runs-on: ${{ matrix.os }}-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| submodules: true | |
| - name: Setup Python | |
| id: python-install | |
| uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install dependencies | |
| run: | | |
| python -Im pip install -U pip wheel setuptools build twine -r requirements/test.in -c requirements/test.txt | |
| - name: Uninstall blocbuster | |
| run: python -m pip uninstall blockbuster -y | |
| - name: Restore llhttp generated files | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: llhttp | |
| path: vendor/llhttp/build/ | |
| - name: Cythonize with linetrace | |
| run: | | |
| make cythonize CYTHON_EXTRA="-X linetrace=True" | |
| - name: Install self | |
| env: | |
| AIOHTTP_CYTHON_TRACE: 1 | |
| run: python -m pip install -e . | |
| - name: Run tests with Cython tracing | |
| env: | |
| COLOR: yes | |
| PIP_USER: 1 | |
| run: >- | |
| pytest tests/test_client_functional.py tests/test_http_parser.py tests/test_http_writer.py tests/test_web_functional.py tests/test_web_response.py tests/test_websocket_parser.py | |
| --cov-config=.coveragerc-cython.toml --cov | |
| --cov-report=xml:cython-coverage.xml | |
| --numprocesses=auto | |
| -m 'not dev_mode and not autobahn' | |
| shell: bash | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./cython-coverage.xml | |
| disable_search: true | |
| flags: cython-coverage | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: true | |
| check: # This job does nothing and is only used for the branch protection | |
| if: always() | |
| needs: | |
| - lint-from-sdist | |
| - lint-from-git | |
| - test | |
| - test-mobile | |
| - autobahn | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Decide whether the needed jobs succeeded or failed | |
| uses: re-actors/alls-green@release/v1 | |
| with: | |
| jobs: ${{ toJSON(needs) }} | |
| - name: Trigger codecov notification | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: true | |
| run_command: send-notifications | |
| pre-deploy: | |
| name: Pre-Deploy | |
| runs-on: ubuntu-latest | |
| needs: | |
| - check | |
| - pre-setup # transitive, for accessing settings | |
| if: fromJSON(needs.pre-setup.outputs.release-requested) | |
| steps: | |
| - name: Dummy | |
| run: | | |
| echo "Predeploy step" | |
| build-wheels: | |
| name: Build wheels on ${{ matrix.os }} ${{ matrix.qemu }} ${{ matrix.musl }} ${{ matrix.platform }} | |
| runs-on: ${{ matrix.os }} | |
| needs: | |
| - build-pure-python-dists | |
| - pre-deploy | |
| strategy: | |
| matrix: | |
| 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 | |
| qemu: ppc64le | |
| musl: "" | |
| - os: ubuntu-latest | |
| qemu: ppc64le | |
| musl: musllinux | |
| - os: ubuntu-latest | |
| qemu: riscv64 | |
| musl: "" | |
| - os: ubuntu-latest | |
| qemu: riscv64 | |
| musl: musllinux | |
| - os: ubuntu-latest | |
| qemu: s390x | |
| musl: "" | |
| - os: ubuntu-latest | |
| qemu: s390x | |
| musl: musllinux | |
| # armv7l builds on aarch64 hosts. We still register QEMU so | |
| # binfmt picks up the 32-bit ARM userspace handler regardless of | |
| # 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 | |
| qemu: armv7l | |
| musl: "" | |
| - os: ubuntu-24.04-arm | |
| qemu: armv7l | |
| musl: musllinux | |
| - os: ubuntu-latest | |
| musl: musllinux | |
| - os: ubuntu-24.04-arm | |
| musl: musllinux | |
| - os: ubuntu-latest | |
| platform: android | |
| - 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 | |
| deploy: | |
| name: Deploy | |
| needs: | |
| - build-wheels | |
| - pre-setup # transitive, for accessing settings | |
| runs-on: ubuntu-latest | |
| if: >- | |
| needs.pre-setup.outputs.upstream-repository-id == github.repository_id | |
| permissions: | |
| contents: write # IMPORTANT: mandatory for making GitHub Releases | |
| id-token: write # IMPORTANT: mandatory for trusted publishing & sigstore | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/aiohttp | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| submodules: true | |
| - name: Login | |
| run: | | |
| echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token | |
| - name: Download distributions | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: dist | |
| pattern: dist-* | |
| merge-multiple: true | |
| - name: Collected dists | |
| run: | | |
| tree dist | |
| - name: Check whether the GitHub Release already exists | |
| # Allows re-running the deploy job after a partial failure (e.g. PyPI | |
| # upload error) without the Make Release step failing with HTTP 422 | |
| # because the tag/release was created on a prior attempt. Treat | |
| # only the literal `release not found` reply as "does not exist"; | |
| # other failures (auth, rate-limit, network) re-raise so the job | |
| # fails loudly instead of falling through to Make Release. | |
| id: gh-release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" \ | |
| >/dev/null 2>err; then | |
| echo 'exists=true' >> "${GITHUB_OUTPUT}" | |
| elif grep -qx 'release not found' err; then | |
| echo 'exists=false' >> "${GITHUB_OUTPUT}" | |
| else | |
| cat err >&2 | |
| exit 1 | |
| fi | |
| - name: Make Release | |
| if: steps.gh-release.outputs.exists != 'true' | |
| uses: aio-libs/create-release@v1.6.6 | |
| with: | |
| changes_file: CHANGES.rst | |
| name: aiohttp | |
| version_file: aiohttp/__init__.py | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| dist_dir: dist | |
| fix_issue_regex: >- | |
| :issue:`(\d+)` | |
| fix_issue_repl: >- | |
| #\1 | |
| - name: >- | |
| Publish 🐍📦 to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| # Allow re-running the deploy job after a partial PyPI upload | |
| # without failing on dists that were already published. | |
| skip-existing: true | |
| - name: Sign the dists with Sigstore | |
| uses: sigstore/gh-action-sigstore-python@v3.5.0 | |
| with: | |
| inputs: >- | |
| ./dist/*.tar.gz | |
| ./dist/*.whl | |
| - name: Upload artifact signatures to GitHub Release | |
| # Confusingly, this action also supports updating releases, not | |
| # just creating them. This is what we want here, since we've manually | |
| # created the release above. | |
| uses: softprops/action-gh-release@v3.0.2 | |
| with: | |
| # dist/ contains the built packages plus the Sigstore signatures | |
| # and certificates emitted alongside them by the signing step above. | |
| files: dist/** |